Skip to content

Commit 445dc3f

Browse files
travisbreaksclaude
andcommitted
fix: address PR review feedback for multi-server chatbot example
- Prefix tool names with serverName__ to prevent silent overwrites when multiple servers expose identically-named tools. Original names are preserved and used for the actual callTool requests. - Fix incorrect type guard that narrowed MCP ContentBlock to Anthropic.TextBlock (wrong type). Now uses plain .filter(). - Move this.servers.set() before listTools() so cleanup can still close the client if tool discovery throws. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0a24dd3 commit 445dc3f

1 file changed

Lines changed: 17 additions & 11 deletions

File tree

  • examples/client-multi-server/src

examples/client-multi-server/src/index.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ interface ServersConfig {
1919

2020
class MultiServerClient {
2121
private servers: Map<string, Client> = new Map();
22-
private toolToServer: Map<string, string> = new Map();
22+
private toolToServer: Map<string, { serverName: string; originalName: string }> = new Map();
2323
private _anthropic: Anthropic | null = null;
2424
private tools: Anthropic.Tool[] = [];
2525

@@ -43,19 +43,24 @@ class MultiServerClient {
4343
});
4444
const client = new Client({ name: `multi-server-client-${name}`, version: '1.0.0' });
4545
await client.connect(transport);
46+
this.servers.set(name, client);
4647

4748
// Discover tools from this server
4849
const toolsResult = await client.listTools();
4950
for (const tool of toolsResult.tools) {
50-
this.toolToServer.set(tool.name, name);
51+
const prefixedName = `${name}__${tool.name}`;
52+
if (this.toolToServer.has(prefixedName)) {
53+
console.warn(
54+
` Warning: tool "${tool.name}" from server "${name}" collides with an existing tool.`
55+
);
56+
}
57+
this.toolToServer.set(prefixedName, { serverName: name, originalName: tool.name });
5158
this.tools.push({
52-
name: tool.name,
53-
description: tool.description ?? '',
59+
name: prefixedName,
60+
description: `[${name}] ${tool.description ?? ''}`,
5461
input_schema: tool.inputSchema as Anthropic.Tool.InputSchema,
5562
});
5663
}
57-
58-
this.servers.set(name, client);
5964
console.log(
6065
` Connected to ${name} with tools: ${toolsResult.tools.map((t) => t.name).join(', ')}`
6166
);
@@ -91,8 +96,8 @@ class MultiServerClient {
9196
if (block.type === 'text') {
9297
finalText.push(block.text);
9398
} else if (block.type === 'tool_use') {
94-
const serverName = this.toolToServer.get(block.name);
95-
if (!serverName) {
99+
const mapping = this.toolToServer.get(block.name);
100+
if (!mapping) {
96101
toolResults.push({
97102
type: 'tool_result',
98103
tool_use_id: block.id,
@@ -102,17 +107,18 @@ class MultiServerClient {
102107
continue;
103108
}
104109

110+
const { serverName, originalName } = mapping;
105111
const client = this.servers.get(serverName)!;
106-
console.log(` [Calling ${block.name} on server "${serverName}"]`);
112+
console.log(` [Calling ${originalName} on server "${serverName}"]`);
107113

108114
try {
109115
const result = await client.callTool({
110-
name: block.name,
116+
name: originalName,
111117
arguments: block.input as Record<string, unknown> | undefined,
112118
});
113119

114120
const resultText = result.content
115-
.filter((c): c is Anthropic.TextBlock => c.type === 'text')
121+
.filter((c) => c.type === 'text')
116122
.map((c) => c.text)
117123
.join('\n');
118124

0 commit comments

Comments
 (0)