diff --git a/.gitignore b/.gitignore
index 9469baac..b43a8de3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -174,3 +174,4 @@ main/xiaozhi-server/mysql
uploadfile
*.json
.vscode
+.cursor
diff --git a/README.md b/README.md
index 7a150e91..b018d5b6 100644
--- a/README.md
+++ b/README.md
@@ -144,6 +144,11 @@
+
+
+
+
+
|
|
@@ -170,8 +175,8 @@
#### 🚀 部署方式选择
| 部署方式 | 特点 | 适用场景 | 部署文档 | 配置要求 | 视频教程 |
|---------|------|---------|---------|---------|---------|
-| **最简化安装** | 智能对话、IOT、MCP、视觉感知,数据存储在配置文件 | 低配置环境,无需数据库 | [①Docker版](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②源码部署](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 如果使用`FunASR`要2核4G,如果全API,要2核2G | - |
-| **全模块安装** | 智能对话、IOT、MCP、视觉感知、OTA、智控台,数据存储在数据库 | 完整功能体验 |[①Docker版](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [②源码部署](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③源码部署自动更新教程](./docs/dev-ops-integration.md) | 如果使用`FunASR`要4核8G,如果全API,要2核4G| [本地源码启动视频教程](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
+| **最简化安装** | 智能对话、IOT、MCP、视觉感知 | 低配置环境,数据存储在配置文件,无需数据库 | [①Docker版](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E5%8F%AA%E8%BF%90%E8%A1%8Cserver) / [②源码部署](./docs/Deployment.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E5%8F%AA%E8%BF%90%E8%A1%8Cserver)| 如果使用`FunASR`要2核4G,如果全API,要2核2G | - |
+| **全模块安装** | 智能对话、IOT、MCP接入点、视觉感知、OTA、智控台 | 完整功能体验,数据存储在数据库 |[①Docker版](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%B8%80docker%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [②源码部署](./docs/Deployment_all.md#%E6%96%B9%E5%BC%8F%E4%BA%8C%E6%9C%AC%E5%9C%B0%E6%BA%90%E7%A0%81%E8%BF%90%E8%A1%8C%E5%85%A8%E6%A8%A1%E5%9D%97) / [③源码部署自动更新教程](./docs/dev-ops-integration.md) | 如果使用`FunASR`要4核8G,如果全API,要2核4G| [本地源码启动视频教程](https://www.bilibili.com/video/BV1wBJhz4Ewe) |
> 💡 提示:以下是按最新代码部署后的测试平台,有需要可烧录测试,并发为6个,每天会清空数据
@@ -226,7 +231,7 @@ Websocket接口地址: wss://2662r3426b.vicp.fun/xiaozhi/v1/
| 视觉感知系统 | 支持多种VLLM(视觉大模型),实现多模态交互 |
| 意图识别系统 | 支持LLM意图识别、Function Call函数调用,提供插件化意图处理机制 |
| 记忆系统 | 支持本地短期记忆、mem0ai接口记忆,具备记忆总结功能 |
-| IOT/MCP控制协议 | 支持设备注册管理、智能控制接口,同时支持IOT、MCP控制协议 |
+| 工具调用 | 支持客户端IOT协议、客户MCP协议、服务端MCP协议、MCP接入点协议、自定义工具函数 |
| 管理后台 | 提供Web管理界面,支持用户管理、系统配置和设备管理 |
| 测试工具 | 提供性能测试工具、视觉模型测试工具和音频交互测试工具 |
| 部署支持 | 支持Docker部署和本地部署,提供完整的配置文件管理 |
diff --git a/docs/dev-ops-integration.md b/docs/dev-ops-integration.md
index bb9640b1..3c547f4d 100644
--- a/docs/dev-ops-integration.md
+++ b/docs/dev-ops-integration.md
@@ -4,6 +4,8 @@
本项目的测试平台`https://2662r3426b.vicp.fun`,从开放以来就使用了该方法,效果良好。
+教程可参考B站博主`毕乐labs`发布的视频教程:[《开源小智服务器xiaozhi-server自动更新以及最新版本MCP接入点配置保姆教程》](https://www.bilibili.com/video/BV15H37zHE7Q)
+
# 开始条件
- 你的电脑/服务器是linux操作系统
- 你已经跑通了整个流程
@@ -40,6 +42,9 @@ git clone https://ghproxy.net/https://github.com/xinnan-tech/xiaozhi-esp32-serve
此刻你需要把`model.pt`文件复制到新的目录去,你可以这样
```
+# 创建需要的目录
+mkdir -p /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/
+
cp 你原来的.config.yaml完整路径 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/data/.config.yaml
cp 你原来的model.pt完整路径 /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/models/SenseVoiceSmall/model.pt
```
@@ -161,3 +166,11 @@ tail -f /home/system/xiaozhi/xiaozhi-esp32-server/main/xiaozhi-server/tmp/server
# 注意事项
测试平台`https://2662r3426b.vicp.fun`,是使用nginx做了反向代理。nginx.conf详细配置可以[参考这里](https://github.com/xinnan-tech/xiaozhi-esp32-server/issues/791)
+
+## 常见问题
+
+### 1、为什么没有见到8001端口?
+回答:8001是开发环境使用的,用于运行前端的端口。如果你是服务器部署,不建议使用`npm run serve`启动8001端口运行前端,而是像本教程一样编译成html文件,然后使用nginx来管理访问。
+
+### 2、每次更新需要更新手动SQL语句吗?
+回答:不需要,因为项目使用**Liquibase**管理数据库版本,会自动执行新的sql脚本。
\ No newline at end of file
diff --git a/docs/docker/nginx.conf b/docs/docker/nginx.conf
index 9b9c3889..ca3bac3a 100644
--- a/docs/docker/nginx.conf
+++ b/docs/docker/nginx.conf
@@ -42,9 +42,9 @@ http {
proxy_set_header Referer $http_referer;
proxy_set_header Cookie $http_cookie;
- proxy_connect_timeout 10;
- proxy_send_timeout 10;
- proxy_read_timeout 10;
+ proxy_connect_timeout 15;
+ proxy_send_timeout 15;
+ proxy_read_timeout 15;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
diff --git a/docs/docker/start.sh b/docs/docker/start.sh
index bdaa1e1a..a64f46ba 100644
--- a/docs/docker/start.sh
+++ b/docs/docker/start.sh
@@ -6,6 +6,7 @@ java -jar /app/xiaozhi-esp32-api.jar \
--spring.datasource.druid.username=${SPRING_DATASOURCE_DRUID_USERNAME} \
--spring.datasource.druid.password=${SPRING_DATASOURCE_DRUID_PASSWORD} \
--spring.data.redis.host=${SPRING_DATA_REDIS_HOST} \
+ --spring.data.redis.password=${SPRING_DATA_REDIS_PASSWORD} \
--spring.data.redis.port=${SPRING_DATA_REDIS_PORT} &
# 启动Nginx(前台运行保持容器存活)
diff --git a/docs/images/demo13.png b/docs/images/demo13.png
new file mode 100644
index 00000000..1ad79fa3
Binary files /dev/null and b/docs/images/demo13.png differ
diff --git a/docs/mcp-endpoint-enable.md b/docs/mcp-endpoint-enable.md
new file mode 100644
index 00000000..7bc72484
--- /dev/null
+++ b/docs/mcp-endpoint-enable.md
@@ -0,0 +1,125 @@
+# MCP 接入点部署使用指南
+
+本教程包含2个部分
+- 1、如何部署MCP接入点这个服务
+- 1、全模块部署时,怎么配置MCP接入点
+- 2、单模块部署时,怎么配置MCP接入点
+
+# 1、如何部署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
+```
+
+此时,日志里会输出类似以下的日志
+```
+250705 INFO-=====下面的地址分别是智控台/单模块MCP接入点地址====
+250705 INFO-智控台MCP参数配置: http://172.22.0.2:8004/mcp_endpoint/health?key=abc
+250705 INFO-单模块部署MCP接入点: ws://172.22.0.2:8004/mcp_endpoint/mcp/?token=def
+250705 INFO-=====请根据具体部署选择使用,请勿泄露给任何人======
+```
+
+请你把两个接口地址复制出来:
+
+由于你是docker部署,切不可直接使用上面的地址!
+
+由于你是docker部署,切不可直接使用上面的地址!
+
+由于你是docker部署,切不可直接使用上面的地址!
+
+你先把地址复制出来,放在一个草稿里,你要知道你的电脑的局域网ip是什么,例如我的电脑局域网ip是`192.168.1.25`,那么
+原来我的接口地址
+```
+智控台MCP参数配置: http://172.22.0.2:8004/mcp_endpoint/health?key=abc
+单模块部署MCP接入点: ws://172.22.0.2:8004/mcp_endpoint/mcp/?token=def
+```
+就要改成
+```
+智控台MCP参数配置: http://192.168.1.25:8004/mcp_endpoint/health?key=abc
+单模块部署MCP接入点: ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=def
+```
+
+改好后,请使用浏览器直接访问`智控台MCP参数配置`。当浏览器出现类似这样的代码,说明是成功了。
+```
+{"result":{"status":"success","connections":{"tool_connections":0,"robot_connections":0,"total_connections":0}},"error":null,"id":null,"jsonrpc":"2.0"}
+```
+
+请你保留好上面两个`接口地址`,下一步要用到。
+
+# 2、全模块部署时,怎么配置MCP接入点
+
+如果你是全模块部署,使用管理员账号,登录智控台,点击顶部`参数字典`,选择`参数管理`功能。
+
+然后搜索参数`server.mcp_endpoint`,此时,它的值应该是`null`值。
+点击修改按钮,把上一步得来的`智控台MCP参数配置`粘贴到`参数值`里。然后保存。
+
+如果能保存成功,说明一切顺利,你可以去智能体查看效果了。如果不成功,说明智控台无法访问mcp接入点,很大概率是网络防火墙,或者没有填写正确的局域网ip。
+
+# 3、单模块部署时,怎么配置MCP接入点
+
+如果你是单模块部署,找到你的配置文件`data/.config.yaml`。
+在配置文件搜索`mcp_endpoint`,如果没有找到,你就增加`mcp_endpoint`配置。类似我是就是这样
+```
+server:
+ websocket: ws://你的ip或者域名:端口号/xiaozhi/v1/
+ http_port: 8002
+log:
+ log_level: INFO
+
+# 此处可能还更多配置..
+
+mcp_endpoint: 你的接入点websocket地址
+```
+这时,请你把`如何部署MCP接入点这个服务`中得到的`单模块部署MCP接入点` 粘贴到 `mcp_endpoint`中。类似这样
+
+```
+server:
+ websocket: ws://你的ip或者域名:端口号/xiaozhi/v1/
+ http_port: 8002
+log:
+ log_level: INFO
+
+# 此处可能还更多配置
+
+mcp_endpoint: ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=def
+```
+
+配置好后,启动单模块会输出如下的日志。
+```
+250705[__main__]-INFO-初始化组件: vad成功 SileroVAD
+250705[__main__]-INFO-初始化组件: asr成功 FunASRServer
+250705[__main__]-INFO-OTA接口是 http://192.168.1.25:8002/xiaozhi/ota/
+250705[__main__]-INFO-视觉分析接口是 http://192.168.1.25:8002/mcp/vision/explain
+250705[__main__]-INFO-mcp接入点是 ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
+250705[__main__]-INFO-Websocket地址是 ws://192.168.1.25:8000/xiaozhi/v1/
+250705[__main__]-INFO-=======上面的地址是websocket协议地址,请勿用浏览器访问=======
+250705[__main__]-INFO-如想测试websocket请用谷歌浏览器打开test目录下的test_page.html
+250705[__main__]-INFO-=============================================================
+```
+
+如上,如果能输出类似的`mcp接入点是`中`ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc`说明配置成功了。
+
diff --git a/docs/mcp-endpoint-integration.md b/docs/mcp-endpoint-integration.md
index ca72aef1..91d62278 100644
--- a/docs/mcp-endpoint-integration.md
+++ b/docs/mcp-endpoint-integration.md
@@ -1,92 +1,36 @@
-# MCP 接入点部署使用指南
+# MCP 接入点使用指南
-本教程包含2个部分
-- 1、如何开启mcp接入点
-- 2、如何为智能体接入一个简单的mcp功能,如计算器功能
+本教程以虾哥开源的mcp计算器功能为示例,介绍如何将自己自定义的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。
+本教程的前提是,你的`xiaozhi-server`已经启用了mcp接入点功能,如果你还没启用,可以先根据[这个教程](./mcp-endpoint-enable.md)启用。
# 如何为智能体接入一个简单的mcp功能,如计算器功能
-如果以上步骤顺利,你可以进入智能体管理,点击`配置角色`,在`意图识别`的右边,有一个`编辑功能`的按钮。
+### 如果你是全模块部署
+如果你是全模块部署,你可以进入智控台,智能体管理,点击`配置角色`,在`意图识别`的右边,有一个`编辑功能`的按钮。
点击这个按钮。在弹出的页面里,位于底部,会有`MCP接入点`,正常来说,会显示这个智能体的`MCP接入点地址`,接下来,我们来给这个智能体扩展一个基于MCP技术的计算器的功能。
这个`MCP接入点地址`很重要,你等一下会用到。
+### 如果你是单模块部署
+如果你是单模块部署,且你已经在配置文件里配置了MCP接入点地址,那么正常来说,单模块部署启动的时候,会输出如下的日志。
+```
+250705[__main__]-INFO-初始化组件: vad成功 SileroVAD
+250705[__main__]-INFO-初始化组件: asr成功 FunASRServer
+250705[__main__]-INFO-OTA接口是 http://192.168.1.25:8002/xiaozhi/ota/
+250705[__main__]-INFO-视觉分析接口是 http://192.168.1.25:8002/mcp/vision/explain
+250705[__main__]-INFO-mcp接入点是 ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
+250705[__main__]-INFO-Websocket地址是 ws://192.168.1.25:8000/xiaozhi/v1/
+250705[__main__]-INFO-=======上面的地址是websocket协议地址,请勿用浏览器访问=======
+250705[__main__]-INFO-如想测试websocket请用谷歌浏览器打开test目录下的test_page.html
+250705[__main__]-INFO-=============================================================
+```
+
+如上,输出`mcp接入点是`中`ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc`就是你的`MCP接入点地址`。
+
+这个`MCP接入点地址`很重要,你等一下会用到。
+
## 第一步 下载虾哥MCP计算器项目代码
浏览器打开虾哥写的[计算器项目](https://github.com/78/mcp-calculator),
@@ -114,13 +58,13 @@ pip install -r requirements.txt
例如我的智能体的mcp地址是
```
-ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc
+ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
```
开始输入命令
```bash
-export MCP_ENDPOINT=ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc
+export MCP_ENDPOINT=ws://192.168.1.25:8004/mcp_endpoint/mcp/?token=abc
```
输入完后,启动程序
@@ -129,6 +73,22 @@ export MCP_ENDPOINT=ws://192.168.4.7:8004/mcp_endpoint/mcp/?token=abc
python mcp_pipe.py calculator.py
```
+### 如果你是智控台部署
+如果你是智控台部署,启动完后,你再进入智控台,点击刷新MCP的接入状态,就会看到你扩展的功能列表了。
-启动完后,你再进入智控台,点击刷新MCP的接入状态,就会看到你扩展的功能列表了。
+### 如果你是单模块部署
+如果你是单模块部署,当设备连接后,会输出类似的日志,说明成功了
+```
+250705 -INFO-正在初始化MCP接入点: wss://2662r3426b.vicp.fun/mcp_e
+250705 -INFO-发送MCP接入点初始化消息
+250705 -INFO-MCP接入点连接成功
+250705 -INFO-MCP接入点初始化成功
+250705 -INFO-统一工具处理器初始化完成
+250705 -INFO-MCP接入点服务器信息: name=Calculator, version=1.9.4
+250705 -INFO-MCP接入点支持的工具数量: 1
+250705 -INFO-所有MCP接入点工具已获取,客户端准备就绪
+250705 -INFO-工具缓存已刷新
+250705 -INFO-当前支持的函数列表: [ 'get_time', 'get_lunar', 'play_music', 'get_weather', 'handle_exit_intent', 'calculator']
+```
+如果包含了 `'calculator'`,说明设备将可以根据意图识别,调用计算器这个工具。
\ No newline at end of file
diff --git a/docs/newsnow_plugin_config.md b/docs/newsnow_plugin_config.md
new file mode 100644
index 00000000..8be887c2
--- /dev/null
+++ b/docs/newsnow_plugin_config.md
@@ -0,0 +1,105 @@
+# get_news_from_newsnow 插件新闻源配置指南
+
+## 概述
+
+`get_news_from_newsnow` 插件现在支持通过Web管理界面动态配置新闻源,不再需要修改代码。用户可以在智控台中为每个智能体配置不同的新闻源。
+
+## 配置方式
+
+### 1. 通过Web管理界面配置(推荐)
+
+1. 登录智控台
+2. 进入"角色配置"页面
+3. 选择要配置的智能体
+4. 点击"编辑功能"按钮
+5. 在右侧参数配置区域找到"newsnow新闻聚合"插件
+6. 在"新闻源配置"字段中输入分号分隔的中文名称
+
+### 2. 配置文件方式
+
+在 `config.yaml` 中配置:
+
+```yaml
+plugins:
+ get_news_from_newsnow:
+ url: "https://newsnow.busiyi.world/api/s?id="
+ news_sources: "澎湃新闻;百度热搜;财联社;微博;抖音"
+```
+
+## 新闻源配置格式
+
+新闻源配置使用分号分隔的中文名称,格式为:
+
+```
+中文名称1;中文名称2;中文名称3
+```
+
+### 配置示例
+
+```
+澎湃新闻;百度热搜;财联社;微博;抖音;知乎;36氪
+```
+
+## 支持的新闻源
+
+插件支持以下新闻源的中文名称:
+
+- 澎湃新闻
+- 百度热搜
+- 财联社
+- 微博
+- 抖音
+- 知乎
+- 36氪
+- 华尔街见闻
+- IT之家
+- 今日头条
+- 虎扑
+- 哔哩哔哩
+- 快手
+- 雪球
+- 格隆汇
+- 法布财经
+- 金十数据
+- 牛客
+- 少数派
+- 稀土掘金
+- 凤凰网
+- 虫部落
+- 联合早报
+- 酷安
+- 远景论坛
+- 参考消息
+- 卫星通讯社
+- 百度贴吧
+- 靠谱新闻
+- 以及更多...
+
+## 默认配置
+
+如果未配置新闻源,插件将使用以下默认配置:
+
+```
+澎湃新闻;百度热搜;财联社
+```
+
+## 使用说明
+
+1. **配置新闻源**:在Web界面或配置文件中设置新闻源的中文名称,用分号分隔
+2. **调用插件**:用户可以说"播报新闻"或"获取新闻"
+3. **指定新闻源**:用户可以说"播报澎湃新闻"或"获取百度热搜"
+4. **获取详情**:用户可以说"详细介绍这条新闻"
+
+## 工作原理
+
+1. 插件接受中文名称作为参数(如"澎湃新闻")
+2. 根据配置的新闻源列表,将中文名称转换为对应的英文ID(如"thepaper")
+3. 使用英文ID调用API获取新闻数据
+4. 返回新闻内容给用户
+
+## 注意事项
+
+1. 配置的中文名称必须与 CHANNEL_MAP 中定义的名称完全一致
+2. 配置更改后需要重启服务或重新加载配置
+3. 如果配置的新闻源无效,插件会自动使用默认新闻源
+4. 多个新闻源之间使用英文分号(;)分隔,不要使用中文分号(;)
\ No newline at end of file
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 32a121fa..5be7b639 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
@@ -232,7 +232,7 @@ public interface Constant {
/**
* 版本号
*/
- public static final String VERSION = "0.6.1";
+ public static final String VERSION = "0.6.3";
/**
* 无效固件URL
diff --git a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java
index 087c1b9d..36b59993 100644
--- a/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java
+++ b/main/manager-api/src/main/java/xiaozhi/modules/agent/service/biz/impl/AgentChatHistoryBizServiceImpl.java
@@ -19,6 +19,8 @@ import xiaozhi.modules.agent.service.AgentChatAudioService;
import xiaozhi.modules.agent.service.AgentChatHistoryService;
import xiaozhi.modules.agent.service.AgentService;
import xiaozhi.modules.agent.service.biz.AgentChatHistoryBizService;
+import xiaozhi.modules.device.entity.DeviceEntity;
+import xiaozhi.modules.device.service.DeviceService;
/**
* {@link AgentChatHistoryBizService} impl
@@ -35,6 +37,7 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
private final AgentChatHistoryService agentChatHistoryService;
private final AgentChatAudioService agentChatAudioService;
private final RedisUtils redisUtils;
+ private final DeviceService deviceService;
/**
* 处理聊天记录上报,包括文件上传和相关信息记录
@@ -68,6 +71,15 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
// 更新设备最后对话时间
redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), new Date());
+
+ // 更新设备最后连接时间
+ DeviceEntity device = deviceService.getDeviceByMacAddress(macAddress);
+ if (device != null) {
+ deviceService.updateDeviceConnectionInfo(agentId, device.getId(), null);
+ } else {
+ log.warn("聊天记录上报时,未找到mac地址为 {} 的设备", macAddress);
+ }
+
return Boolean.TRUE;
}
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
index e45d2201..839f99ff 100644
--- 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
@@ -5,6 +5,7 @@ import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -18,7 +19,6 @@ 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;
@@ -61,53 +61,119 @@ public class AgentMcpAccessPointServiceImpl implements AgentMcpAccessPointServic
wsUrl = wsUrl.replace("/mcp/", "/call/");
try {
- // 创建 WebSocket 连接
+ // 创建 WebSocket 连接,增加超时时间到15秒
try (WebSocketClientManager client = WebSocketClientManager.build(
new WebSocketClientManager.Builder()
.uri(wsUrl)
- .connectTimeout(5, TimeUnit.SECONDS)
- .maxSessionDuration(20, TimeUnit.SECONDS))) {
+ .connectTimeout(8, TimeUnit.SECONDS)
+ .maxSessionDuration(10, TimeUnit.SECONDS))) {
- // 发送初始化通知
- McpJsonRpcRequest initRequest = new McpJsonRpcRequest("notifications/initialized");
- client.sendJson(initRequest);
+ // 步骤1: 发送初始化消息并等待响应
+ log.info("发送MCP初始化消息,智能体ID: {}", id);
+ McpJsonRpcRequest initializeRequest = new McpJsonRpcRequest("initialize",
+ Map.of(
+ "protocolVersion", "2024-11-05",
+ "capabilities", Map.of(
+ "roots", Map.of("listChanged", false),
+ "sampling", Map.of()),
+ "clientInfo", Map.of(
+ "name", "xz-mcp-broker",
+ "version", "0.0.1")),
+ 1);
+ client.sendJson(initializeRequest);
- // 等待 0.2 秒
- Thread.sleep(200);
-
- // 发送工具列表请求
- McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 1);
- client.sendJson(toolsRequest);
-
- // 监听响应,直到收到包含 id=1 的响应
- List responses = client.listener(response -> {
+ // 等待初始化响应 (id=1) - 移除固定延迟,改为响应驱动
+ List initResponses = client.listenerWithoutClose(response -> {
try {
- McpJsonRpcResponse jsonResponse = JsonUtils.parseObject(response, McpJsonRpcResponse.class);
- return jsonResponse != null && Integer.valueOf(1).equals(jsonResponse.getId());
+ Map jsonMap = JsonUtils.parseObject(response, Map.class);
+ if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
+ // 检查是否有result字段,表示初始化成功
+ return jsonMap.containsKey("result") && !jsonMap.containsKey("error");
+ }
+ return false;
} catch (Exception e) {
- log.warn("解析响应失败: {}", response, e);
+ log.warn("解析初始化响应失败: {}", response, e);
return false;
}
});
- // 处理响应
- for (String response : responses) {
+ // 验证初始化响应
+ boolean initSucceeded = false;
+ for (String response : initResponses) {
try {
- McpJsonRpcResponse jsonResponse = JsonUtils.parseObject(response, McpJsonRpcResponse.class);
- if (jsonResponse != null && Integer.valueOf(1).equals(jsonResponse.getId())
- && jsonResponse.getResult() != null && jsonResponse.getResult().getTools() != null) {
+ Map jsonMap = JsonUtils.parseObject(response, Map.class);
+ if (jsonMap != null && Integer.valueOf(1).equals(jsonMap.get("id"))) {
+ if (jsonMap.containsKey("result")) {
+ log.info("MCP初始化成功,智能体ID: {}", id);
+ initSucceeded = true;
+ break;
+ } else if (jsonMap.containsKey("error")) {
+ log.error("MCP初始化失败,智能体ID: {}, 错误: {}", id, jsonMap.get("error"));
+ return List.of();
+ }
+ }
+ } catch (Exception e) {
+ log.warn("处理初始化响应失败: {}", response, e);
+ }
+ }
- // 提取工具名称列表
- return java.util.Arrays.stream(jsonResponse.getResult().getTools())
- .map(McpJsonRpcResponse.McpTool::getName)
- .collect(Collectors.toList());
+ if (!initSucceeded) {
+ log.error("未收到有效的MCP初始化响应,智能体ID: {}", id);
+ return List.of();
+ }
+
+ // 步骤2: 发送初始化完成通知 - 只有在收到initialize响应后才发送
+ log.info("发送MCP初始化完成通知,智能体ID: {}", id);
+ String notificationJson = "{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}";
+ client.sendText(notificationJson);
+
+ // 步骤3: 发送工具列表请求 - 立即发送,无需额外延迟
+ log.info("发送MCP工具列表请求,智能体ID: {}", id);
+ McpJsonRpcRequest toolsRequest = new McpJsonRpcRequest("tools/list", null, 2);
+ client.sendJson(toolsRequest);
+
+ // 等待工具列表响应 (id=2)
+ List toolsResponses = client.listener(response -> {
+ try {
+ Map jsonMap = JsonUtils.parseObject(response, Map.class);
+ return jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"));
+ } catch (Exception e) {
+ log.warn("解析工具列表响应失败: {}", response, e);
+ return false;
+ }
+ });
+
+ // 处理工具列表响应
+ for (String response : toolsResponses) {
+ try {
+ Map jsonMap = JsonUtils.parseObject(response, Map.class);
+ if (jsonMap != null && Integer.valueOf(2).equals(jsonMap.get("id"))) {
+ // 检查是否有result字段
+ Object resultObj = jsonMap.get("result");
+ if (resultObj instanceof Map) {
+ Map resultMap = (Map) resultObj;
+ Object toolsObj = resultMap.get("tools");
+ if (toolsObj instanceof List) {
+ List