Agent and MCP Access

Heaper provides two MCP connections with different boundaries:

  • Hosted MCP v2 is available directly at https://<server>/mcp. It uses Streamable HTTP and browser-based OAuth, so web and desktop MCP clients can connect without installing the CLI.
  • CLI MCP is started with heaper mcp --stdio. It connects to a saved remote login or the Electron Local API and includes machine-local tools that cannot safely be hosted.

Both connections use the same shared contract for the 23 hosted tools. The CLI can expose additional login, local filesystem, SQL, and Electron capabilities when its active connection supports them.

Discover Agent Skills

Once connected, call list_skills({}) to discover task guides, then get_skill({"name":"heaper"}) for the starting workflow. Read heaper-search, heaper-documents, or heaper-organize when that task arises. These ordinary read-only tools work in clients that expose tools but do not support MCP resources or local skill installation. They return the full guide in text and structured data.

Shell agents can discover the same instructions offline with heaper skills list and heaper skills show heaper. heaper doc formatting exposes the structured node reference for CLI patches. The CLI package and Heaper plugin also contain portable SKILL.md folders. No repository access is required.

The guide explains which returned IDs and hashes to copy, exact MCP and CLI syntax, search and pagination, tag direction, safe writes, and capability-specific recovery. Load one guide at a time instead of inserting every guide into the initial prompt. If discovery commands are absent, update the CLI or hosted server; an older installed release will not gain them merely by changing the agent prompt.

Connection Options

GoalConnectionRequirements
Connect a web or desktop agent directly to hosted Heaperhttps://<server>/mcpAn MCP client with Streamable HTTP and OAuth support
Connect directly to a self-hosted serverhttps://<your-server>/mcpCorrect PUBLIC_URL, HTTPS, and proxy routes
Use a saved CLI login from a desktop agentheaper mcp --stdioNode.js and heaper-cli
Use Electron-only data or machine-local operationsheaper mcp --stdio through the Electron Local APIA running Heaper desktop app with Local API enabled
Run Heaper commands from a shell agentNormal heaper commandsNode.js and heaper-cli

Prefer hosted MCP when the client supports it. Keep the CLI transport for local workflows and clients that do not yet support remote OAuth.

Connect to Hosted MCP

Enter this URL in the MCP client's remote server field:

https://<server>/mcp

For example, a self-hosted instance at https://heaper.example.com exposes MCP at https://heaper.example.com/mcp. Use the public server origin, not an /api URL.

The client discovers authentication from the unauthenticated response and the server's RFC 9728 protected-resource metadata. It then discovers the OAuth authorization server through RFC 8414, dynamically registers as a public client, creates an S256 PKCE challenge, and opens the Heaper consent screen. Access tokens are bound to the exact MCP resource and accepted only in the Authorization: Bearer header. Tokens in query strings or form fields are rejected.

Approve an agent with your own account

An approved human account can pair agents on Heaper Cloud and on a self-hosted server. Server administrator access is not required. Self-hosted account admission is still separate: the server must first approve a pending human account.

  1. Connect the MCP client to the server's /mcp endpoint. It opens the authorization page served by that backend, showing the server, client, callback, and requested permissions.
  2. Sign in directly on that page. Cloud accounts use an email sign-in code. Self-hosted accounts use the same recovery phrase as the Heaper app; the phrase stays in the browser and only a signed challenge is sent to the server.
  3. Check the account shown, choose a new agent name or an existing compatible profile, and select its permissions and heaps. Read access is selected initially; writes and staying connected require an explicit selection. Only heaps you own or administer are offered.
  4. Choose Connect agent, or Reject request. Signing in alone never claims the request or grants access. The browser completes the claim when you explicitly decide.
  5. Keep the page open as it returns to the MCP client. The client completes its PKCE exchange and receives agent credentials, never your human session.

The whole browser flow is embedded in the backend binary. It requires neither the full Heaper app as a web frontend nor the docs site. Human credentials are kept in memory for this tab only; reloading before approval requires signing in again.

