mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-27 09:33:55 +08:00
update:智能体获取mcp接入点工具列表
This commit is contained in:
@@ -0,0 +1,32 @@
|
|||||||
|
package xiaozhi.modules.agent.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP JSON-RPC 请求 DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class McpJsonRpcRequest {
|
||||||
|
private String jsonrpc = "2.0";
|
||||||
|
private String method;
|
||||||
|
private Object params;
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
public McpJsonRpcRequest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public McpJsonRpcRequest(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public McpJsonRpcRequest(String method, Object params, Integer id) {
|
||||||
|
this.method = method;
|
||||||
|
this.params = params;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public McpJsonRpcRequest(String method, Object params) {
|
||||||
|
this.method = method;
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package xiaozhi.modules.agent.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP JSON-RPC 响应 DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class McpJsonRpcResponse {
|
||||||
|
private String jsonrpc = "2.0";
|
||||||
|
private Integer id;
|
||||||
|
private McpResult result;
|
||||||
|
private McpError error;
|
||||||
|
|
||||||
|
public McpJsonRpcResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class McpResult {
|
||||||
|
private String type;
|
||||||
|
private String message;
|
||||||
|
private String agent_id;
|
||||||
|
private McpTool[] tools;
|
||||||
|
|
||||||
|
public McpResult() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class McpTool {
|
||||||
|
private String name;
|
||||||
|
private String description;
|
||||||
|
private Object inputSchema;
|
||||||
|
|
||||||
|
public McpTool() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class McpError {
|
||||||
|
private Integer code;
|
||||||
|
private String message;
|
||||||
|
private Object data;
|
||||||
|
|
||||||
|
public McpError() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+70
-2
@@ -2,7 +2,11 @@ package xiaozhi.modules.agent.service.impl;
|
|||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -12,8 +16,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import xiaozhi.common.constant.Constant;
|
import xiaozhi.common.constant.Constant;
|
||||||
import xiaozhi.common.utils.AESUtils;
|
import xiaozhi.common.utils.AESUtils;
|
||||||
import xiaozhi.common.utils.HashEncryptionUtil;
|
import xiaozhi.common.utils.HashEncryptionUtil;
|
||||||
|
import xiaozhi.common.utils.JsonUtils;
|
||||||
|
import xiaozhi.modules.agent.dto.McpJsonRpcRequest;
|
||||||
|
import xiaozhi.modules.agent.dto.McpJsonRpcResponse;
|
||||||
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
|
import xiaozhi.modules.agent.service.AgentMcpAccessPointService;
|
||||||
import xiaozhi.modules.sys.service.SysParamsService;
|
import xiaozhi.modules.sys.service.SysParamsService;
|
||||||
|
import xiaozhi.modules.sys.utils.WebSocketClientManager;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Service
|
@Service
|
||||||
@@ -35,8 +43,10 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
String key = getSecretKey(uri);
|
String key = getSecretKey(uri);
|
||||||
// 获取加密的token
|
// 获取加密的token
|
||||||
String encryptToken = encryptToken(id, key);
|
String encryptToken = encryptToken(id, key);
|
||||||
|
// 对token进行URL编码
|
||||||
|
String encodedToken = URLEncoder.encode(encryptToken, StandardCharsets.UTF_8);
|
||||||
// 返回智能体Mcp路径的格式
|
// 返回智能体Mcp路径的格式
|
||||||
agentMcpUrl = "%s/mcp?token=%s".formatted(agentMcpUrl, encryptToken);
|
agentMcpUrl = "%s/mcp/?token=%s".formatted(agentMcpUrl, encodedToken);
|
||||||
return agentMcpUrl;
|
return agentMcpUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +56,65 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
|
|||||||
if (StringUtils.isBlank(wsUrl)) {
|
if (StringUtils.isBlank(wsUrl)) {
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
return List.of();
|
|
||||||
|
// 将 /mcp 替换为 /call
|
||||||
|
wsUrl = wsUrl.replace("/mcp/", "/call/");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建 WebSocket 连接
|
||||||
|
try (WebSocketClientManager client = WebSocketClientManager.build(
|
||||||
|
new WebSocketClientManager.Builder()
|
||||||
|
.uri(wsUrl)
|
||||||
|
.connectTimeout(5, TimeUnit.SECONDS)
|
||||||
|
.maxSessionDuration(20, TimeUnit.SECONDS))) {
|
||||||
|
|
||||||
|
// 发送初始化通知
|
||||||
|
McpJsonRpcRequest initRequest = new McpJsonRpcRequest("notifications/initialized");
|
||||||
|
client.sendJson(initRequest);
|
||||||
|
|
||||||
|
// 等待 0.2 秒
|
||||||
|
Thread.sleep(200);
|
||||||
|
|
||||||
|
// 发送工具列表请求
|
||||||
|
McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 1);
|
||||||
|
client.sendJson(toolsRequest);
|
||||||
|
|
||||||
|
// 监听响应,直到收到包含 id=1 的响应
|
||||||
|
List<String> responses = client.listener(response -> {
|
||||||
|
try {
|
||||||
|
McpJsonRpcResponse jsonResponse = JsonUtils.parseObject(response, McpJsonRpcResponse.class);
|
||||||
|
return jsonResponse != null && Integer.valueOf(1).equals(jsonResponse.getId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("解析响应失败: {}", response, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理响应
|
||||||
|
for (String response : responses) {
|
||||||
|
try {
|
||||||
|
McpJsonRpcResponse jsonResponse = JsonUtils.parseObject(response, McpJsonRpcResponse.class);
|
||||||
|
if (jsonResponse != null && Integer.valueOf(1).equals(jsonResponse.getId())
|
||||||
|
&& jsonResponse.getResult() != null && jsonResponse.getResult().getTools() != null) {
|
||||||
|
|
||||||
|
// 提取工具名称列表
|
||||||
|
return java.util.Arrays.stream(jsonResponse.getResult().getTools())
|
||||||
|
.map(McpJsonRpcResponse.McpTool::getName)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("处理工具列表响应失败: {}", response, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn("未找到有效的工具列表响应");
|
||||||
|
return List.of();
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取智能体 MCP 工具列表失败,智能体ID: {}", id, e);
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user