Skip to main content

Remote MCP tools

Strategy update (2026-09-18): The tool integration strategy defines the planned shared catalog, named connections, progressive discovery, and Add capability flow. The delivery plan tracks implementation. Descriptions of existing behavior below remain the current contract until those changes ship. Mesh connects each agent to remote MCP servers over Streamable HTTP with an agent-owned credential: either a bearer token or API key the operator pastes, or an OAuth grant the operator authorizes in the browser (below, “Connect with OAuth”). Connections support both read and write tools and are available in every conversation with that agent, including new conversations. Multiple servers and multiple accounts at the same endpoint can be connected independently. Existing built-in Linear and GitHub tools keep their own credentials and enablement.

Connect a server

  1. Open Agents → your agent → Tools → Remote MCP connections.
  2. Enter a connection name, HTTPS MCP endpoint, and the agent’s bearer token or API key. The form starts blank. Use Linear preset optionally fills in Linear MCP and https://mcp.linear.app/mcp.
  3. Choose Connect. Mesh saves this agent’s credential, discovers the catalog, and activates every supported tool, including writes, for the next turn.
  4. Optionally choose Manage tools to exclude tools, then Save tool preferences. Select all, Deselect all, Select read-only, and individual checkboxes change this optional selection. Saving with every tool turned off is allowed.
If discovery fails, the account remains saved and the page shows catalog health. Mesh retries automatically. Connecting requires no extra approval checklist. Linear’s standard endpoint supports reading, creating and editing issues and comments using a bearer API key or OAuth access token. The token must have the corresponding permissions in Linear. See Linear’s MCP documentation. Mesh discovers the actual tools and schemas offered to that account; it does not maintain a vendor-specific tool list. For an existing connection using /mcp/readonly, choose Edit connection, then Use Linear preset, and Save changes. Leave the token blank to reuse the saved credential. The expanded catalog is discovered automatically; saved exclusions persist. Endpoint edits replace only this agent’s binding; other agents using the old endpoint keep their own connections. Named account edits never overwrite a sibling account. Older, endpoint-based connections retain their collision check when moving to an endpoint already occupied by another legacy connection.

Multiple accounts at one endpoint

Each new dashboard connection creates a separate account identity, for both bearer tokens and OAuth. Give accounts recognizable names, such as “Linear — Engineering” and “Linear — Support.” Each has its own encrypted credential, tool preferences, version, tool namespace, and turn-off action. Reauthorizing or rotating one account does not change another account, even at the same endpoint. Accounts remain agent-owned; connecting one does not grant another agent access. API clients opt into this behavior with new_account: true on POST /api/agents/{slug}/mcp/providers or POST /api/agents/{slug}/mcp/oauth/start. To update an existing account, use its returned provider_id: the versioned PUT route for bearer edits, or provider_id in OAuth start for reauthorization. OAuth start rejects requests that specify both provider_id and new_account: true. Omitting new_account preserves the legacy endpoint-based reconnect slot. It never selects or overwrites an explicitly created account. Existing connection IDs, encrypted secret references, tool approvals, historical aliases, and pending OAuth authorizations survive the migration unchanged. Pending authorizations created before the upgrade retain legacy reconnect semantics. The migration replaces the global endpoint uniqueness constraint with uniqueness for legacy endpoint slots. Do not run older binaries against the migrated schema: their endpoint upsert does not recognize the partial unique index. Drain old application replicas before applying this migration and starting new replicas. A rollback to older binaries requires restoring the pre-upgrade database backup, or explicitly removing/reconciling additional accounts and restoring the original endpoint constraint. Deleting duplicates automatically would discard credentials, approvals, or historical provenance and is not a supported rollback. This is the first implementation slice of the connection-management work. Unified native/web bindings, named agent-owned connections, and delivery of an agent’s own credentials to its workspace remain follow-up work. Shared acting accounts and cross-agent identity grants are prohibited by the identity guardrail (repository). The MCP cache still keys each binding by (agent_id, provider_id); distinct accounts therefore remain distinct cache entries without changing the cache implementation.

Connect with OAuth

