diff --git a/docs/mcp-endpoint-integration.md b/docs/mcp-endpoint-integration.md new file mode 100644 index 00000000..ca72aef1 --- /dev/null +++ b/docs/mcp-endpoint-integration.md @@ -0,0 +1,134 @@ +# MCP 接入点部署使用指南 + +本教程包含2个部分 +- 1、如何开启mcp接入点 +- 2、如何为智能体接入一个简单的mcp功能,如计算器功能 + +部署的前提条件: +- 1、你已经部署了全模块,因为mcp接入点需要全模块中的智控台功能 +- 2、你想在不修改xiaozhi-server项目的前提下,扩展小智的功能 + +# 如何开启mcp接入点 + +## 第一步,下载mcp接入点项目源码 + +浏览器打开[mcp接入点项目地址](https://github.com/xinnan-tech/mcp-endpoint-server) + +打开完,找到页面中一个绿色的按钮,写着`Code`的按钮,点开它,然后你就看到`Download ZIP`的按钮。 + +点击它,下载本项目源码压缩包。下载到你电脑后,解压它,此时它的名字可能叫`mcp-endpoint-server-main` +你需要把它重命名成`mcp-endpoint-server`。 + +## 第二步,启动程序 +这个项目是一个很简单的项目,建议使用docker运行。不过如果你不想使用docker运行,你可以参考[这个页面](https://github.com/xinnan-tech/mcp-endpoint-server/blob/main/README_dev.md)使用源码运行。以下是docker运行的方法 + +``` +# 进入本项目源码根目录 +cd mcp-endpoint-server + +# 清除缓存 +docker compose -f docker-compose.yml down +docker stop mcp-endpoint-server +docker rm mcp-endpoint-server +docker rmi ghcr.nju.edu.cn/xinnan-tech/mcp-endpoint-server:latest + +# 启动docker容器 +docker compose -f docker-compose.yml up -d +# 查看日志 +docker logs -f mcp-endpoint-server +``` + +此时,日志里会输出类似以下的日志 +``` +====================================================== +接口地址: http://172.1.1.1:8004/mcp_endpoint/health?key=xxxx +=======上面的地址是MCP接入点地址,请勿泄露给任何人============ +``` + +请你把接口地址复制出来: + +由于你是docker部署,切不可直接使用上面的地址! + +由于你是docker部署,切不可直接使用上面的地址! + +由于你是docker部署,切不可直接使用上面的地址! + +你先把地址复制出来,放在一个草稿里,你要知道你的电脑的局域网ip是什么,例如我的电脑局域网ip是`192.168.1.25`,那么 +原来我的接口地址 +``` +http://172.1.1.1:8004/mcp_endpoint/health?key=xxxx +``` +就要改成 +``` +http://192.168.1.25:8004/mcp_endpoint/health?key=xxxx +``` + +改好后,请使用浏览器直接访问这个接口。当浏览器出现类似这样的代码,说明是成功了。 +``` +{"result":{"status":"success","connections":{"tool_connections":0,"robot_connections":0,"total_connections":0}},"error":null,"id":null,"jsonrpc":"2.0"} +``` + +请你保留好这个`接口地址`,下一步要用到。 + +## 第三步,配置智控台 + +使用管理员账号,登录智控台,点击顶部`参数字典`,选择`参数管理`功能。 + +然后搜索参数`server.mcp_endpoint`,此时,它的值应该是`null`值。 +点击修改按钮,把上一步得来的`接口地址`粘贴到`参数值`里。然后保存。 + +如果能保存成功,说明一切顺利,你可以去智能体查看效果了。如果不成功,说明智控台无法访问mcp接入点,很大概率是网络防火墙,或者没有填写正确的局域网ip。 + +# 如何为智能体接入一个简单的mcp功能,如计算器功能 + +如果以上步骤顺利,你可以进入智能体管理,点击`配置角色`,在`意图识别`的右边,有一个`编辑功能`的按钮。 + +点击这个按钮。在弹出的页面里,位于底部,会有`MCP接入点`,正常来说,会显示这个智能体的`MCP接入点地址`,接下来,我们来给这个智能体扩展一个基于MCP技术的计算器的功能。 + +这个`MCP接入点地址`很重要,你等一下会用到。 + +## 第一步 下载虾哥MCP计算器项目代码 + +浏览器打开虾哥写的[计算器项目](https://github.com/78/mcp-calculator), + +打开完,找到页面中一个绿色的按钮,写着`Code`的按钮,点开它,然后你就看到`Download ZIP`的按钮。 + +点击它,下载本项目源码压缩包。下载到你电脑后,解压它,此时它的名字可能叫`mcp-calculatorr-main` +你需要把它重命名成`mcp-calculator`。接下来,我们用命令行进入项目目录即安装依赖 + + +```bash +# 进入项目目录 +cd mcp-calculator + +conda remove -n mcp-calculator --all -y +conda create -n mcp-calculator python=3.10 -y +conda activate mcp-calculator + +pip install -r requirements.txt +``` + +## 第二步 启动 + +启动前,先从你的智控台的智能体里,复制到了MCP接入点的地址。 + +例如我的智能体的mcp地址是 +``` +ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc +``` + +开始输入命令 + +```bash +export MCP_ENDPOINT=ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc +``` + +输入完后,启动程序 + +```bash +python mcp_pipe.py calculator.py +``` + + +启动完后,你再进入智控台,点击刷新MCP的接入状态,就会看到你扩展的功能列表了。 + diff --git a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java index b5837eb5..32a121fa 100644 --- a/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java +++ b/main/manager-api/src/main/java/xiaozhi/common/constant/Constant.java @@ -111,6 +111,11 @@ public interface Constant { */ String FILE_EXTENSION_SEG = "."; + /** + * mcp接入点路径 + */ + String SERVER_MCP_ENDPOINT = "server.mcp_endpoint"; + /** * 无记忆 */ @@ -227,7 +232,7 @@ public interface Constant { /** * 版本号 */ - public static final String VERSION = "0.5.8"; + public static final String VERSION = "0.6.1"; /** * 无效固件URL diff --git a/main/manager-api/src/main/java/xiaozhi/common/utils/HashEncryptionUtil.java b/main/manager-api/src/main/java/xiaozhi/common/utils/HashEncryptionUtil.java new file mode 100644 index 00000000..79b05192 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/common/utils/HashEncryptionUtil.java @@ -0,0 +1,52 @@ +package xiaozhi.common.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * 哈希加密算法的工具类 + * @author zjy + */ +@Slf4j +public class HashEncryptionUtil { + /** + * 使用md5进行加密 + * @param context 被加密的内容 + * @return 哈希值 + */ + public static String Md5hexDigest(String context){ + return hexDigest(context,"MD5"); + } + + /** + * 指定哈希算法进行加密 + * @param context 被加密的内容 + * @param algorithm 哈希算法 + * @return 哈希值 + */ + public static String hexDigest(String context,String algorithm ){ + // 获取MD5算法实例 + MessageDigest md = null; + try { + md = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + log.error("加密失败的算法:{}",algorithm); + throw new RuntimeException("加密失败,"+ algorithm +"哈希算法系统不支持"); + } + // 计算智能体id的MD5值 + byte[] messageDigest = md.digest(context.getBytes()); + // 将字节数组转换为十六进制字符串 + StringBuilder hexString = new StringBuilder(); + for (byte b : messageDigest) { + String hex = Integer.toHexString(0xFF & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + +} diff --git a/main/manager-api/src/main/java/xiaozhi/common/utils/HttpSendUtils.java b/main/manager-api/src/main/java/xiaozhi/common/utils/HttpSendUtils.java new file mode 100644 index 00000000..0e3429e7 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/common/utils/HttpSendUtils.java @@ -0,0 +1,64 @@ +package xiaozhi.common.utils; + +import lombok.extern.slf4j.Slf4j; +import okhttp3.*; +import org.jetbrains.annotations.Nullable; +import org.springframework.stereotype.Component; +import xiaozhi.common.exception.RenException; + +import java.io.IOException; + +@Slf4j +@Component +public class HttpSendUtils { + private OkHttpClient client = new OkHttpClient(); + + /** + * 发送get请求,获取返回的body转换成字符串 + * @param url get请求地址 + * @return body内容 + */ + public String fetchGetBodyAsString(String url) { + Request request = new Request.Builder() + .url(url) + .build(); + return getString(url, request); + } + + /** + * 发送post请求,参数为json格式。取返回的body转换成字符串 + * @param url post请求地址 + * @param json json参数 + * @return body内容 + */ + public String fetchJsonPostBodyAsString(String url,String json) { + // 创建请求体 + MediaType JSON = MediaType.get("application/json; charset=utf-8"); + RequestBody body = RequestBody.create(json, JSON); + + Request request = new Request.Builder() + .url(url) + .post(body) + .build(); + + return getString(url, request); + } + + private String getString(String url, Request request) { + String body = null; + try (Response response = client.newCall(request).execute()) { + if (response.isSuccessful()) { + if (response.body() != null){ + body = response.body().string(); + } + } else { + throw new RenException("请求失败,错误码:"+response.code()); + } + } catch (Exception e) { + String method = request.method(); + log.error("{}请求发送错误地址:{} \n 发送错误信息:{}",method, url,e.getMessage()); + throw new RenException("请求发送失败"); + } + return body; + } +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentMcpAccessPointController.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentMcpAccessPointController.java new file mode 100644 index 00000000..3f369774 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/controller/AgentMcpAccessPointController.java @@ -0,0 +1,66 @@ +package xiaozhi.modules.agent.controller; + +import java.util.List; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import xiaozhi.common.user.UserDetail; +import xiaozhi.common.utils.Result; +import xiaozhi.modules.agent.service.AgentMcpAccessPointService; +import xiaozhi.modules.agent.service.AgentService; +import xiaozhi.modules.security.user.SecurityUser; + +@Tag(name = "智能体Mcp接入点管理") +@RequiredArgsConstructor +@RestController +@RequestMapping("/agent/mcp") +public class AgentMcpAccessPointController { + private final AgentMcpAccessPointService agentMcpAccessPointService; + private final AgentService agentService; + + /** + * 获取智能体的Mcp接入点地址 + * + * @param audioId 智能体id + * @return 返回错误提醒或者Mcp接入点地址 + */ + @Operation(summary = "获取智能体的Mcp接入点地址") + @GetMapping("/address/{agentId}") + @RequiresPermissions("sys:role:normal") + public Result getAgentMcpAccessAddress(@PathVariable("agentId") String agentId) { + // 获取当前用户 + UserDetail user = SecurityUser.getUser(); + + // 检查权限 + if (!agentService.checkAgentPermission(agentId, user.getId())) { + return new Result().error("没有权限查看该智能体的MCP接入点地址"); + } + String agentMcpAccessAddress = agentMcpAccessPointService.getAgentMcpAccessAddress(agentId); + if (agentMcpAccessAddress == null) { + return new Result().ok("请联系管理员进入参数管理配置mcp接入点地址"); + } + return new Result().ok(agentMcpAccessAddress); + } + + @Operation(summary = "获取智能体的Mcp工具列表") + @GetMapping("/tools/{agentId}") + @RequiresPermissions("sys:role:normal") + public Result> getAgentMcpToolsList(@PathVariable("agentId") String agentId) { + // 获取当前用户 + UserDetail user = SecurityUser.getUser(); + + // 检查权限 + if (!agentService.checkAgentPermission(agentId, user.getId())) { + return new Result>().error("没有权限查看该智能体的MCP工具列表"); + } + List agentMcpToolsList = agentMcpAccessPointService.getAgentMcpToolsList(agentId); + return new Result>().ok(agentMcpToolsList); + } +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/McpJsonRpcRequest.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/McpJsonRpcRequest.java new file mode 100644 index 00000000..e4fac002 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/McpJsonRpcRequest.java @@ -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; + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/McpJsonRpcResponse.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/McpJsonRpcResponse.java new file mode 100644 index 00000000..1bd667e6 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/dto/McpJsonRpcResponse.java @@ -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() { + } + } +} \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentMcpAccessPointService.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentMcpAccessPointService.java new file mode 100644 index 00000000..336ade65 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/AgentMcpAccessPointService.java @@ -0,0 +1,25 @@ +package xiaozhi.modules.agent.service; + + +import java.util.List; + +/** + * 智能体Mcp接入点处理service + * + * @author zjy + */ +public interface AgentMcpAccessPointService { + /** + * 获取智能体的mcp接入点地址 + * @param id 智能体id + * @return mcp接入点地址 + */ + String getAgentMcpAccessAddress(String id); + + /** + * 获取智能体的mcp接入点已有的工具列表 + * @param id 智能体id + * @return 工具列表 + */ + List getAgentMcpToolsList(String id); +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentMcpAccessPointServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentMcpAccessPointServiceImpl.java new file mode 100644 index 00000000..e45d2201 --- /dev/null +++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/impl/AgentMcpAccessPointServiceImpl.java @@ -0,0 +1,180 @@ +package xiaozhi.modules.agent.service.impl; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import xiaozhi.common.constant.Constant; +import xiaozhi.common.utils.AESUtils; +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.sys.service.SysParamsService; +import xiaozhi.modules.sys.utils.WebSocketClientManager; + +@AllArgsConstructor +@Service +@Slf4j +public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointService { + private SysParamsService sysParamsService; + + @Override + public String getAgentMcpAccessAddress(String id) { + // 获取到mcp的地址 + String url = sysParamsService.getValue(Constant.SERVER_MCP_ENDPOINT, true); + if (StringUtils.isBlank(url) || "null".equals(url)) { + return null; + } + URI uri = getURI(url); + // 获取智能体mcp的url前缀 + String agentMcpUrl = getAgentMcpUrl(uri); + // 获取密钥 + String key = getSecretKey(uri); + // 获取加密的token + String encryptToken = encryptToken(id, key); + // 对token进行URL编码 + String encodedToken = URLEncoder.encode(encryptToken, StandardCharsets.UTF_8); + // 返回智能体Mcp路径的格式 + agentMcpUrl = "%s/mcp/?token=%s".formatted(agentMcpUrl, encodedToken); + return agentMcpUrl; + } + + @Override + public List getAgentMcpToolsList(String id) { + String wsUrl = getAgentMcpAccessAddress(id); + if (StringUtils.isBlank(wsUrl)) { + 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 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(); + } + } + + /** + * 获取URI对象 + * + * @param url 路径 + * @return URI对象 + */ + private static URI getURI(String url) { + try { + return new URI(url); + } catch (URISyntaxException e) { + log.error("路径格式不正确路径:{},\n错误信息:{}", url, e.getMessage()); + throw new RuntimeException("mcp的地址存在错误,请进入参数管理修改mcp接入点地址"); + } + } + + /** + * 获取密钥 + * + * @param uri mcp地址 + * @return 密钥 + */ + private static String getSecretKey(URI uri) { + // 获取参数 + String query = uri.getQuery(); + // 获取aes加密密钥 + String str = "key="; + return query.substring(query.indexOf(str) + str.length()); + } + + /** + * 获取智能体mcp接入点url + * + * @param uri mcp地址 + * @return 智能体mcp接入点url + */ + private String getAgentMcpUrl(URI uri) { + // 获取协议 + String wsScheme = (uri.getScheme().equals("https")) ? "wss" : "ws"; + // 获取主机,端口,路径 + String path = uri.getSchemeSpecificPart(); + // 获取到最后一个/前的path + path = path.substring(0, path.lastIndexOf("/")); + return wsScheme + ":" + path; + } + + /** + * 获取对智能体id加密的token + * + * @param agentId 智能体id + * @param key 加密密钥 + * @return 加密后token + */ + private static String encryptToken(String agentId, String key) { + // 使用md5对智能体id进行加密 + String md5 = HashEncryptionUtil.Md5hexDigest(agentId); + // aes需要加密文本 + String json = "{\"agentId\": \"%s\"}".formatted(md5); + // 加密后成token值 + return AESUtils.encrypt(key, json); + } +} diff --git a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java index e56cb381..34192dd8 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java @@ -1,6 +1,10 @@ package xiaozhi.modules.config.service.impl; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -15,6 +19,7 @@ import xiaozhi.common.utils.JsonUtils; import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.agent.entity.AgentPluginMapping; import xiaozhi.modules.agent.entity.AgentTemplateEntity; +import xiaozhi.modules.agent.service.AgentMcpAccessPointService; import xiaozhi.modules.agent.service.AgentPluginMappingService; import xiaozhi.modules.agent.service.AgentService; import xiaozhi.modules.agent.service.AgentTemplateService; @@ -39,6 +44,7 @@ public class ConfigServiceImpl implements ConfigService { private final RedisUtils redisUtils; private final TimbreService timbreService; private final AgentPluginMappingService agentPluginMappingService; + private final AgentMcpAccessPointService agentMcpAccessPointService; @Override public Object getConfig(Boolean isCache) { @@ -144,6 +150,12 @@ public class ConfigServiceImpl implements ConfigService { result.put("plugins", pluginParams); } } + // 获取mcp接入点地址 + String mcpEndpoint = agentMcpAccessPointService.getAgentMcpAccessAddress(agent.getId()); + if (StringUtils.isNotBlank(mcpEndpoint) && mcpEndpoint.startsWith("ws")) { + mcpEndpoint = mcpEndpoint.replace("/mcp/", "/call/"); + result.put("mcp_endpoint", mcpEndpoint); + } // 构建模块配置 buildModuleConfig( diff --git a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java index 02f595d2..17dca161 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/sys/service/impl/SysParamsServiceImpl.java @@ -20,6 +20,7 @@ import xiaozhi.common.exception.RenException; import xiaozhi.common.page.PageData; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.common.utils.ConvertUtils; +import xiaozhi.common.utils.HttpSendUtils; import xiaozhi.common.utils.JsonUtils; import xiaozhi.modules.sys.dao.SysParamsDao; import xiaozhi.modules.sys.dto.SysParamsDTO; @@ -34,6 +35,7 @@ import xiaozhi.modules.sys.service.SysParamsService; @Service public class SysParamsServiceImpl extends BaseServiceImpl implements SysParamsService { private final SysParamsRedis sysParamsRedis; + private final HttpSendUtils httpSendUtils; @Override public PageData page(Map params) { @@ -86,6 +88,7 @@ public class SysParamsServiceImpl extends BaseServiceImpl { + RequestService.clearRequestTime(); + callback(res); + }) + .networkFail(() => { + RequestService.reAjaxFun(() => { + this.getAgentMcpAccessAddress(agentId, callback); + }); + }).send(); + }, + // 获取智能体的MCP工具列表 + getAgentMcpToolsList(agentId, callback) { + RequestService.sendRequest() + .url(`${getServiceUrl()}/agent/mcp/tools/${agentId}`) + .method('GET') + .success((res) => { + RequestService.clearRequestTime(); + callback(res); + }) + .networkFail(() => { + RequestService.reAjaxFun(() => { + this.getAgentMcpToolsList(agentId, callback); + }); + }).send(); + }, } diff --git a/main/manager-web/src/components/FunctionDialog.vue b/main/manager-web/src/components/FunctionDialog.vue index f001ad11..6f637490 100644 --- a/main/manager-web/src/components/FunctionDialog.vue +++ b/main/manager-web/src/components/FunctionDialog.vue @@ -101,39 +101,54 @@
-
-

MCP接入点

-
-
-
-

接入点地址

-
- 以下是智能体的MCP接入点地址。 - 查看接入点使用文档 +
+
+

MCP接入点

+
+
+ 以下是智能体的MCP接入点地址。 + 查看接入点使用文档 +
+
+ + +
- - - -
-

接入点状态

+
+

接入点状态

+
- {{ mcpStatus === 'connected' ? '已连接' : '未连接' }} + {{ + mcpStatus === 'connected' ? '已连接' : + mcpStatus === 'loading' ? '加载中...' : '未连接' + }}
+
+
+ + {{ tool }} + +
+
+ 暂无可用工具 +
+
@@ -146,6 +161,8 @@