To approve on your phone or desktop instead, expand Approve in the Heaper app instead, then enter the pairing code in Settings > Agents in a client connected to the same server. You can also send the request to your username from this section; the recipient still confirms the code and permissions in the app. Each fresh authorization attempt has its own private connection. Approval affects that attempt only. Reusing a public OAuth client ID cannot inherit another person's approval. Ordinary reconnects use the rotating refresh token when Stay connected was approved. A new authorization attempt requires consent again; you can explicitly reconnect it to a profile you already manage when its requested permissions cover that profile.

Only your claimed or username-addressed requests appear in your inbox. Requests expire after ten minutes; expired, rejected, already decided, and wrong-account codes cannot be claimed. Unknown usernames receive the same acknowledgement as valid ones. If the wrong server or account is selected, switch to the correct one or start a new request. Verify the code on the agent's handoff page before approving an unsolicited inbox request.

Manage permissions or disconnect an agent in Settings > Agents. Disconnecting invalidates its access and refresh sessions. If your account is disabled or you lose authority over a granted heap, its delegation stops working. Existing approved agent profiles and credentials survive the additive migration; pending authorization attempts from older server versions must be restarted.

In an app client, Sign in and continue preserves the code and exact server through setup. The embedded browser page signs in directly on the backend instead. Opening the page or signing in never approves an agent automatically.

Keep the handoff page open while approving. It retries interrupted status requests automatically and offers Retry now. Reopening the same page before its deadline recovers the same authorization code; retries do not extend its lifetime. Redemption remains single-use. After redemption, reopening the handoff reports completion. An expired request requires a new connection attempt.

Connected includes an unexpired access token, even when Stay connected was not selected. Stay connected enables renewable access; without it the agent must authorize again when its access token expires.

Agent identity versus CLI account login

Hosted MCP creates an agent identity without requiring an agent email address. Its OAuth credentials belong to that agent and are restricted by the granted heaps and permissions. An approved human grants access on the embedded browser page or confirms the pairing code in Settings > Agents, without server-admin privileges.

CLI auth login and CLI-backed MCP login_with_agent use a separate key/account login path. New or unverified cloud keys can receive email_required; this does not mean hosted agents need mailboxes. For an intentionally chosen CLI account login, request a code with heaper auth login <server> --agent <existing-identity> --email <account-email> --json, then repeat with the same values and --email-code <code> supplied by the user. Reuse the account's intended key; an email attached to another key cannot be rebound through this flow. CLI-backed MCP has no email-code tool, so a user must complete that step on the bridge's machine or choose a hosted OAuth client. Do not reuse hosted MCP tokens as REST credentials.

heaper skills show heaper (MCP get_skill({"name":"heaper"})) contains the full pairing, login, verification, and recovery steps.

OAuth scopes

ScopeGrants
heaper.mcp.readConnection checks, discovery, searches, reads, history, files, and invite listing
heaper.mcp.writeThe hosted mutation tools, plus the reads needed to verify them
offline_accessA rotating refresh token for reconnecting without another consent prompt

Initial authentication leaves scope selection to protected-resource discovery, which advertises all three scopes. Clients can request these capabilities together; the person still explicitly chooses whether to grant writes and staying connected at approval. Read access is the default selection. Without an offline grant, the token response contains no refresh token.

OpenCode setup and write access

Settings > Agents > Set up an agent provides your selected server's MCP URL and OAuth scopes. Client-specific configuration examples live here in the documentation. For OpenCode, merge the following into your configuration, replacing the example endpoint:

{
  "mcp": {
    "heaper": {
      "type": "remote",
      "url": "https://<server>/mcp",
      "oauth": {
        "scope": "heaper.mcp.read heaper.mcp.write offline_access"
      }
    }
  }
}

Run opencode mcp auth heaper. On the approval page or in the app, select Create and edit content, optionally Stay connected, and the intended heaps, then connect. Requesting a permission in configuration does not grant it automatically. See OpenCode's MCP configuration.

For an existing read-only connection, update the configuration, run opencode mcp logout heaper, and authorize again. Replace heaper in these commands if your configured connection has another name. A client registered with only read access cannot add write scope to that registration; it needs fresh registration and consent. If a client retains its old registration after logout, remove that connection's cached OAuth registration using the client's controls before reconnecting.

Writes without permission return 403 Forbidden with an insufficient_scope challenge for heaper.mcp.write. Do not repeatedly retry a write or expect a refresh to add permission. Start a fresh authorization that includes write capability and obtain explicit consent. A read-only request shows these recovery instructions on the embedded approval page and in the app.

