google-gemini / google-gemini/gemini-cli
MCP tool names over 63 chars can collide after truncation, dropping one tool from the registry
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
## What happened?
`generateValidName` shortens qualified MCP tool names that exceed the Gemini API's function-name limit to their first 30 and last 30 characters (`mcp-tool.ts:608-615`). That transform is not injective: every name sharing those two windows maps to the same result. Two tools on one server whose names differ only inside the discarded middle therefore produce the same registry name, and `ToolRegistry.registerTool` overwrites the first with the second (`tool-registry.ts:271-279`).
The server advertises two tools; one entry ends up in the registry, backed by whichever tool was registered last.
**Reproduced** against a stdio MCP server, driving `discoverMcpTools` from the built `packages/core/dist`:
```
server "atlassian-rovo-mcp-server-cloud" advertises:
createConfluencePageWithAttachmentsAndPermissions
deleteConfluencePageWithAttachmentsAndPermissions
result:
Tool with name "mcp_atlassian-rovo-mcp-server-...eWithAttachmentsAndPermissions"
is already registered. Overwriting.
server advertised : 2 tools
registry holds : 1 mcp tool
backed by: deleteConfluencePageWithAttachmentsAndPermissions
```
**Scope.** Collision requires both names to be long enough to be truncated and to share their final 30 characters, differing only in the discarded middle. A `create` / `delete` prefix pair differs exactly there, so two tools with different risk profiles can collapse into one entry, and which one survives depends on registration order.
## What did you expect to happen?
Two distinct MCP tools stay distinctly addressable — either the generated name remains unique, or the collision is surfaced rather than resolved by overwriting.
## Client information
```
CLI Version: 0.56.0-nightly.20260806.g761f604c1 (commit 5411f113c)
OS: macOS 26.3.1 (arm64)
Node: v26.7.0
Auth Method: n/a (collision happens during MCP discovery, before any model request)
```
## Anything else we need to know?
Why this reads as unintended
- PR #20987, which introduced the truncation, describes it as a middle-truncation strategy **"to preserve uniqueness"** in long names.
- `docs/tools/mcp-server.md` documents the rule; the section immediately following it is titled *"Tool naming and namespaces … To prevent collisions"*.
- The existing tests around `generateValidName` assert length and shape, but not injectivity.
- The overwrite branch carries the comment `// Decide on behavior: throw error, log warning, or allow overwrite`.
Possible fixes
1. **Make truncation injective** — insert a short digest of the full qualified name between the retained head and tail.
2. **Make registration collision-safe** — have `registerTool` reject or disambiguate rather than overwrite.
I have (1) implemented and tested locally and can open a PR, or take (2) if maintainers prefer.
Related (different root causes)
#20987 introduced the truncation · #21425 standardized the `mcp_` FQN · #27981 is `parseMcpToolName` mis-splitting underscored server names — opposite direction, same theme of encoding server+tool identity into one flat string.
Contributor guide
Research direction
Start with generateValidName in mcp-tool.ts around lines 608-615 and trace discovery into ToolRegistry.registerTool in tool-registry.ts around lines 271-279. Review the existing generateValidName tests and docs/tools/mcp-server.md, then add coverage for colliding long names; done means distinct advertised tools remain addressable or the collision is surfaced instead of silently overwriting.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100