refactor(mcp): switch desc.json loading to fastjson2 to avoid desc.json loading error

This commit is contained in:
2026-04-01 22:57:17 +08:00
parent 4ae65b885e
commit b9fd9bcaac
3 changed files with 44 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
package work.slhaf.partner.core.action.runner.mcp; package work.slhaf.partner.core.action.runner.mcp;
import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.McpJsonMapper;
@@ -241,7 +241,7 @@ public class DynamicActionMcpManager implements AutoCloseable {
} }
MetaActionInfo info; MetaActionInfo info;
try { try {
info = JSONUtil.readJSONObject(dir.resolve("desc.json").toFile(), StandardCharsets.UTF_8).toBean(MetaActionInfo.class); info = JSON.parseObject(Files.readString(dir.resolve("desc.json"), StandardCharsets.UTF_8), MetaActionInfo.class);
} catch (Exception e) { } catch (Exception e) {
log.error("desc.json 加载失败: {}", dir); log.error("desc.json 加载失败: {}", dir);
return false; return false;

View File

@@ -1,6 +1,6 @@
package work.slhaf.partner.core.action.runner.mcp; package work.slhaf.partner.core.action.runner.mcp;
import cn.hutool.json.JSONUtil; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.json.McpJsonMapper; import io.modelcontextprotocol.json.McpJsonMapper;
@@ -70,7 +70,7 @@ public class McpMetaRegistry implements AutoCloseable {
return false; return false;
} }
try { try {
MetaActionInfo info = JSONUtil.readJSONObject(file, StandardCharsets.UTF_8).toBean(MetaActionInfo.class); MetaActionInfo info = JSON.parseObject(Files.readString(file.toPath(), StandardCharsets.UTF_8), MetaActionInfo.class);
String uri = file.toPath().toUri().toString(); String uri = file.toPath().toUri().toString();
descCache.put(uri, JSONObject.toJSONString(info)); descCache.put(uri, JSONObject.toJSONString(info));
String actionKey = name.replace(".desc.json", ""); String actionKey = name.replace(".desc.json", "");

View File

@@ -183,11 +183,34 @@ public class LocalRunnerClientTest {
+ " }"; + " }";
} }
static String buildStdioServerEntry(String id, String packageName, Path npmCacheDir) {
String cachePath = npmCacheDir.toAbsolutePath().toString().replace("\\", "\\\\");
return " \"" + id + "\": {\n"
+ " \"command\": \"npx\",\n"
+ " \"args\": [\n"
+ " \"-y\",\n"
+ " \"" + packageName + "\"\n"
+ " ],\n"
+ " \"env\": {\n"
+ " \"NPM_CONFIG_CACHE\": \"" + cachePath + "\",\n"
+ " \"npm_config_cache\": \"" + cachePath + "\"\n"
+ " }\n"
+ " }";
}
static MetaAction buildMetaAction(MetaAction.Type type, String location, String name, Map<String, Object> params) { static MetaAction buildMetaAction(MetaAction.Type type, String location, String name, Map<String, Object> params) {
return buildMetaAction(type, location, name, null, params);
}
static MetaAction buildMetaAction(MetaAction.Type type,
String location,
String name,
String launcher,
Map<String, Object> params) {
MetaAction metaAction = new MetaAction( MetaAction metaAction = new MetaAction(
name, name,
false, false,
null, launcher,
type, type,
location location
); );
@@ -698,12 +721,13 @@ public class LocalRunnerClientTest {
void testCommonMcpInitialLoad(@TempDir Path tempDir) throws IOException, InterruptedException { void testCommonMcpInitialLoad(@TempDir Path tempDir) throws IOException, InterruptedException {
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>(); ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
Path npmCacheDir = tempDir.resolve("npm-cache");
Path mcpDir = tempDir.resolve("action").resolve("mcp"); Path mcpDir = tempDir.resolve("action").resolve("mcp");
Files.createDirectories(mcpDir); Files.createDirectories(mcpDir);
Path configFile = mcpDir.resolve("servers.json"); Path configFile = mcpDir.resolve("servers.json");
String config = buildCommonMcpConfig( String config = buildCommonMcpConfig(
buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest") buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest", npmCacheDir)
); );
writeCommonMcpConfig(configFile, config); writeCommonMcpConfig(configFile, config);
@@ -722,6 +746,7 @@ public class LocalRunnerClientTest {
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>(); ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); new LocalRunnerClient(existedMetaActions, executor, tempDir.toString());
Path npmCacheDir = tempDir.resolve("npm-cache");
try { try {
Path mcpDir = tempDir.resolve("action").resolve("mcp"); Path mcpDir = tempDir.resolve("action").resolve("mcp");
@@ -729,15 +754,15 @@ public class LocalRunnerClientTest {
Path configFile = mcpDir.resolve("servers.json"); Path configFile = mcpDir.resolve("servers.json");
String config = buildCommonMcpConfig( String config = buildCommonMcpConfig(
buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest") buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest", npmCacheDir)
); );
writeCommonMcpConfig(configFile, config); writeCommonMcpConfig(configFile, config);
waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")), 20000); waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")), 20000);
Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::"))); Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")));
String updatedConfig = buildCommonMcpConfig( String updatedConfig = buildCommonMcpConfig(
buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest"), buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest", npmCacheDir),
buildStdioServerEntry("playwright", "@playwright/mcp@latest") buildStdioServerEntry("playwright", "@playwright/mcp@latest", npmCacheDir)
); );
writeCommonMcpConfig(configFile, updatedConfig); writeCommonMcpConfig(configFile, updatedConfig);
waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("playwright::")), 20000); waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("playwright::")), 20000);
@@ -757,6 +782,7 @@ public class LocalRunnerClientTest {
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>(); ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); new LocalRunnerClient(existedMetaActions, executor, tempDir.toString());
Path npmCacheDir = tempDir.resolve("npm-cache");
try { try {
Path mcpDir = tempDir.resolve("action").resolve("mcp"); Path mcpDir = tempDir.resolve("action").resolve("mcp");
@@ -764,8 +790,8 @@ public class LocalRunnerClientTest {
Path configFile = mcpDir.resolve("servers.json"); Path configFile = mcpDir.resolve("servers.json");
String config = buildCommonMcpConfig( String config = buildCommonMcpConfig(
buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest"), buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest", npmCacheDir),
buildStdioServerEntry("playwright", "@playwright/mcp@latest") buildStdioServerEntry("playwright", "@playwright/mcp@latest", npmCacheDir)
); );
writeCommonMcpConfig(configFile, config); writeCommonMcpConfig(configFile, config);
waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")), 20000); waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")), 20000);
@@ -774,7 +800,7 @@ public class LocalRunnerClientTest {
Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("playwright::"))); Assertions.assertTrue(hasActionKey(existedMetaActions, key -> key.startsWith("playwright::")));
String updatedConfig = buildCommonMcpConfig( String updatedConfig = buildCommonMcpConfig(
buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest") buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest", npmCacheDir)
); );
writeCommonMcpConfig(configFile, updatedConfig); writeCommonMcpConfig(configFile, updatedConfig);
@@ -791,6 +817,7 @@ public class LocalRunnerClientTest {
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>(); ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
new LocalRunnerClient(existedMetaActions, executor, tempDir.toString()); new LocalRunnerClient(existedMetaActions, executor, tempDir.toString());
Path npmCacheDir = tempDir.resolve("npm-cache");
try { try {
Path mcpDir = tempDir.resolve("action").resolve("mcp"); Path mcpDir = tempDir.resolve("action").resolve("mcp");
@@ -802,7 +829,7 @@ public class LocalRunnerClientTest {
Assertions.assertFalse(hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::"))); Assertions.assertFalse(hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")));
String config = buildCommonMcpConfig( String config = buildCommonMcpConfig(
buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest") buildStdioServerEntry("mcp-deepwiki", "mcp-deepwiki@latest", npmCacheDir)
); );
writeCommonMcpConfig(configFile, config); writeCommonMcpConfig(configFile, config);
waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")), 20000); waitForCondition(() -> hasActionKey(existedMetaActions, key -> key.startsWith("mcp-deepwiki::")), 20000);
@@ -830,7 +857,7 @@ public class LocalRunnerClientTest {
RunnerClient.RunnerResponse response = client.doRun(metaAction); RunnerClient.RunnerResponse response = client.doRun(metaAction);
Assertions.assertNotNull(response); Assertions.assertNotNull(response);
Assertions.assertFalse(response.isOk()); Assertions.assertFalse(response.isOk());
Assertions.assertEquals("未知文件类型", response.getData()); Assertions.assertTrue(response.getData().contains("parameter command"));
} finally { } finally {
executor.shutdownNow(); executor.shutdownNow();
} }
@@ -845,7 +872,7 @@ public class LocalRunnerClientTest {
try { try {
Path script = tempDir.resolve("run.sh"); Path script = tempDir.resolve("run.sh");
Files.writeString(script, "echo ok\n"); Files.writeString(script, "echo ok\n");
MetaAction metaAction = buildMetaAction(MetaAction.Type.ORIGIN, script.toString(), "run", Map.of()); MetaAction metaAction = buildMetaAction(MetaAction.Type.ORIGIN, script.toString(), "run", "sh", Map.of());
RunnerClient.RunnerResponse response = client.doRun(metaAction); RunnerClient.RunnerResponse response = client.doRun(metaAction);
Assertions.assertNotNull(response); Assertions.assertNotNull(response);
Assertions.assertTrue(response.isOk()); Assertions.assertTrue(response.isOk());
@@ -927,12 +954,13 @@ public class LocalRunnerClientTest {
void testDoRunWithMcpLoadedFromCommonConfig(@TempDir Path tempDir) throws IOException, InterruptedException { void testDoRunWithMcpLoadedFromCommonConfig(@TempDir Path tempDir) throws IOException, InterruptedException {
ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>(); ConcurrentHashMap<String, MetaActionInfo> existedMetaActions = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
Path npmCacheDir = tempDir.resolve("npm-cache");
Path mcpDir = tempDir.resolve("action").resolve("mcp"); Path mcpDir = tempDir.resolve("action").resolve("mcp");
Files.createDirectories(mcpDir); Files.createDirectories(mcpDir);
Path configFile = mcpDir.resolve("servers.json"); Path configFile = mcpDir.resolve("servers.json");
String config = buildCommonMcpConfig( String config = buildCommonMcpConfig(
buildStdioServerEntry("playwright", "@playwright/mcp@latest") buildStdioServerEntry("playwright", "@playwright/mcp@latest", npmCacheDir)
); );
writeCommonMcpConfig(configFile, config); writeCommonMcpConfig(configFile, config);