From 9ddd831b34f37f950e54d1e3ebfca97172f6e044 Mon Sep 17 00:00:00 2001 From: David Kaya Date: Sat, 28 Mar 2026 20:09:45 +0100 Subject: [PATCH] fix: preserve probed MCP tools across session switches mergeDiscoveredToolingState now carries over probedTools from existing servers when the config fingerprint is unchanged. The equality check in syncProjectDiscoveredTooling also strips probedTools before comparing, so runtime-only probe data never triggers a spurious state replacement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/main/AryxAppService.ts | 6 ++-- src/shared/domain/discoveredTooling.ts | 1 + tests/shared/discoveredTooling.test.ts | 46 ++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/main/AryxAppService.ts b/src/main/AryxAppService.ts index 561b343..7bbb30b 100644 --- a/src/main/AryxAppService.ts +++ b/src/main/AryxAppService.ts @@ -2348,8 +2348,10 @@ export class AryxAppService extends EventEmitter { left?: DiscoveredToolingState, right?: DiscoveredToolingState, ): boolean { - return JSON.stringify(normalizeDiscoveredToolingState(left).mcpServers) - === JSON.stringify(normalizeDiscoveredToolingState(right).mcpServers); + const stripRuntime = (servers: DiscoveredMcpServer[]) => + servers.map(({ probedTools: _, ...rest }) => rest); + return JSON.stringify(stripRuntime(normalizeDiscoveredToolingState(left).mcpServers)) + === JSON.stringify(stripRuntime(normalizeDiscoveredToolingState(right).mcpServers)); } private equalProjectCustomizationState( diff --git a/src/shared/domain/discoveredTooling.ts b/src/shared/domain/discoveredTooling.ts index 8c5874f..65fc860 100644 --- a/src/shared/domain/discoveredTooling.ts +++ b/src/shared/domain/discoveredTooling.ts @@ -122,6 +122,7 @@ export function mergeDiscoveredToolingState( return { ...server, status: existing.status, + probedTools: existing.probedTools, } satisfies DiscoveredMcpServer; } diff --git a/tests/shared/discoveredTooling.test.ts b/tests/shared/discoveredTooling.test.ts index 9c9c18c..5521dd9 100644 --- a/tests/shared/discoveredTooling.test.ts +++ b/tests/shared/discoveredTooling.test.ts @@ -105,4 +105,50 @@ describe('discovered tooling helpers', () => { expect(listPendingDiscoveredMcpServers(dismissed)).toEqual([]); expect(dismissed.mcpServers[1]?.status).toBe('dismissed'); }); + + test('preserves probedTools when fingerprint is unchanged on re-scan', () => { + const probedTools = [ + { name: 'git_status', description: 'Get git status' }, + { name: 'git_diff', description: 'Get git diff' }, + ]; + const current = { + mcpServers: [ + createDiscoveredServer({ + status: 'accepted', + probedTools, + }), + ], + lastScannedAt: TIMESTAMP, + }; + + const merged = mergeDiscoveredToolingState( + current, + [createDiscoveredServer()], + '2026-03-25T01:00:00.000Z', + ); + + expect(merged.mcpServers[0]?.probedTools).toEqual(probedTools); + expect(merged.mcpServers[0]?.status).toBe('accepted'); + }); + + test('drops probedTools when fingerprint changes on re-scan', () => { + const current = { + mcpServers: [ + createDiscoveredServer({ + status: 'accepted', + probedTools: [{ name: 'git_status' }], + }), + ], + lastScannedAt: TIMESTAMP, + }; + + const merged = mergeDiscoveredToolingState( + current, + [createDiscoveredServer({ args: ['server.js', '--debug'] })], + '2026-03-25T01:00:00.000Z', + ); + + expect(merged.mcpServers[0]?.probedTools).toBeUndefined(); + expect(merged.mcpServers[0]?.status).toBe('pending'); + }); });