Skip to content

Commit 285548d

Browse files
authored
fix: client capabilities tool swapping (#311)
* fix: client capabilities tool swapping * lint
1 parent 42adea0 commit 285548d

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

src/utils/tools-loader.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,6 @@ export async function loadToolsFromInput(
7272

7373
const categoryTools = toolCategories[selector as ToolCategory];
7474

75-
// Handler client capabilities logic for 'actors' category to swap call-actor for add-actor
76-
// if client supports dynamic tools.
77-
if (selector === 'actors' && doesMcpClientSupportDynamicTools(initializeRequestData)) {
78-
internalSelections.push(...categoryTools.filter((t) => t.tool.name !== HelperTools.ACTOR_CALL));
79-
internalSelections.push(addTool);
80-
continue;
81-
}
8275
if (categoryTools) {
8376
internalSelections.push(...categoryTools);
8477
continue;
@@ -115,7 +108,7 @@ export async function loadToolsFromInput(
115108
} // else: selectors provided but none are actors => do not load defaults
116109

117110
// Compose final tool list
118-
const result: ToolEntry[] = [];
111+
let result: ToolEntry[] = [];
119112

120113
// Internal tools
121114
if (selectorsProvided) {
@@ -142,13 +135,23 @@ export async function loadToolsFromInput(
142135
* If there is any tool that in some way, even indirectly (like add-actor), allows calling
143136
* Actor, then we need to ensure the get-actor-output tool is available.
144137
*/
145-
const hasCallActor = result.some((entry) => entry.tool.name === 'call-actor');
138+
const hasCallActor = result.some((entry) => entry.tool.name === HelperTools.ACTOR_CALL);
146139
const hasActorTools = result.some((entry) => entry.type === 'actor');
147-
const hasAddActorTool = result.some((entry) => entry.tool.name === 'add-actor');
140+
const hasAddActorTool = result.some((entry) => entry.tool.name === HelperTools.ACTOR_ADD);
148141
if (hasCallActor || hasActorTools || hasAddActorTool) {
149142
result.push(getActorOutput);
150143
}
151144

145+
// Handle client capabilities logic for 'actors' category to swap call-actor for add-actor
146+
// if client supports dynamic tools.
147+
const selectorContainsCallActor = selectors?.some((s) => s === HelperTools.ACTOR_CALL);
148+
if (doesMcpClientSupportDynamicTools(initializeRequestData) && hasCallActor && !selectorContainsCallActor) {
149+
// Remove call-actor
150+
result = result.filter((entry) => entry.tool.name !== HelperTools.ACTOR_CALL);
151+
// Replace with add-actor if not already present
152+
if (!hasAddActorTool) result.push(addTool);
153+
}
154+
152155
// De-duplicate by tool name for safety
153156
const seen = new Set<string>();
154157
const filtered = result.filter((entry) => !seen.has(entry.tool.name) && seen.add(entry.tool.name));

tests/integration/suite.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,26 @@ export function createIntegrationTestsSuite(
989989

990990
await client.close();
991991
});
992+
it.runIf(options.transport === 'streamable-http')(`should swap call-actor for add-actor when client supports dynamic tools for default tools`, async () => {
993+
client = await createClientFn({ clientName: 'Visual Studio Code' });
994+
const names = getToolNames(await client.listTools());
995+
996+
// should not contain call-actor but should contain add-actor
997+
expect(names).not.toContain('call-actor');
998+
expect(names).toContain('add-actor');
999+
1000+
await client.close();
1001+
});
1002+
it.runIf(options.transport === 'streamable-http')(`should NOT swap call-actor for add-actor when client supports dynamic tools when using the call-actor explicitly`, async () => {
1003+
client = await createClientFn({ clientName: 'Visual Studio Code', tools: ['call-actor'] });
1004+
const names = getToolNames(await client.listTools());
1005+
1006+
// should not contain call-actor but should contain add-actor
1007+
expect(names).toContain('call-actor');
1008+
expect(names).not.toContain('add-actor');
1009+
1010+
await client.close();
1011+
});
9921012

9931013
it('should return error message when tryging to call MCP server Actor without tool name in actor parameter', async () => {
9941014
client = await createClientFn({ tools: ['actors'] });

0 commit comments

Comments
 (0)