Skip to main content

Combine several MCP servers behind one endpoint

Do this when a client — a Codex or Claude harness, an IDE, another agent — should see one MCP endpoint that exposes tools drawn from several servers. Do not do this to give an AgentArea agent tools; an agent is configured with its own tool list and does not need an aggregate. The mechanism is a registered client. A client is a governable entity that owns a set of MCP instances and skills, and gets a single MCP endpoint that merges them. There is no separate “compound MCP” resource in the API — earlier drafts of these docs described a compound-mcps collection that was never part of the shipped surface.

Prerequisites

  • Two or more MCP server instances that verified successfully. An instance whose URL cannot be resolved is skipped from the bundle silently, so verify first with Add a hosted MCP server.
  • An API key for the workspace.
  • Optionally a project, if you want several clients to share one curated set.

Choose how members are supplied

The effective set is the union of the client’s own attachments and those of its source project, so “both” needs no extra configuration.

Steps

1. Create the client

mcp_endpoint_url is the aggregate. Note it is served at /client-mcp/{client_id} — outside /v1, because it is a mounted MCP application rather than a REST route.

2. Attach instances, with a namespace each

Repeat per member. namespace_prefix decides the tool prefix: a search tool on the instance namespaced gh is exposed as gh__search. Two members that both expose search stay distinguishable only if their namespaces differ, so set the prefix deliberately rather than leaving it null.

3. Or inherit from a project

Curate the project’s members with POST /v1/projects/{project_id}/mcp-instances. Every client pointing at that project picks up changes without being touched.

4. Attach skills, if the client should have them

When a client has skills, the aggregate exposes an extra activate_skill tool whose enum lists them, alongside the namespaced member tools.

5. Point the harness at the endpoint

Give the harness mcp_endpoint_url and a token. Access is checked on every request: the token’s subject must be the client itself, or a principal holding the use relation on that client. See Issue MCP access tokens.

Verify

List the tools through the aggregate. This is the same call the harness makes.
Namespaced names from more than one member prove the aggregation resolved. Then call one to prove forwarding works:

Troubleshooting

tools/list returns an empty array. Either the client id in the path does not exist, or no member resolved. A member whose URL cannot be resolved is skipped and logged rather than failing the whole bundle, so one broken instance looks like a missing tool rather than an error. Check each member’s verification.status individually. “Not authorized for this client”. The token’s subject is neither the client nor a principal with use on it. A workspace API key is not automatically authorized for a client bundle. Tool names collide. Two members exposing the same tool name with the same or null namespace produce ambiguous entries. Set a distinct namespace_prefix on each member. A tool that exists on the server is missing from the aggregate. Member tools come from each instance’s stored snapshot, discovered at its last verification. Run POST /v1/mcp-server-instances/{instance_id}/discover-tools on the member, then list again. Removing an instance from the project does not change the client. It does, but only for members the client inherited. An instance attached directly to the client stays until you DELETE /v1/clients/{client_id}/mcp-instances/{mcp_instance_id}. The effective set is a union, so a direct attachment shadows a project removal. The endpoint 404s. /client-mcp/{client_id} is a mounted application, not a /v1 route, and it is absent from the OpenAPI spec for that reason. Use the mcp_endpoint_url from the client response rather than assembling the path from the API base by hand.