From c793851107491924452c3f082cdef8a18c162e34 Mon Sep 17 00:00:00 2001 From: slhafzjw Date: Fri, 16 Jan 2026 21:48:17 +0800 Subject: [PATCH] fix(LocalRunnerClient): support cleaning non-existing MCP Servers' tools while MCP configuration files changed in CommonMcp --- .../core/action/runner/LocalRunnerClient.java | 14 ++++---- .../action/runner/LocalRunnerClientTest.java | 34 +++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java b/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java index 99888228..142f90a0 100644 --- a/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java +++ b/Partner-Main/src/main/java/work/slhaf/partner/core/action/runner/LocalRunnerClient.java @@ -1269,13 +1269,13 @@ public class LocalRunnerClient extends RunnerClient { private void checkAndReload(boolean trustCache) { /* - for each file cannot present all mcp configurations, - we need to load all at once, and then compare them with existed records. - we will record existing mcp paramsCacheMap and id-params map for which is changed. + for each file cannot present all mcp configurations, + we need to load all at once, and then compare them with existed records. + we will record existing mcp paramsCacheMap and id-params map for which is changed. - recording changedMap only cannot figure out which mcp was deleted, - so existingMcpIdSet attr is required - */ + recording changedMap only cannot figure out which mcp was deleted, + so existingMcpIdSet attr is required + */ val changedMap = new HashMap(); val existingMcpIdSet = new HashSet(); @@ -1349,6 +1349,8 @@ public class LocalRunnerClient extends RunnerClient { // new mcp clients and outdated clients has been updated in above logic // this part focus on removing non-existing mcp mcpClients.keySet().removeIf(id -> !existingMcpIdSet.contains(id)); + // clear relevant tools' action info + existedMetaActions.keySet().removeIf(id -> !existingMcpIdSet.contains(id.split("::")[0])); } private boolean fileChanged(File file, McpConfigFileRecord fileRecord) { diff --git a/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java b/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java index 7f12551e..f502f296 100644 --- a/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java +++ b/Partner-Main/src/test/java/work/slhaf/partner/core/action/runner/LocalRunnerClientTest.java @@ -701,6 +701,40 @@ public class LocalRunnerClientTest { } } + @Test + void testCommonMcpRemoveEntryFromConfig(@TempDir Path tempDir) throws IOException, InterruptedException { + ConcurrentHashMap existedMetaActions = new ConcurrentHashMap<>(); + ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); + + try { + Path mcpDir = tempDir.resolve("action").resolve("mcp"); + Files.createDirectories(mcpDir); + Path configFile = mcpDir.resolve("servers.json"); + + String config = buildCommonMcpConfig( + buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest"), + buildStdioServerEntry("playwright", "@playwright/mcp@latest") + ); + writeCommonMcpConfig(configFile, config); + waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")), 20000); + waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("playwright::")), 20000); + Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::"))); + Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("playwright::"))); + + String updatedConfig = buildCommonMcpConfig( + buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest") + ); + writeCommonMcpConfig(configFile, updatedConfig); + + waitForCondition(() -> !hasActionKey(existedMetaActions, key -> key.startsWith("playwright::")), 20000); + Assertions.assertFalse(hasActionKey(existedMetaActions, key -> key.startsWith("playwright::"))); + Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::"))); + } finally { + executor.shutdownNow(); + } + } + @Test void testCommonMcpInvalidJsonRecovery(@TempDir Path tempDir) throws IOException, InterruptedException { ConcurrentHashMap existedMetaActions = new ConcurrentHashMap<>();