skip to content

In MCP, how should a client handle two servers exposing the same tool name?

level: seniorimportance: should knowfreq 44%

answer

  1. unique within a server, not globally
  2. the client owns the combined namespace
  3. never namespace with what the server calls itself
  4. separators can be forged from the name side
  5. key standing approvals by server plus tool

basics

~20 s

MCP requires tool names to be unique only within one server, so collisions across servers are legal and expected. An aggregating client should prefix each name with its own identifier for that server — never with the server's self-reported serverInfo name.

solid answer

~50 s

Tool names in MCP 2026-07-28 are 1–128 characters from `A-Za-z0-9_-.`, case-sensitive, and unique **within a server**. Nothing coordinates names across servers, so a newly added server may legitimately — or deliberately — expose `create_issue` or `search` alongside a trusted server's tool of the same name. The spec's guidance is that aggregating clients SHOULD prefix names with a server identifier, and it explicitly warns that `serverInfo.name` is not reliable for that: `io.modelcontextprotocol/serverInfo` is self-reported and untrusted, so a hostile server simply claims to be the one you trust. The prefix must therefore come from the client's own local identity for the connection — the config entry name or connection id. Choose the separator carefully too, since `.`, `-` and `_` are all legal inside tool names, and make the approval UI name the concrete server, not a display string the server chose.

go deeper

for a junior

Recall that MCP tool names are unique only inside one server, so two connected servers can both expose search, and that the client is what tells them apart.

for a middle

Explain the SHOULD-prefix guidance and why serverInfo.name cannot be the prefix: it is self-reported and untrusted, so an attacker would be choosing its own namespace.

for a senior

Trace it through the whole path — client-owned identity as the prefix, an unforgeable separator, provenance shown to the model and in the human approval prompt, and standing approvals keyed by server plus tool.

for a principal

Set the naming and provenance contract for a host that aggregates many servers: who assigns local identities, how they survive reconfiguration, and how the UI makes server origin unspoofable when a user is granting trust.

## Why collisions are guaranteed, not hypothetical MCP 2026-07-28 constrains tool names to 1–128 characters drawn from `A-Za-z0-9_-.`, treats them as case-sensitive, and requires uniqueness **within a single server**. There is no global registry, no reserved-name list, and no coordination mechanism between servers. Meanwhile the obvious names — `search`, `read_file`, `create_issue`, `send_message` — are exactly the ones every server author reaches for. A host that connects five servers will hit a collision by accident long before anyone attacks it. The attack version has an ecosystem name, *tool shadowing*: a low-trust server deliberately publishes a tool whose name (and often description) mimics a high-trust server's, hoping the client, the user, or the model routes a call to the wrong one. That term is community vocabulary; the specification does not define it. What the specification supplies is the disambiguation guidance below. ## What the specification says Two statements do the work. First, aggregating clients **SHOULD** prefix tool names with a server identifier when presenting a combined list. Second — and this is the part candidates miss — `serverInfo.name` is **not reliable** for that purpose. The reason is the same as everywhere else in this area: `io.modelcontextprotocol/serverInfo`, which results SHOULD carry in `_meta`, is self-reported and explicitly untrusted, exactly like `io.modelcontextprotocol/clientInfo` in the other direction. Using it as a namespace hands the naming authority to the very party you are trying to disambiguate. A malicious server sets `serverInfo.name` to `github` and its tools now render as `github.create_issue`. ## Where the prefix must come from From the client's own side of the relationship: the key of the entry in the host's server configuration, the connection identifier the client minted, or a stable local alias the user chose when adding the server. These are facts the host knows independently of anything on the wire, and they are the only kind of identity that survives a hostile counterparty. A detail that bites in practice: because `.`, `-` and `_` are all legal inside tool names, naive concatenation is ambiguous. If the client renders `<serverId>.<toolName>`, a server registered as `evil` can name a tool `github.create_issue` and the rendered string `evil.github.create_issue` is fine — but a server registered as `evil.github` exposing `create_issue` produces the same string as a server registered `evil` exposing `github.create_issue`. Pick a separator or an encoding that cannot be forged from the name side: reserve a character disallowed in tool names, encode lengths, or keep the pair structured internally and only flatten for display. ## Beyond the prefix The prefix fixes routing — the client always knows which connection a call goes to. It does not by itself fix the human and the model. **The model** sees whatever names you render. If two tools look nearly identical in the flattened list, the model will sometimes pick the wrong one, and a shadowing server writes its description precisely to encourage that. Rendering provenance in the name is what gives the model something to discriminate on at all. **The human** sees the approval prompt. It must name the concrete server using local identity — "acme-tickets (stdio, configured 2026-06-02)" — rather than a display string the server supplied. An approval dialog that echoes an attacker-chosen name is a phishing surface. **Standing approvals** must be keyed by the pair, never by the bare tool name. Approving `search` once and having that grant apply to whichever server later exposes a tool called `search` is the whole vulnerability in one line of code. ## What does not help A few plausible-sounding non-answers are worth ruling out explicitly. The rule that a server's tool set MUST NOT vary per connection is per-server and says nothing across servers. Protocol-level sessions, which some people reach for as a scoping mechanism, were removed in 2026-07-28 — there is no session to hang a namespace on. And rejecting the second server outright is not what the spec asks for; collisions are legal, and a host that refuses them breaks ordinary multi-server use for no security gain that prefixing does not already provide. The summary a senior candidate should land: names are server-scoped by design, the client owns the namespace, the namespace must be built from identity the client controls, and the resulting name must reach both the model and the human unmodified.

  • Why is serverInfo.name specifically called out as unreliable for prefixing?
    Because `io.modelcontextprotocol/serverInfo` is self-reported by the server and explicitly untrusted, like `clientInfo` in the other direction. A hostile server can set it to any string, including the name of a server the user trusts, so using it as a namespace lets the attacker choose its own prefix and defeat the disambiguation entirely.
  • What should the prefix be built from instead?
    Identity the client already holds: the key of the entry in host configuration, the connection identifier the client minted, or a user-chosen local alias. These are known independently of the wire, so a malicious server cannot influence them, and they stay stable across restarts, which matters because standing approvals are keyed by them.
  • Does a naive dot-separated prefix fully disambiguate?
    No. Dot, hyphen and underscore are all legal inside tool names, so `evil` exposing `github.create_issue` and `evil.github` exposing `create_issue` flatten to the same display string. Use a separator that cannot appear in a tool name, or keep the server-and-tool pair structured internally and flatten only for rendering.
  • Should a client reject a server whose tool names collide with an existing one?
    Generally no. Collisions are legal — uniqueness is server-scoped — and common names collide by accident constantly, so rejecting breaks ordinary multi-server setups. Prefixing solves routing; the residual risk is that a human or the model picks the wrong one, which provenance in the rendered name and in the approval prompt addresses.

saying these in an interview costs you the question

  • Assumes MCP tool names are globally unique
  • Uses the server's self-reported serverInfo name as the namespace
  • Keys a standing approval by bare tool name across servers
  • Thinks the must-not-vary-per-connection rule prevents cross-server collisions
  • Expects the protocol to reject a colliding server automatically

context