feat: 对接 Agent MCP 能力
- 新增 runtime MCP 声明、ClientFactory、Toolkit 适配与工具别名映射 - 增加 MCP 环境检测与 stdio 环境变量透传 - 补齐 MCP 工具事件、审批与生命周期释放测试
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
package com.easyagents.mcp.client;
|
||||
|
||||
import io.modelcontextprotocol.json.TypeRef;
|
||||
import io.modelcontextprotocol.spec.McpClientTransport;
|
||||
import io.modelcontextprotocol.spec.McpSchema;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* MCP 环境检测测试。
|
||||
*/
|
||||
public class McpEnvironmentCheckerTest {
|
||||
|
||||
@Test
|
||||
public void checkValidStdioConfigWithoutProbe() {
|
||||
String json = """
|
||||
{
|
||||
"mcpServers": {
|
||||
"test": {
|
||||
"transport": "stdio",
|
||||
"command": "java",
|
||||
"args": ["-version"]
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
McpEnvironmentCheckResult result = new McpEnvironmentChecker(false).check(json);
|
||||
|
||||
assertEquals(McpCheckStatus.SUCCESS, result.getOverallStatus());
|
||||
assertEquals("test", result.getServers().get(0).getServerName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkMissingCommand() {
|
||||
String json = """
|
||||
{
|
||||
"mcpServers": {
|
||||
"test": {
|
||||
"transport": "stdio"
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
McpEnvironmentCheckResult result = new McpEnvironmentChecker(false).check(json);
|
||||
|
||||
assertEquals(McpCheckStatus.FAILED, result.getOverallStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkMissingHttpUrl() {
|
||||
String json = """
|
||||
{
|
||||
"mcpServers": {
|
||||
"test": {
|
||||
"transport": "http-sse"
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
McpEnvironmentCheckResult result = new McpEnvironmentChecker(false).check(json);
|
||||
|
||||
assertEquals(McpCheckStatus.FAILED, result.getOverallStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkUnresolvedInputEnv() {
|
||||
String json = """
|
||||
{
|
||||
"mcpServers": {
|
||||
"test": {
|
||||
"transport": "stdio",
|
||||
"command": "java",
|
||||
"env": {
|
||||
"API_KEY": "${input:api_key}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
McpEnvironmentCheckResult result = new McpEnvironmentChecker(false).check(json);
|
||||
|
||||
assertEquals(McpCheckStatus.FAILED, result.getOverallStatus());
|
||||
assertFalse(result.getServers().get(0).getChecks().toString().contains("secret"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkInvalidJson() {
|
||||
McpEnvironmentCheckResult result = new McpEnvironmentChecker(false).check("{ invalid json }");
|
||||
|
||||
assertEquals(McpCheckStatus.FAILED, result.getOverallStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closeTransportWhenProbeFailed() {
|
||||
TrackingCloseableTransport closeableTransport = new TrackingCloseableTransport();
|
||||
McpEnvironmentChecker checker = new McpEnvironmentChecker(true, transport -> (spec, resolvedEnv) -> closeableTransport);
|
||||
String json = """
|
||||
{
|
||||
"mcpServers": {
|
||||
"test": {
|
||||
"transport": "stdio",
|
||||
"command": "java"
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
McpEnvironmentCheckResult result = checker.check(json);
|
||||
|
||||
assertEquals(McpCheckStatus.FAILED, result.getOverallStatus());
|
||||
assertTrue(closeableTransport.closed);
|
||||
}
|
||||
|
||||
private static class TrackingCloseableTransport implements CloseableTransport {
|
||||
private boolean closed;
|
||||
|
||||
@Override
|
||||
public McpClientTransport getTransport() {
|
||||
return new FailingClientTransport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class FailingClientTransport implements McpClientTransport {
|
||||
@Override
|
||||
public Mono<Void> connect(java.util.function.Function<Mono<McpSchema.JSONRPCMessage>,
|
||||
Mono<McpSchema.JSONRPCMessage>> handler) {
|
||||
return Mono.error(new IllegalStateException("probe failed"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> closeGracefully() {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> sendMessage(McpSchema.JSONRPCMessage message) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T unmarshalFrom(Object data, TypeRef<T> typeRef) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.easyagents.mcp.client;
|
||||
|
||||
import io.modelcontextprotocol.client.transport.ServerParameters;
|
||||
import io.modelcontextprotocol.client.transport.StdioClientTransport;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Stdio MCP transport factory tests.
|
||||
*/
|
||||
public class StdioTransportFactoryTest {
|
||||
|
||||
@Test
|
||||
public void createWithResolvedEnv() throws Exception {
|
||||
McpConfig.ServerSpec spec = new McpConfig.ServerSpec();
|
||||
spec.setCommand("npx");
|
||||
spec.setArgs(List.of("-y", "test-mcp-server"));
|
||||
|
||||
CloseableTransport closeableTransport = new StdioTransportFactory()
|
||||
.create(spec, Map.of("API_KEY", "resolved-secret"));
|
||||
|
||||
assertTrue(closeableTransport.getTransport() instanceof StdioClientTransport);
|
||||
StdioClientTransport transport = (StdioClientTransport) closeableTransport.getTransport();
|
||||
ServerParameters parameters = extractParameters(transport);
|
||||
|
||||
assertEquals("npx", parameters.getCommand());
|
||||
assertEquals(List.of("-y", "test-mcp-server"), parameters.getArgs());
|
||||
assertEquals("resolved-secret", parameters.getEnv().get("API_KEY"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWithNullArgsAndEnv() throws Exception {
|
||||
McpConfig.ServerSpec spec = new McpConfig.ServerSpec();
|
||||
spec.setCommand("python");
|
||||
spec.setArgs(null);
|
||||
|
||||
CloseableTransport closeableTransport = new StdioTransportFactory().create(spec, null);
|
||||
|
||||
assertTrue(closeableTransport.getTransport() instanceof StdioClientTransport);
|
||||
StdioClientTransport transport = (StdioClientTransport) closeableTransport.getTransport();
|
||||
ServerParameters parameters = extractParameters(transport);
|
||||
|
||||
assertEquals("python", parameters.getCommand());
|
||||
assertEquals(List.of(), parameters.getArgs());
|
||||
}
|
||||
|
||||
private ServerParameters extractParameters(StdioClientTransport transport) throws Exception {
|
||||
Field paramsField = StdioClientTransport.class.getDeclaredField("params");
|
||||
paramsField.setAccessible(true);
|
||||
return (ServerParameters) paramsField.get(transport);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user