From f0114058bad2e0845b4b8be656d9c33b0318aaad Mon Sep 17 00:00:00 2001 From: David Kaya Date: Sat, 28 Mar 2026 19:35:56 +0100 Subject: [PATCH] fix: show MCP tools in all provider groups when shared When multiple MCP servers expose the same tool names (e.g., several kusto-mcp instances), each server's group now shows its tools instead of only the first server getting the tools and others showing 0/0. Deduplicate the effectiveAutoApprovedCount to avoid double-counting shared tools in the pill header. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/renderer/components/ChatPane.tsx | 10 ++++++---- src/shared/domain/tooling.ts | 16 ++++++++++++++++ tests/shared/tooling.test.ts | 6 ++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/renderer/components/ChatPane.tsx b/src/renderer/components/ChatPane.tsx index 91354c9..7d158b8 100644 --- a/src/renderer/components/ChatPane.tsx +++ b/src/renderer/components/ChatPane.tsx @@ -131,15 +131,17 @@ export function ChatPane({ ); const effectiveAutoApprovedCount = useMemo(() => { const groups = groupApprovalToolsByProvider(approvalTools, toolingSettings); - let count = 0; + const counted = new Set(); for (const group of groups) { if (group.serverApprovalKey && effectiveAutoApproved.has(group.serverApprovalKey)) { - count += group.tools.length; + for (const tool of group.tools) counted.add(tool.id); } else { - count += group.tools.filter((t) => effectiveAutoApproved.has(t.id)).length; + for (const tool of group.tools) { + if (effectiveAutoApproved.has(tool.id)) counted.add(tool.id); + } } } - return count; + return counted.size; }, [approvalTools, effectiveAutoApproved, toolingSettings]); useEffect(() => { diff --git a/src/shared/domain/tooling.ts b/src/shared/domain/tooling.ts index bc2f170..e1c23bb 100644 --- a/src/shared/domain/tooling.ts +++ b/src/shared/domain/tooling.ts @@ -306,6 +306,22 @@ export function groupApprovalToolsByProvider( const groups = new Map(); for (const tool of tools) { + // Place MCP tools in every provider group they belong to, so each server + // shows its own tools even when multiple servers share the same tool names. + if (tool.kind === 'mcp' && tool.providerIds.length > 1) { + for (const providerId of tool.providerIds) { + const groupId = `mcp:${providerId}`; + let group = groups.get(groupId); + if (!group) { + const label = serverNames.get(providerId) ?? providerId; + group = { id: groupId, label, kind: 'mcp', tools: [] }; + groups.set(groupId, group); + } + group.tools.push(tool); + } + continue; + } + const groupKey = resolveApprovalToolGroupKey(tool, serverNames, profileNames); let group = groups.get(groupKey.id); if (!group) { diff --git a/tests/shared/tooling.test.ts b/tests/shared/tooling.test.ts index de1a28b..e114a07 100644 --- a/tests/shared/tooling.test.ts +++ b/tests/shared/tooling.test.ts @@ -443,7 +443,7 @@ describe('groupApprovalToolsByProvider', () => { expect(builtinGroups[0].tools.length).toBe(5); }); - test('multi-provider tools go into first provider group, second provider gets empty group', () => { + test('multi-provider tools appear in all provider groups', () => { const tooling = makeTooling([ makeMcpServer('git-a', 'Git A', ['git.status']), makeMcpServer('git-b', 'Git B', ['git.status']), @@ -455,10 +455,12 @@ describe('groupApprovalToolsByProvider', () => { expect(mcpGroups.length).toBe(2); const gitA = mcpGroups.find((g) => g.label === 'Git A'); expect(gitA).toBeDefined(); + expect(gitA!.tools.length).toBe(1); expect(gitA!.tools[0].providerNames).toEqual(['Git A', 'Git B']); const gitB = mcpGroups.find((g) => g.label === 'Git B'); expect(gitB).toBeDefined(); - expect(gitB!.tools.length).toBe(0); + expect(gitB!.tools.length).toBe(1); + expect(gitB!.tools[0].id).toBe('git.status'); }); test('sorts builtin first, then MCP by name, then LSP by name', () => {