Refresh tokens are scoped, rotating, and single-use. A refresh cannot add scopes. Reusing an already rotated token fails, so clients must persist each new refresh token before discarding the previous one.

Hosted Tool Contract

Hosted MCP exposes exactly these shared tools:

KindHosted tools
Agent skillslist_skills, get_skill
Connection and discoveryconnection_status, list_heaps, list_blocks, search_blocks
Block and document readsget_block, get_document, get_document_formatting
Files, history, and inviteslist_files, get_history, list_invites
Writescreate_block, update_block_title, patch_document, replace_document, update_relation, update_property, accept_invite
Attached artifactsprepare_artifact_input, publish_artifact, get_artifacts, search_artifacts

The hosted endpoint intentionally excludes:

  • agent identity creation and login bootstrap;
  • local file creation, upload, and download;
  • raw local SQL;
  • Electron Local API and Electron debug controls.

Those operations either establish the identity used to authorize MCP or touch the client machine. Use the CLI or Electron-specific tooling when they are needed. list_files on hosted MCP returns authorized metadata and thumbnail variants, not local file bytes.

Sessions, Progress, and Safe Retries

Hosted MCP is stateless. Each request is independently authenticated, the server does not issue an Mcp-Session-Id, and a reconnect starts with a fresh MCP initialize exchange. Multiple clients can use the same Heaper account without sharing transport state.

Read calls are safe to retry after a network interruption. For a write whose response was lost, do not immediately repeat it. First reconcile by reading the affected block, document, relation, property, invite, or provenance history. Retry only when that read proves the intended mutation did not happen. Continue to use conflict hashes for title and document edits.

Streamable HTTP responses support SSE progress notifications when the client supplies a progress token. Client cancellation propagates to the active backend operation. A cancelled or disconnected write still requires the same read-back reconciliation because the mutation might have completed before cancellation arrived.

Bounded Documents and Safe Writes

Remote block reads omit raw YDocument and encrypted-blob fields. Use get_document for collaborative content and list_files for file metadata.

get_document paginates top-level body blocks with limit and offset. Continue with page.next_offset while page.has_more is true. Read results are bounded to 64 KiB overall and 32 KiB of page content, with at most 2,048 nodes, 32 levels of nesting, and 64 references. Inspect truncation.reasons and truncation.continuation; some intra-block truncation cannot be resumed.

The edit view never silently truncates a lossless patch snapshot. If a requested edit page, title, or single node exceeds a bound, the tool returns a structured 413 document_page_too_large error. Request a smaller page. A single oversized node must be reduced or edited through another client before it can be returned as a valid MCP patch target.

For a document mutation:

  1. Call get_document_formatting to load the shared rich-text node and patch contract.
  2. Call get_document with view: "edit".
  3. Copy its top-level hash into patch_document.base_hash, never content.hash.
  4. Target only nodes marked patch_target: true, preferring a stable node_id over the snapshot-specific agent_id.
  5. Read the document again after the write.

replace_document rebuilds the collaborative document and requires confirm_rebuild=true. Use it only after explicit user confirmation. A stale hash returns conflict details instead of overwriting concurrent work.

CLI MCP Compatibility

Install the CLI when a client needs stdio or local-only tools:

npm install -g heaper-cli
heaper auth keygen codex
heaper auth login https://your-heaper.example --agent codex

Configure an MCP client that accepts an mcpServers JSON object:

{
  "mcpServers": {
    "heaper": {
      "command": "heaper",
      "args": [
        "--server",
        "https://your-heaper.example",
        "mcp",
        "--stdio"
      ]
    }
  }
}

Restart the agent after changing its MCP configuration. If the saved default server is correct, "args": ["mcp", "--stdio"] also works. Put global options such as --server, --port, and --token before mcp.

The stdio server remains supported and negotiates the MCP protocol with existing SDK clients. Hosted MCP replaces the previous need to deploy a separate HTTPS wrapper around the CLI. It does not deprecate the CLI's local capabilities. New remote integrations should use /mcp; query-string bearer authentication and custom wrappers around CLI stdio are not part of the hosted contract.