Some servers issue no token to paste. Contentful’s hosted MCP server (https://mcp.contentful.com/mcp) is one: it implements the MCP authorization specification, so the only way in is to sign in and consent in a browser. Mesh is the OAuth client in that flow.
  1. In Remote MCP connections, choose Sign in with OAuth as the authentication, or press Use Contentful preset. Enter a connection name and the HTTPS MCP endpoint. Leave the pre-registered client fields empty unless the server’s documentation gave you a client ID for Mesh.
  2. Choose Continue to sign in. Mesh discovers the server’s authorization server, settles its own client identity, and sends the browser to the server’s sign-in page. Consent there is the server’s, not Mesh’s: it decides what the grant may reach (Contentful asks which spaces and environments).
  3. The browser returns to the agent’s tools page with the account connected and its supported tools active. Manage tools is optional. Reauthorizing an existing account retains its exclusions and explicit turn-off state.
What Mesh does on your behalf, and refuses to do:
  • Discovery follows the spec: an unauthenticated request for the server’s WWW-Authenticate challenge, RFC 9728 protected resource metadata (from the challenge or the well-known locations), then RFC 8414 or OpenID metadata for the authorization server, in the spec’s order. The authorization server must advertise PKCE (S256) or Mesh stops. Every URL involved must be HTTPS and public; redirects are never followed.
  • Client identity is chosen in the spec’s priority: pre-registered credentials you entered, then a Client ID Metadata Document when the authorization server supports one (Mesh serves its own at PUBLIC_URL/api/mcp/oauth/client; this needs an HTTPS PUBLIC_URL), then RFC 7591 dynamic registration. A server offering none of these needs a pre-registered client ID from its operator.
  • The authorization request carries a PKCE challenge, an unguessable state, the RFC 8707 resource (the server’s own identifier from its metadata) and the scopes the server asked for in its challenge, or those its metadata lists. Mesh requests nothing beyond that.
  • The redirect URI is always PUBLIC_URL/api/mcp/oauth/callback. The callback is single-use and expires ten minutes after the start; it must be completed by the operator account that started it, and when the server says it echoes its issuer (RFC 9207) a mismatch is refused before any code is redeemed.
  • Tokens are stored encrypted under MESH_AGENT_MASTER_KEY in the same secrets row a pasted token would use, together with the token endpoint and client identity needed to refresh them. Mesh refreshes an access token itself, shortly before it expires or once when the server answers 401, under the connection’s row lock so replicas never spend a single-use refresh token twice. Rotated refresh tokens replace the old one.
  • A refused refresh (invalid_grant: the grant was revoked, or the server issued no refresh token and the access token expired) marks the connection with auth_error. Its tools stop being offered without stopping the turn, the dashboard shows the reason, and Reconnect with OAuth starts a new authorization for the same connection. Reauthorizing replaces the credential and preserves tool preferences; renaming keeps them too. Moving an OAuth connection to another endpoint means connecting that endpoint with OAuth: the grant is bound to the resource it was issued for.
Mesh never asks for scopes on its own, never sends a token anywhere but the registered endpoint and its token endpoint, and never surfaces access or refresh tokens through the API or the dashboard.

Identity and execution

tool_providers describes an endpoint. agent_tool_providers binds it to an agent-owned encrypted secret, connection label, exclusions, optional legacy tool classifications, and the credential’s auth_kind (bearer or oauth). A pending browser authorization lives encrypted in mcp_oauth_authorizations until its callback consumes it. Sharing an endpoint never shares accounts, catalogs or permissions between agents. Credentials are never returned by the API. The access policy is all supported tools except explicit exclusions. New tools are included automatically; changed definitions do not reset the selection. Exclusions include removed names so a tool that reappears stays off. Credential rotation and endpoint edits preserve preferences and explicit connection turn-offs. All changes still fence offers created under an earlier connection version. Tools use ClassEffect/Effectful automatically: Mesh records the durable effect boundary before the call and never replays an uncertain remote operation. The operator does not have to classify tools. The legacy API still accepts a query classification against an exact fingerprint. An unchanged explicit query keeps ClassQuery/Replayable; a changed definition returns to effect semantics. Unknown classes are refused on submission and interpreted as effects in stored legacy rows. Server read-only annotations remain hints; they do not authorize replay. A query classification does not widen where a call may go: every MCP call, whatever its class, is an outbound request to the registered endpoint and nowhere else (R21 (repository) is about query tools that fetch arbitrary URLs from the key-holding process; a remote MCP tool cannot). Discovery fingerprints the full advertised definition, including descriptions, schemas and annotations. Every call verifies that its offered fingerprint still matches the live definition. Refreshing adopts current definitions automatically; an in-flight call with an older definition is rejected. Unsupported schemas are shown in Manage tools and omitted from the offer. Turn off requires no network access.

Upgrading existing connections

Migration 20261002233000_mcp_tool_preferences.sql converts reviewed subsets to exclusions using a catalog from the current connection version. If the snapshot is absent or stale, the old allowlist remains until the first successful discovery can convert it. Stored classes and credential ownership are retained. Unreviewed connections left off by the old setup default become active automatically. Explicit turn-offs are preserved from stored selections and the latest successful enable/disable audit event. Repeated boots do not rewrite the migrated policy. API clients can submit { "version": n, "disabled_tools": [] } to the existing enable route to allow all supported tools, or include the names to exclude. Use { "version": n, "keep_preferences": true } to turn an account back on without replacing its preferences, including a legacy subset. The version-fenced legacy { "version": n, "tools": [...] } request remains supported and is converted to exclusions. Do not supply both formats.

Cached discovery

A turn builds its remote tool offer from a stored catalog rather than contacting every connected server first. Before this, every turn opened a session per connection and paged tools/list before the model saw anything — a round trip per server on the critical path, under a 40-second budget, to re-learn a list that changes on the order of releases. There is nothing to switch on. A connection whose catalog is not stored, or was stored under a different configuration of that connection, is read live for that turn exactly as it always was, and the result is kept for the next one. So the cache can only make a turn faster; it can never make an enabled tool disappear while waiting for a background refresh, which is what would make it a mode rather than an optimisation. What is cached is metadata, never authority:
  • The offer contains the current supported catalog minus the account’s exclusions, with effect semantics or an unchanged explicit legacy query classification.
  • Every call still opens a session and verifies the selected tool’s full definition against the live server before invoking it. A tool that changed since the offer was built is refused at that point whether or not a snapshot still lists it. Caching removes a round trip from the offer, not the guard from the call.
  • A snapshot belongs to one agent’s connection, not to an endpoint and not to a provider row. Two agents connected to the same server hold separate catalogs, and so do two independent accounts at that server, because each holds its own credential and is shown its own tools.
Staleness is decided by the connection version, so a reconnect, a credential rotation, an endpoint edit, a preference change, or a turn-off invalidates the stored catalog immediately and without a network call — the connection simply offers nothing until the next refresh, rather than offering something captured under different permissions. Renewing an OAuth access token is not such a change: it rewrites the stored credential in place and the catalog survives it. A catalog is captured automatically on connection, and when an operator discovers or saves preferences, and refreshed by a background worker roughly every fifteen minutes per connection. Refreshes are claimed under a lease in a single statement, so every replica can run the worker and a connection due for refresh is still read once, by one of them, in batches — replicas cannot compound into a refresh storm. When a server cannot be reached, its last catalog is kept and still offered, and the connection reports why it is stale along with when it was last read. Emptying it would silently withdraw enabled tools over a transient outage while looking like a server that offers nothing. The connection listing carries this as catalog: health (pending, ok, unavailable, unauthorized), an operator-facing detail, captured_at, failed_at, revision, and stale. Mesh’s own fixed sentences fill detail; text from the remote server never does. catalog appears as soon as a connection has been looked at, which includes a first read that failed — that case reports the failure with no captured_at and revision zero, because there is nothing stored behind it yet and the connection offers nothing. stale means a catalog exists but was captured under a different configuration of the connection, so it too is offering nothing until the next refresh. A connection no refresh has yet attempted has no catalog at all. Live discovery has not gone away: it is the fallback, and it is what a cold or superseded catalog still uses. Only the connections a stored catalog cannot serve are read, so one newly connected server does not cost the others a round trip. Remote write failures can leave an uncertain outcome. Mesh does not automatically retry effectful calls after a crash or transport failure; an effect tool’s remote failure stops the turn with the effect recorded for recovery. A query tool’s remote failure is an observation the model sees and can recover from (narrow, retry, or explain). Local persistence failures stop the turn for both. MCP transport retries remain disabled for both classes; the query class changes what the turn does with a failure, not how many times the wire is tried.

Knowledge and provenance

MCP results belong to the agent, without a conversation allowlist or audience snapshot. Before calling a tool, Mesh validates the persisted call’s agent/run ownership and records provider, remote name and fingerprint in tool_source_evidence. Successful observations retain that evidence in memory writes and reply provenance. Tool-source leaves carry no conversation restriction; public in this evidence representation means unrestricted across the owning agent’s conversations, not shared credentials or cross-agent memory access. Ordinary human-message evidence still retains its own privacy rules. A memory that combines external data with private conversation content retains the restrictions belonging to that human content. Migration 0056_agent_wide_mcp.sql removes the old MCP conversation scopes and updates existing tool-source leaves in memories and replies without changing human-source leaves.

Supported protocol and resource limits

  • Public HTTPS endpoints on port 443, authenticated by an Authorization: Bearer header sent only to the registered URL. Private addresses, redirects, environment proxies, URL credentials and query parameters are refused.
  • Up to 256 discovered tools and 16 discovery pages per connection operation. Every supported, non-excluded tool is offered across all enabled connections; there is no additional aggregate tool-count or 64 KiB schema selection cap. Definitions still consume the model’s ordinary context budget.
  • Input/output schemas up to 32 KiB; descriptions up to 4 KiB; HTTP response streams up to 2 MiB; model-visible results up to 512 KiB. A larger result is truncated to fit, never refused: structured content is dropped first (it usually duplicates the text), then the text’s tail is cut on a rune boundary, and the envelope carries a truncated notice with the total and shown sizes and how to ask for less. The credential check runs on the whole result before the cut.
  • Validated inline JSON Schemas, at most 64 levels deep. Schema references and x-mcp-header are unsupported. Unsupported tools are displayed but not selected.
  • Text and structured JSON results. Non-text results return errors; resource links and attachments are not fetched.
  • A 20-second operation deadline, at most four concurrent provider discoveries, and a 40-second overall discovery budget. Completed catalogs remain available if other servers time out. Local database failures and parent cancellation stop discovery.
  • Up to 1,000 tool-source IDs per memory or reply. Combined reply lineage beyond 5,000 sources uses the existing unknown-provenance compaction behavior.
The official Go MCP SDK handles negotiation and Streamable HTTP; its oauthex and auth packages parse challenges and metadata and perform dynamic registration, while Mesh owns the browser round-trip, token storage and refresh (internal/tool/mcp/oauth.go). Local stdio, step-up scope challenges (insufficient_scope at runtime), roots, sampling, elicitation, resources and prompts are not supported by this connection flow.

Administration API

Routes require the existing authenticated agent-access authorization and CSRF boundary, and return Cache-Control: no-store. OAuth routes. The start is agent-scoped; the callback and client metadata are install-wide because an authorization server sees one redirect URI and one client identity per install. Connections list auth_kind and, when a grant was refused, auth_error. They also list catalog once the connection has been looked at — see “Cached discovery” above for its fields and for what a stale or unavailable catalog means. Migration 0069 adds mcp_catalog_snapshots, one row per connection. Its tool documents are stored in a json column rather than jsonb on purpose: a fingerprint is taken over the exact bytes a server advertised, and jsonb stores a parsed structure that it re-renders with sorted keys, so a round trip through it would change every fingerprint and read as though every reviewed tool had been modified. Migration 0055 requires ordinary PostgreSQL and is applied by the normal migration runner. It drops conversation_scopes; older MCP binaries cannot be run against this schema. The upgrade preserves enabled connections and selected tools, making them agent-wide. Existing read-only endpoints remain read-only until edited. Integration tests use real PostgreSQL and local TLS MCP servers. They cover multiple agents and servers, read/write calls, new conversations, encrypted credential reuse, stale reviews, revocation, schema validation and provenance. They do not contact Linear or require production credentials.