Skip to content

Commit f7d59ab

Browse files
PierrunoYTclaude
andcommitted
fix(mcp): keep both name separators when shortening a long MCP tool name
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
1 parent 76be781 commit f7d59ab

3 files changed

Lines changed: 65 additions & 1 deletion

File tree

‎src/core/assistant-message/__tests__/NativeToolCallParser-mcp-long-name.spec.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ describe("NativeToolCallParser.parseDynamicMcpTool name for API history", () =>
2424
expect(result?.name).toBe(declared)
2525
})
2626

27+
it("parses the declared name of a tool on a server with a very long name", () => {
28+
const declared = buildMcpToolName("s".repeat(60), TOOL)
29+
30+
const result = NativeToolCallParser.parseDynamicMcpTool({ id: "toolu_4", name: declared, arguments: "{}" })
31+
32+
expect(result).toMatchObject({ type: "mcp_tool_use", name: declared })
33+
})
34+
2735
it("keeps a short name verbatim", () => {
2836
const result = NativeToolCallParser.parseDynamicMcpTool({
2937
id: "toolu_3",

‎src/utils/__tests__/mcp-name-long.spec.ts‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,52 @@ describe("long MCP tool names", () => {
1818
expect(name.slice(55)).toMatch(/^_[0-9a-f]{8}$/)
1919
})
2020

21+
it.each([
22+
{ toolLength: 50, shortened: false },
23+
{ toolLength: 51, shortened: true },
24+
])(
25+
"treats a $toolLength-character tool as shortened: $shortened at the 64-character limit",
26+
({ toolLength, shortened }) => {
27+
const tool = "t".repeat(toolLength)
28+
const uncut = `mcp--weather--${tool}`
29+
const name = buildMcpToolName("weather", tool)
30+
31+
expect(uncut).toHaveLength(14 + toolLength)
32+
expect(name).toHaveLength(64)
33+
if (shortened) {
34+
expect(name).toMatch(/_[0-9a-f]{8}$/)
35+
} else {
36+
expect(name).toBe(uncut)
37+
}
38+
},
39+
)
40+
41+
describe("with a long server name", () => {
42+
it.each([46, 47, 48, 49, 50, 70])("keeps both separators for a %i-character server name", (serverLength) => {
43+
const server = `s${"x".repeat(serverLength - 1)}`
44+
const name = buildMcpToolName(server, TOOL)
45+
const parsed = parseMcpToolName(name)
46+
47+
expect(name).toHaveLength(64)
48+
expect(name).toMatch(/_[0-9a-f]{8}$/)
49+
expect(parsed).not.toBeNull()
50+
expect(server.startsWith(parsed!.serverName)).toBe(true)
51+
expect(parsed!.toolName.length).toBeGreaterThan(0)
52+
expect(resolveMcpToolByName([{ name: server, tools: [{ name: TOOL }] }], name)).toEqual({
53+
serverName: server,
54+
toolName: TOOL,
55+
})
56+
})
57+
58+
it("drops a hyphen at the cut so it does not merge with the separator", () => {
59+
const server = `${"a".repeat(46)}-${"b".repeat(10)}`
60+
const name = buildMcpToolName(server, TOOL)
61+
62+
expect(name).not.toContain("---")
63+
expect(parseMcpToolName(name)?.serverName).toBe("a".repeat(46))
64+
})
65+
})
66+
2167
it("is deterministic and keeps tools that share a 55-character prefix distinct", () => {
2268
const first = buildMcpToolName(SERVER, `${TOOL}_a`)
2369
const second = buildMcpToolName(SERVER, `${TOOL}_b`)

‎src/utils/mcp-name.ts‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,17 @@ export function buildMcpToolName(serverName: string, toolName: string): string {
151151
}
152152

153153
const hash = createHash("sha256").update(fullName).digest("hex").slice(0, HASH_SUFFIX_LENGTH)
154-
return `${fullName.slice(0, MAX_TOOL_NAME_LENGTH - HASH_SUFFIX_LENGTH - 1)}_${hash}`
154+
const budget = MAX_TOOL_NAME_LENGTH - HASH_SUFFIX_LENGTH - 1
155+
const prefix = `${MCP_TOOL_PREFIX}${MCP_TOOL_SEPARATOR}`
156+
// Cap the server part so the second separator and at least one tool character survive;
157+
// otherwise the shortened name no longer parses as mcp--server--tool and the call is dropped.
158+
// A trailing hyphen is removed so it cannot merge with the separator.
159+
const maxServerLength = budget - prefix.length - MCP_TOOL_SEPARATOR.length - 1
160+
const server = sanitizeMcpName(serverName).slice(0, maxServerLength).replace(/-+$/, "")
161+
const head = `${prefix}${server}${MCP_TOOL_SEPARATOR}`
162+
const tool = sanitizeMcpName(toolName).slice(0, budget - head.length)
163+
164+
return `${head}${tool}_${hash}`
155165
}
156166

157167
/**

0 commit comments

Comments
 (0)