For local Electron access, enable Settings > Local API in the desktop app. Set HEAPER_TOKEN when Local API authentication is enabled. Use --port only when discovery through ~/.heaper/api.json is unavailable.

CLI-only capability boundary

CapabilityHosted /mcpCLI with remote serverCLI with Electron Local API
The 23 shared tools aboveYesYesConnection-dependent
Named agent identity and login bootstrapNoYesNot applicable
Local file upload and downloadNoYesDownload only
Read-only local SQLNoNoYes
Saved-view evaluation through get_block(view=...)NoNot yetYes
Electron debug controlsNoNoSeparate project debug MCP only

Call connection_status in CLI MCP to inspect the active mode, apiContractVersion, and advertised capabilities. Do not infer a CLI-only capability from the presence of the corresponding hosted read tool.

Self-Hosted Setup

The self-hosted image serves the local server status UI at / and keeps OAuth pairing on backend-owned routes. It does not bundle the general Heaper web app. Set PUBLIC_URL to the exact external origin that users and MCP clients enter:

environment:
  - PUBLIC_URL=https://heaper.example.com

Do not include /api, /mcp, a trailing slash, an internal container address, or the reverse proxy's private port. Use HTTPS in normal deployments. This value becomes the OAuth issuer and resource origin, so a mismatch causes audience or redirect validation failures. The agent authorization page, human sign-in, and consent are all served from that backend origin. FRONTEND_URL is not used for MCP approval. Desktop and mobile approval remain available through the pairing code. Use an existing approved self-hosted identity; a pending human must first be admitted by the server administrator.

If a reverse proxy splits frontend and backend routing, forward these paths without stripping or adding prefixes:

GET, POST, DELETE  /mcp
GET                /.well-known/oauth-protected-resource
GET                /.well-known/oauth-protected-resource/mcp
GET                /.well-known/oauth-authorization-server
GET                /.well-known/oauth-authorization-server/oauth
GET                /.well-known/jwks.json
POST               /oauth/register
GET                /oauth/authorize
GET                /oauth/authorize/wait
GET                /oauth/authorization-requests/*/status
POST               /oauth/authorization-requests/*/route
POST               /oauth/token

All listed discovery, registration, authorization, pairing-status, token, JWKS, and /mcp routes go to the backend. Account approval uses /api/account/oauth-agents and /api/account/oauth-agent-claims; forward /api/* to the backend as usual. Preserve the external Host and HTTPS forwarding information. Do not cache OAuth responses or SSE, and do not rewrite the Authorization header.

Troubleshooting

  • The browser never reaches agent pairing: verify all discovery and /oauth/* proxy routes, especially /oauth/authorize/wait and the authorization-request status endpoint.
  • Audience or issuer mismatch: make PUBLIC_URL exactly match the public backend origin used in the MCP URL.
  • A write returns 403: authorize again with heaper.mcp.write; do not broaden the existing token outside the consent flow.
  • Reconnect requires another sign-in: request offline_access if persistent access is appropriate for that client.
  • heaper is not found: install the package globally and restart the agent so it receives the updated PATH.
  • No CLI connection: run heaper auth status, log in to a remote server, or start Electron with the Local API enabled.
  • MCP protocol parse errors over stdio: remove shell wrappers that print banners to stdout, which is reserved for MCP messages.

Test the deployed browser flow

Deploy the updated backend and connect a fresh OAuth-capable MCP client to https://dev.heaper.de/mcp for staging, or https://<your-server>/mcp for self-hosting. No app frontend deployment is needed. Check that the browser remains on the backend's /oauth/authorize/wait page during sign-in and heap selection, returns to the MCP client after Connect agent, and that the agent can list only the granted heaps. Test a read-only grant, a write grant, reconnecting with Stay connected, rejection, and revocation from Settings > Agents. Use an approved test account and dedicated test heaps; never reset staging data.

Developers can rebuild the embedded JavaScript with cd apps/ui && bun run build:oauth-page; bun run build:oauth-page --check detects stale generated output. The committed bundle is embedded by Go, so backend/self-hosted Docker builds do not need an app build. The PostgreSQL integration test TestHeaperOAuthAccountPairingPostgres exercises both modes through the production HTTP router and always creates new databases. See backend/tests/oauth-browser-testing.md for browser fixture instructions.