Skip to content

Apps cannot call their server's tools through a namespacing aggregator #745

Description

@blakebauman

Summary

An app calls its server's tools by the name the server registered. When the host is connected to an aggregating intermediary (an MCP gateway, proxy, or multi-server bridge) rather than to the server directly, that name has usually been rewritten to keep two servers' tools distinct — and the app has no way to learn the rewritten name. Its tools/call fails.

This is not a hypothetical intermediary: aggregation is a common deployment for MCP, and prefixing tool names is how essentially every aggregator keeps search from one server distinct from search from another.

Observed

Behind a gateway that exposes an upstream's get-time as alpha__get-time:

// what the app sends (AppBridge forwards it verbatim to the server)
{"method":"tools/call","params":{"name":"get-time"}}
// what comes back
{"error":{"code":-32043,"message":"unknown name \"get-time\": no upstream owns this namespace"}}

// what the model uses, from the same tools/list the host holds
{"method":"tools/call","params":{"name":"alpha__get-time"}} → ok

examples/lazy-auth-server/src/mcp-app.ts is one of the apps in this repository that would hit it — it calls callTool("get_secret") and callTool("revoke_auth_token") with names written into the bundle.

The reference AppBridge forwards an app's tools/call straight through (src/app-bridge.ts, the oncalltool installed in connect), so nothing between the app and the server corrects or rejects the name; the failure surfaces as a server-side error for a tool the app can see listed under a different name.

What already works

An app that reads hostContext.toolInfo.tool.name from the ui/initialize result gets the name the host knows, which is correct through an aggregator. So the gap is specific: an app can always call the tool that opened it, but not any other tool on its own server.

Possible directions

  1. Document the constraint — apps SHOULD derive names from hostContext.toolInfo or from tools/list rather than hardcoding them. Cheapest, and it makes existing examples the guidance rather than the counterexample.
  2. Give the app the callable set at initialize — e.g. the tools of the originating server in McpUiInitializeResult, which is where the app already receives toolInfo.
  3. Let the app address tools relative to its own server, leaving the host to map that onto whatever name it holds.

Happy to send a PR for (1) against the examples and the spec text if that is the direction you'd want.

Context

Found while adding MCP Apps support to an MCP gateway. Everything else federated cleanly — _meta.ui survives, and interface resources resolve once the gateway scopes ui:// URIs per server (they are unique only within a server, which is a separate report I can file if useful).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions