From 03813208595ea688726a55f3a9c65f7a5617ffed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=8E=E5=A4=A9=E5=A5=BD=E5=83=8F=E6=B2=A1=E4=BB=80?= =?UTF-8?q?=E4=B9=88?= Date: Sat, 25 Oct 2025 16:16:23 +0800 Subject: [PATCH] =?UTF-8?q?revert(mcp):=20=E7=A7=BB=E9=99=A4MCP=20SSE?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E5=8F=8A=E5=B7=A5=E5=85=B7=E9=9B=86?= =?UTF-8?q?=E6=88=90=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO.md | 2 +- docs/MCP_SIMPLE_GUIDE.md | 175 --------- docs/MCP_SSE_INTEGRATION.md | 175 --------- docs/MCP_SSE_QUICKSTART.md | 178 ---------- docs/MCP_TOOLS_INTEGRATION.md | 356 ------------------- src/config/api_ada_configs.py | 4 +- src/main.py | 43 +-- src/plugin_system/apis/tool_api.py | 20 +- src/plugin_system/core/tool_use.py | 5 +- src/plugin_system/utils/mcp_connector.py | 233 ------------ src/plugin_system/utils/mcp_tool_provider.py | 173 --------- template/bot_config_template.toml | 26 +- template/model_config_template.toml | 18 - 13 files changed, 24 insertions(+), 1384 deletions(-) delete mode 100644 docs/MCP_SIMPLE_GUIDE.md delete mode 100644 docs/MCP_SSE_INTEGRATION.md delete mode 100644 docs/MCP_SSE_QUICKSTART.md delete mode 100644 docs/MCP_TOOLS_INTEGRATION.md delete mode 100644 src/plugin_system/utils/mcp_connector.py delete mode 100644 src/plugin_system/utils/mcp_tool_provider.py diff --git a/TODO.md b/TODO.md index a44c61152..f66768422 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,7 @@ - [x] 内置空间插件 - [ ] 在好友聊天生成回复时设置输入状态 - [x] 基于关键帧的视频识别功能 -- [x] 对XML,JSON等特殊消息解析 +- [ ] 对XML,JSON等特殊消息解析 - [x] 插件热重载 - [x] 适配器黑/白名单迁移至独立配置文件,并支持热重载 - [x] 添加MySQL支持,重构数据库 diff --git a/docs/MCP_SIMPLE_GUIDE.md b/docs/MCP_SIMPLE_GUIDE.md deleted file mode 100644 index 291ccd5ab..000000000 --- a/docs/MCP_SIMPLE_GUIDE.md +++ /dev/null @@ -1,175 +0,0 @@ -# MCP工具集成 - 简化指南 - -## ✅ 已完成的工作 - -MCP (Model Context Protocol) 工具支持已经完全集成到MoFox Bot!**AI现在可以自动发现并调用MCP工具了**。 - -## 🎯 快速开始 - -### 步骤1: 启动MCP服务器 - -首先你需要一个MCP服务器。最简单的方式是使用官方提供的文件系统服务器: - -```bash -# 安装(需要Node.js) -npm install -g @modelcontextprotocol/server-filesystem - -# 启动服务器,允许访问指定目录 -mcp-server-filesystem --port 3000 /path/to/your/project -``` - -### 步骤2: 配置Bot - -编辑 `config/bot_config.toml`,在文件末尾添加: - -```toml -[[mcp_servers]] -name = "filesystem" -url = "http://localhost:3000" -api_key = "" # 如果服务器不需要认证就留空 -timeout = 30 -enabled = true -``` - -### 步骤3: 启动Bot - -```bash -python bot.py -``` - -启动后你会看到: - -``` -[INFO] 连接MCP服务器: filesystem (http://localhost:3000) -[INFO] 从filesystem获取5个工具 -[INFO] MCP工具提供器初始化成功 -``` - -### 步骤4: AI自动使用工具 - -现在AI可以自动调用MCP工具了! - -**示例对话:** - -``` -用户: 帮我读取README.md文件的内容 - -AI: [内部决策: 需要读取文件 → 调用 filesystem_read_file 工具] - README.md的内容是... - -用户: 列出当前目录下的所有文件 - -AI: [调用 filesystem_list_directory 工具] - 当前目录包含以下文件: - - README.md - - bot.py - - ... -``` - -## 🔧 工作原理 - -``` -用户消息 - ↓ -AI决策系统 (ToolExecutor) - ↓ -获取可用工具列表 - ↓ -【包含Bot内置工具 + MCP工具】 ← 自动合并 - ↓ -AI选择需要的工具 - ↓ -执行工具调用 - ↓ -返回结果给用户 -``` - -## 📝 配置多个MCP服务器 - -```toml -# 文件系统工具 -[[mcp_servers]] -name = "filesystem" -url = "http://localhost:3000" -enabled = true - -# Git工具 -[[mcp_servers]] -name = "git" -url = "http://localhost:3001" -enabled = true - -# 数据库工具 -[[mcp_servers]] -name = "database" -url = "http://localhost:3002" -api_key = "your-secret-key" -enabled = true -``` - -每个服务器的工具会自动添加名称前缀: -- `filesystem_read_file` -- `git_status` -- `database_query` - -## 🛠️ 可用的MCP服务器 - -官方提供的MCP服务器: - -1. **@modelcontextprotocol/server-filesystem** - 文件系统操作 -2. **@modelcontextprotocol/server-git** - Git操作 -3. **@modelcontextprotocol/server-github** - GitHub API -4. **@modelcontextprotocol/server-sqlite** - SQLite数据库 -5. **@modelcontextprotocol/server-postgres** - PostgreSQL数据库 - -你也可以开发自定义MCP服务器! - -## 🐛 常见问题 - -### Q: 如何查看AI是否使用了MCP工具? - -查看日志,会显示: -``` -[INFO] [工具执行器] 正在执行工具: filesystem_read_file -[INFO] 调用MCP工具: filesystem_read_file -``` - -### Q: MCP服务器连接失败怎么办? - -检查: -1. MCP服务器是否正在运行 -2. URL配置是否正确(注意端口号) -3. 防火墙是否阻止连接 - -### Q: 如何临时禁用MCP工具? - -在配置中设置 `enabled = false`: - -```toml -[[mcp_servers]] -name = "filesystem" -url = "http://localhost:3000" -enabled = false # 禁用 -``` - -## 📚 相关文档 - -- **详细集成文档**: [MCP_TOOLS_INTEGRATION.md](./MCP_TOOLS_INTEGRATION.md) -- **MCP SSE客户端**: [MCP_SSE_USAGE.md](./MCP_SSE_USAGE.md) -- **MCP协议官方文档**: https://github.com/anthropics/mcp - -## 🎉 总结 - -MCP工具支持已经完全集成!你只需要: - -1. ✅ 启动MCP服务器 -2. ✅ 在`bot_config.toml`中配置 -3. ✅ 启动Bot - -**AI会自动发现并使用工具,无需任何额外代码!** - ---- - -**实现方式**: 通过修改`tool_api.py`和`tool_use.py`,将MCP工具无缝集成到现有工具系统 -**版本**: v1.0.0 -**日期**: 2025-10-05 diff --git a/docs/MCP_SSE_INTEGRATION.md b/docs/MCP_SSE_INTEGRATION.md deleted file mode 100644 index 90c569e7c..000000000 --- a/docs/MCP_SSE_INTEGRATION.md +++ /dev/null @@ -1,175 +0,0 @@ -# MCP SSE 集成完成报告 - -## ✅ 集成状态:已完成 - -MCP (Model Context Protocol) SSE (Server-Sent Events) 客户端已完全集成到 MoFox Bot 框架中。 - -## 📋 完成的工作 - -### 1. 依赖管理 -- ✅ 在 `pyproject.toml` 中添加 `mcp>=0.9.0` 和 `sse-starlette>=2.2.1` -- ✅ 在 `requirements.txt` 中同步添加依赖 - -### 2. 客户端实现 -- ✅ 创建 `src/llm_models/model_client/mcp_sse_client.py` -- ✅ 实现完整的MCP SSE协议支持 -- ✅ 支持流式响应、工具调用、多模态内容 -- ✅ 实现中断处理和Token统计 - -### 3. 配置系统集成 -- ✅ 在 `src/config/api_ada_configs.py` 中添加 `"mcp_sse"` 到 `client_type` 的 `Literal` 类型 -- ✅ 在 `src/llm_models/model_client/__init__.py` 中注册客户端 -- ✅ 通过 `@client_registry.register_client_class("mcp_sse")` 装饰器完成自动注册 - -### 4. 配置模板 -- ✅ 在 `template/model_config_template.toml` 中添加 MCP Provider 配置示例 -- ✅ 添加 MCP 模型配置示例 -- ✅ 提供详细的配置注释 - -### 5. 文档 -- ✅ 创建 `docs/MCP_SSE_USAGE.md` - 详细使用文档 -- ✅ 创建 `docs/MCP_SSE_QUICKSTART.md` - 快速配置指南 -- ✅ 创建 `docs/MCP_SSE_INTEGRATION.md` - 集成完成报告(本文档) - -### 6. 任务追踪 -- ✅ 更新 `TODO.md`,标记"添加MCP SSE支持"为已完成 - -## 🔧 配置示例 - -### Provider配置 -```toml -[[api_providers]] -name = "MCPProvider" -base_url = "https://your-mcp-server.com" -api_key = "your-api-key" -client_type = "mcp_sse" # 关键:使用MCP SSE客户端 -timeout = 60 -max_retry = 2 -``` - -### 模型配置 -```toml -[[models]] -model_identifier = "claude-3-5-sonnet-20241022" -name = "mcp-claude" -api_provider = "MCPProvider" -force_stream_mode = true -``` - -### 任务配置 -```toml -[model_task_config.replyer] -model_list = ["mcp-claude"] -temperature = 0.7 -max_tokens = 800 -``` - -## 🎯 功能特性 - -### 支持的功能 -- ✅ 流式响应(SSE协议) -- ✅ 多轮对话 -- ✅ 工具调用(Function Calling) -- ✅ 多模态内容(文本+图片) -- ✅ 中断信号处理 -- ✅ Token使用统计 -- ✅ 自动重试和错误处理 -- ✅ API密钥轮询 - -### 当前限制 -- ❌ 不支持嵌入(Embedding)功能 -- ❌ 不支持音频转录功能 - -## 📊 架构集成 - -``` -MoFox Bot -├── src/llm_models/ -│ ├── model_client/ -│ │ ├── base_client.py # 基础客户端接口 -│ │ ├── openai_client.py # OpenAI客户端 -│ │ ├── aiohttp_gemini_client.py # Gemini客户端 -│ │ ├── mcp_sse_client.py # ✨ MCP SSE客户端(新增) -│ │ └── __init__.py # 客户端注册(已更新) -│ └── ... -├── src/config/ -│ └── api_ada_configs.py # ✨ 添加mcp_sse类型(已更新) -├── template/ -│ └── model_config_template.toml # ✨ 添加MCP配置示例(已更新) -├── docs/ -│ ├── MCP_SSE_USAGE.md # ✨ 使用文档(新增) -│ ├── MCP_SSE_QUICKSTART.md # ✨ 快速配置指南(新增) -│ └── MCP_SSE_INTEGRATION.md # ✨ 集成报告(本文档) -└── pyproject.toml # ✨ 添加依赖(已更新) -``` - -## 🚀 使用流程 - -1. **安装依赖** - ```bash - uv sync - ``` - -2. **配置Provider和模型** - - 编辑 `model_config.toml` - - 参考 `template/model_config_template.toml` 中的示例 - -3. **使用MCP模型** - - 在任何 `model_task_config` 中引用配置的MCP模型 - - 例如:`model_list = ["mcp-claude"]` - -4. **启动Bot** - - 正常启动,MCP客户端会自动加载 - -## 🔍 验证方法 - -### 检查客户端注册 -启动Bot后,查看日志确认MCP SSE客户端已加载: -``` -[INFO] 已注册客户端类型: mcp_sse -``` - -### 测试配置 -发送测试消息,确认MCP模型正常响应。 - -### 查看日志 -``` -[INFO] MCP-SSE客户端: 正在处理请求... -[DEBUG] SSE流: 接收到内容块... -``` - -## 📚 相关文档 - -- **快速开始**: [MCP_SSE_QUICKSTART.md](./MCP_SSE_QUICKSTART.md) -- **详细使用**: [MCP_SSE_USAGE.md](./MCP_SSE_USAGE.md) -- **配置模板**: [model_config_template.toml](../template/model_config_template.toml) -- **MCP协议**: [https://github.com/anthropics/mcp](https://github.com/anthropics/mcp) - -## 🐛 已知问题 - -目前没有已知问题。 - -## 📝 更新日志 - -### v0.8.1 (2025-10-05) -- ✅ 添加MCP SSE客户端支持 -- ✅ 集成到配置系统 -- ✅ 提供完整文档和配置示例 - -## 👥 贡献者 - -- MoFox Studio Team - -## 📞 技术支持 - -如遇到问题: -1. 查看日志文件中的错误信息 -2. 参考文档排查配置问题 -3. 提交Issue到项目仓库 -4. 加入QQ交流群寻求帮助 - ---- - -**集成完成时间**: 2025-10-05 -**集成版本**: v0.8.1 -**状态**: ✅ 生产就绪 diff --git a/docs/MCP_SSE_QUICKSTART.md b/docs/MCP_SSE_QUICKSTART.md deleted file mode 100644 index 9c789e3fd..000000000 --- a/docs/MCP_SSE_QUICKSTART.md +++ /dev/null @@ -1,178 +0,0 @@ -# MCP SSE 快速配置指南 - -## 什么是MCP SSE? - -MCP (Model Context Protocol) SSE (Server-Sent Events) 是一种支持流式通信的协议,允许MoFox Bot通过SSE与兼容MCP协议的AI服务进行交互。 - -## 快速开始 - -### 步骤1: 安装依赖 - -```bash -# 使用uv(推荐) -uv sync - -# 或使用pip -pip install mcp>=0.9.0 sse-starlette>=2.2.1 -``` - -### 步骤2: 编辑配置文件 - -打开或创建 `model_config.toml` 文件,添加以下配置: - -#### 2.1 添加MCP Provider - -```toml -[[api_providers]] -name = "MCPProvider" # Provider名称,可自定义 -base_url = "https://your-mcp-server.com" # 你的MCP服务器地址 -api_key = "your-mcp-api-key" # 你的API密钥 -client_type = "mcp_sse" # 必须设置为 "mcp_sse" -timeout = 60 # 超时时间(秒) -max_retry = 2 # 最大重试次数 -``` - -#### 2.2 添加MCP模型 - -```toml -[[models]] -model_identifier = "claude-3-5-sonnet-20241022" # 模型ID -name = "mcp-claude" # 模型名称,用于引用 -api_provider = "MCPProvider" # 使用上面配置的Provider -force_stream_mode = true # MCP建议使用流式模式 -price_in = 3.0 # 输入价格(可选) -price_out = 15.0 # 输出价格(可选) -``` - -#### 2.3 在任务中使用MCP模型 - -```toml -# 例如:使用MCP模型作为回复模型 -[model_task_config.replyer] -model_list = ["mcp-claude"] # 引用上面定义的模型名称 -temperature = 0.7 -max_tokens = 800 -``` - -### 步骤3: 验证配置 - -启动MoFox Bot,查看日志确认MCP SSE客户端是否正确加载: - -``` -[INFO] MCP-SSE客户端: 正在初始化... -[INFO] 已加载模型: mcp-claude (MCPProvider) -``` - -## 完整配置示例 - -```toml -# ===== MCP SSE Provider配置 ===== -[[api_providers]] -name = "MCPProvider" -base_url = "https://api.anthropic.com" # Anthropic的Claude支持MCP -api_key = "sk-ant-xxx..." -client_type = "mcp_sse" -timeout = 60 -max_retry = 2 -retry_interval = 10 - -# ===== MCP模型配置 ===== -[[models]] -model_identifier = "claude-3-5-sonnet-20241022" -name = "mcp-claude-sonnet" -api_provider = "MCPProvider" -force_stream_mode = true -price_in = 3.0 -price_out = 15.0 - -[[models]] -model_identifier = "claude-3-5-haiku-20241022" -name = "mcp-claude-haiku" -api_provider = "MCPProvider" -force_stream_mode = true -price_in = 1.0 -price_out = 5.0 - -# ===== 任务配置:使用MCP模型 ===== - -# 回复生成使用Sonnet(高质量) -[model_task_config.replyer] -model_list = ["mcp-claude-sonnet"] -temperature = 0.7 -max_tokens = 800 - -# 小型任务使用Haiku(快速响应) -[model_task_config.utils_small] -model_list = ["mcp-claude-haiku"] -temperature = 0.5 -max_tokens = 500 - -# 工具调用使用Sonnet -[model_task_config.tool_use] -model_list = ["mcp-claude-sonnet"] -temperature = 0.3 -max_tokens = 1000 -``` - -## 支持的MCP服务 - -目前已知支持MCP协议的服务: - -- ✅ **Anthropic Claude** (推荐) -- ✅ 任何实现MCP SSE协议的自定义服务器 -- ⚠️ 其他服务需验证是否支持MCP协议 - -## 常见问题 - -### Q: 我的服务器不支持MCP怎么办? - -A: 确保你的服务器实现了MCP SSE协议规范。如果是标准OpenAI API,请使用 `client_type = "openai"` 而不是 `"mcp_sse"`。 - -### Q: 如何测试MCP连接是否正常? - -A: 启动Bot后,在日志中查找相关信息,或尝试发送一条测试消息。 - -### Q: MCP SSE与OpenAI客户端有什么区别? - -A: -- **MCP SSE**: 使用Server-Sent Events协议,支持更丰富的流式交互 -- **OpenAI**: 使用标准OpenAI API格式 -- **选择建议**: 如果你的服务明确支持MCP,使用MCP SSE;否则使用OpenAI客户端 - -### Q: 可以混合使用不同类型的客户端吗? - -A: 可以!你可以在同一个配置文件中定义多个providers,使用不同的 `client_type`: - -```toml -# OpenAI Provider -[[api_providers]] -name = "OpenAIProvider" -client_type = "openai" -# ... - -# MCP Provider -[[api_providers]] -name = "MCPProvider" -client_type = "mcp_sse" -# ... - -# Gemini Provider -[[api_providers]] -name = "GoogleProvider" -client_type = "aiohttp_gemini" -# ... -``` - -## 下一步 - -- 查看 [MCP_SSE_USAGE.md](./MCP_SSE_USAGE.md) 了解详细API使用 -- 查看 [template/model_config_template.toml](../template/model_config_template.toml) 查看完整配置模板 -- 参考 [README.md](../README.md) 了解MoFox Bot的整体架构 - -## 技术支持 - -如遇到问题,请: -1. 检查日志文件中的错误信息 -2. 确认MCP服务器地址和API密钥正确 -3. 验证服务器是否支持MCP SSE协议 -4. 提交Issue到项目仓库 diff --git a/docs/MCP_TOOLS_INTEGRATION.md b/docs/MCP_TOOLS_INTEGRATION.md deleted file mode 100644 index 736866b6f..000000000 --- a/docs/MCP_TOOLS_INTEGRATION.md +++ /dev/null @@ -1,356 +0,0 @@ -# MCP工具集成完整指南 - -## 概述 - -MoFox Bot现在完全支持MCP (Model Context Protocol),包括: -1. **MCP SSE客户端** - 与支持MCP的LLM(如Claude)通信 -2. **MCP工具提供器** - 将MCP服务器的工具集成到Bot,让AI能够调用 - -## 架构说明 - -``` -┌─────────────────────────────────────────┐ -│ MoFox Bot AI系统 │ -│ ┌───────────────────────────────────┐ │ -│ │ AI决策层 (ToolExecutor) │ │ -│ │ - 分析用户请求 │ │ -│ │ - 决定调用哪些工具 │ │ -│ └───────────────┬───────────────────┘ │ -│ │ │ -│ ┌───────────────▼───────────────────┐ │ -│ │ 工具注册表 (ComponentRegistry) │ │ -│ │ - Bot内置工具 │ │ -│ │ - MCP动态工具 ✨ │ │ -│ └───────────────┬───────────────────┘ │ -│ │ │ -│ ┌───────────────▼───────────────────┐ │ -│ │ MCP工具提供器插件 │ │ -│ │ - 连接MCP服务器 │ │ -│ │ - 动态注册工具 │ │ -│ └───────────────┬───────────────────┘ │ -└──────────────────┼───────────────────────┘ - │ - ┌──────────────▼──────────────┐ - │ MCP连接器 │ - │ - tools/list │ - │ - tools/call │ - │ - resources/list (未来) │ - └──────────────┬──────────────┘ - │ - ┌──────────────▼──────────────┐ - │ MCP服务器 │ - │ - 文件系统工具 │ - │ - Git工具 │ - │ - 数据库工具 │ - │ - 自定义工具... │ - └─────────────────────────────┘ -``` - -## 完整配置步骤 - -### 步骤1: 启动MCP服务器 - -首先你需要一个运行中的MCP服务器。这里以官方的文件系统MCP服务器为例: - -```bash -# 安装MCP服务器(以filesystem为例) -npm install -g @modelcontextprotocol/server-filesystem - -# 启动服务器 -mcp-server-filesystem --port 3000 /path/to/allowed/directory -``` - -或使用其他MCP服务器: -- **Git MCP**: 提供Git操作工具 -- **数据库MCP**: 提供数据库查询工具 -- **自定义MCP服务器**: 你自己开发的MCP服务器 - -### 步骤2: 配置MCP工具提供器插件 - -编辑配置文件 `config/plugins/mcp_tools_provider.toml`: - -```toml -[plugin] -enabled = true # 启用插件 - -# 配置MCP服务器 -[[mcp_servers]] -name = "filesystem" # 服务器标识名 -url = "http://localhost:3000" # MCP服务器地址 -api_key = "" # API密钥(如果需要) -timeout = 30 # 超时时间 -enabled = true # 是否启用 - -# 可以配置多个MCP服务器 -[[mcp_servers]] -name = "git" -url = "http://localhost:3001" -enabled = true -``` - -### 步骤3: 启动Bot - -```bash -python bot.py -``` - -启动后,你会在日志中看到: - -``` -[INFO] MCP工具提供器插件启动中... -[INFO] 发现 1 个MCP服务器配置 -[INFO] 正在连接MCP服务器: filesystem (http://localhost:3000) -[INFO] 从MCP服务器 'filesystem' 获取到 5 个工具 -[INFO] ✓ 已注册MCP工具: filesystem_read_file -[INFO] ✓ 已注册MCP工具: filesystem_write_file -[INFO] ✓ 已注册MCP工具: filesystem_list_directory -... -[INFO] MCP工具提供器插件启动完成,共注册 5 个工具 -``` - -### 步骤4: AI自动调用MCP工具 - -现在AI可以自动发现并调用这些工具!例如: - -**用户**: "帮我读取项目根目录下的README.md文件" - -**AI决策过程**: -1. 分析用户请求 → 需要读取文件 -2. 查找可用工具 → 发现 `filesystem_read_file` -3. 调用工具 → `filesystem_read_file(path="README.md")` -4. 获取结果 → 文件内容 -5. 生成回复 → "README.md的内容是..." - -## 工具命名规则 - -MCP工具会自动添加服务器名前缀,避免冲突: - -- 原始工具名: `read_file` -- 注册后: `filesystem_read_file` - -如果有多个MCP服务器提供相同名称的工具,它们会被区分开: -- 服务器A: `serverA_search` -- 服务器B: `serverB_search` - -## 配置示例 - -### 示例1: 本地文件操作 - -```toml -[[mcp_servers]] -name = "local_fs" -url = "http://localhost:3000" -enabled = true -``` - -**可用工具**: -- `local_fs_read_file` - 读取文件 -- `local_fs_write_file` - 写入文件 -- `local_fs_list_directory` - 列出目录 - -### 示例2: Git操作 - -```toml -[[mcp_servers]] -name = "git" -url = "http://localhost:3001" -enabled = true -``` - -**可用工具**: -- `git_status` - 查看Git状态 -- `git_commit` - 提交更改 -- `git_log` - 查看提交历史 - -### 示例3: 多服务器配置 - -```toml -[[mcp_servers]] -name = "filesystem" -url = "http://localhost:3000" -enabled = true - -[[mcp_servers]] -name = "database" -url = "http://localhost:3002" -api_key = "db-secret-key" -enabled = true - -[[mcp_servers]] -name = "api_tools" -url = "https://mcp.example.com" -api_key = "your-api-key" -timeout = 60 -enabled = true -``` - -## 开发自定义MCP服务器 - -你可以开发自己的MCP服务器来提供自定义工具: - -```javascript -// 简单的MCP服务器示例 (Node.js) -const express = require('express'); -const app = express(); - -app.use(express.json()); - -// 列出工具 -app.post('/tools/list', (req, res) => { - res.json({ - tools: [ - { - name: 'custom_tool', - description: '自定义工具描述', - inputSchema: { - type: 'object', - properties: { - param1: { - type: 'string', - description: '参数1' - } - }, - required: ['param1'] - } - } - ] - }); -}); - -// 执行工具 -app.post('/tools/call', async (req, res) => { - const { name, arguments: args } = req.body; - - if (name === 'custom_tool') { - // 执行你的逻辑 - const result = await doSomething(args.param1); - - res.json({ - content: [ - { - type: 'text', - text: result - } - ] - }); - } -}); - -app.listen(3000, () => { - console.log('MCP服务器运行在 http://localhost:3000'); -}); -``` - -## 常见问题 - -### Q: MCP服务器连接失败? - -**检查**: -1. MCP服务器是否正在运行 -2. URL配置是否正确 -3. 防火墙是否阻止连接 -4. 查看日志中的具体错误信息 - -### Q: 工具注册成功但AI不调用? - -**原因**: -- 工具描述不够清晰 -- 参数定义不明确 - -**解决**: -在MCP服务器端优化工具的`description`和`inputSchema` - -### Q: 如何禁用某个MCP服务器? - -在配置中设置: -```toml -[[mcp_servers]] -enabled = false # 禁用 -``` - -### Q: 如何查看已注册的MCP工具? - -查看启动日志,或在Bot运行时检查组件注册表。 - -## MCP协议规范 - -MCP服务器必须实现以下端点: - -### 1. POST /tools/list -列出所有可用工具 - -**响应**: -```json -{ - "tools": [ - { - "name": "tool_name", - "description": "工具描述", - "inputSchema": { - "type": "object", - "properties": { ... }, - "required": [...] - } - } - ] -} -``` - -### 2. POST /tools/call -执行工具 - -**请求**: -```json -{ - "name": "tool_name", - "arguments": { ... } -} -``` - -**响应**: -```json -{ - "content": [ - { - "type": "text", - "text": "执行结果" - } - ] -} -``` - -## 高级功能 - -### 动态刷新工具列表 - -工具列表默认缓存5分钟。如果MCP服务器更新了工具,Bot会自动在下次缓存过期后刷新。 - -### 错误处理 - -MCP工具调用失败时,会返回错误信息给AI,AI可以据此做出相应处理或提示用户。 - -### 性能优化 - -- 工具列表有缓存机制 -- 支持并发工具调用 -- 自动重试机制 - -## 相关文档 - -- [MCP SSE使用指南](./MCP_SSE_USAGE.md) -- [MCP协议官方文档](https://github.com/anthropics/mcp) -- [插件开发文档](../README.md) - -## 更新日志 - -### v1.0.0 (2025-10-05) -- ✅ 完整的MCP工具集成 -- ✅ 动态工具注册 -- ✅ 多服务器支持 -- ✅ 自动错误处理 - ---- - -**集成状态**: ✅ 生产就绪 -**版本**: v1.0.0 -**更新时间**: 2025-10-05 diff --git a/src/config/api_ada_configs.py b/src/config/api_ada_configs.py index a57c1b264..de7479efb 100644 --- a/src/config/api_ada_configs.py +++ b/src/config/api_ada_configs.py @@ -12,8 +12,8 @@ class APIProvider(ValidatedConfigBase): name: str = Field(..., min_length=1, description="API提供商名称") base_url: str = Field(..., description="API基础URL") api_key: str | list[str] = Field(..., min_length=1, description="API密钥,支持单个密钥或密钥列表轮询") - client_type: Literal["openai", "gemini", "aiohttp_gemini", "mcp_sse"] = Field( - default="openai", description="客户端类型(如openai/google/mcp_sse等,默认为openai)" + client_type: Literal["openai", "gemini", "aiohttp_gemini"] = Field( + default="openai", description="客户端类型(如openai/google等,默认为openai)" ) max_retry: int = Field(default=2, ge=0, description="最大重试次数(单个模型API调用失败,最多重试的次数)") timeout: int = Field( diff --git a/src/main.py b/src/main.py index ad263b032..1004b6480 100644 --- a/src/main.py +++ b/src/main.py @@ -414,37 +414,28 @@ MoFox_Bot(第三方修改版) # 处理所有缓存的事件订阅(插件加载完成后) event_manager.process_all_pending_subscriptions() + + # 初始化表情管理器 + get_emoji_manager().initialize() + logger.info("表情包管理器初始化成功") - # 初始化MCP工具提供器 + """ + # 初始化回复后关系追踪系统 try: - mcp_config = global_config.get("mcp_servers", []) - if mcp_config: - from src.plugin_system.utils.mcp_tool_provider import mcp_tool_provider + from src.plugins.built_in.affinity_flow_chatter.interest_scoring import chatter_interest_scoring_system + from src.plugins.built_in.affinity_flow_chatter.relationship_tracker import ChatterRelationshipTracker - await mcp_tool_provider.initialize(mcp_config) - logger.info("MCP工具提供器初始化成功") + relationship_tracker = ChatterRelationshipTracker(interest_scoring_system=chatter_interest_scoring_system) + chatter_interest_scoring_system.relationship_tracker = relationship_tracker + logger.info("回复后关系追踪系统初始化成功") except Exception as e: - logger.info(f"MCP工具提供器未配置或初始化失败: {e}") + logger.error(f"回复后关系追踪系统初始化失败: {e}") + relationship_tracker = None + """ - # 并行初始化其他管理器 - manager_init_tasks = [] - - # 表情管理器 - manager_init_tasks.append(self._safe_init("表情包管理器", get_emoji_manager().initialize)()) - - # 情绪管理器 - manager_init_tasks.append(self._safe_init("情绪管理器", mood_manager.start)()) - - # 聊天管理器 - manager_init_tasks.append(self._safe_init("聊天管理器", get_chat_manager()._initialize)()) - - # 等待所有管理器初始化完成 - results = await asyncio.gather(*manager_init_tasks, return_exceptions=True) - - # 检查初始化结果 - for i, result in enumerate(results): - if isinstance(result, Exception): - logger.error(f"组件初始化失败: {result}") + # 启动情绪管理器 + await mood_manager.start() + logger.info("情绪管理器初始化成功") # 启动聊天管理器的自动保存任务 asyncio.create_task(get_chat_manager()._auto_save_task()) diff --git a/src/plugin_system/apis/tool_api.py b/src/plugin_system/apis/tool_api.py index 285df7884..82174e642 100644 --- a/src/plugin_system/apis/tool_api.py +++ b/src/plugin_system/apis/tool_api.py @@ -17,12 +17,7 @@ def get_tool_instance(tool_name: str) -> BaseTool | None: plugin_config = None tool_class: type[BaseTool] = component_registry.get_component_class(tool_name, ComponentType.TOOL) # type: ignore - if tool_class: - return tool_class(plugin_config) - - # 如果不是常规工具,检查是否是MCP工具 - # MCP工具不需要返回实例,会在execute_tool_call中特殊处理 - return None + return tool_class(plugin_config) if tool_class else None def get_llm_available_tool_definitions(): @@ -34,17 +29,4 @@ def get_llm_available_tool_definitions(): from src.plugin_system.core import component_registry llm_available_tools = component_registry.get_llm_available_tools() - tool_definitions = [(name, tool_class.get_tool_definition()) for name, tool_class in llm_available_tools.items()] - # 添加MCP工具 - try: - from src.plugin_system.utils.mcp_tool_provider import mcp_tool_provider - - mcp_tools = mcp_tool_provider.get_mcp_tool_definitions() - tool_definitions.extend(mcp_tools) - if mcp_tools: - logger.debug(f"已添加 {len(mcp_tools)} 个MCP工具到可用工具列表") - except Exception as e: - logger.debug(f"获取MCP工具失败(可能未配置): {e}") - - return tool_definitions diff --git a/src/plugin_system/core/tool_use.py b/src/plugin_system/core/tool_use.py index 82a9bf721..789d16ab9 100644 --- a/src/plugin_system/core/tool_use.py +++ b/src/plugin_system/core/tool_use.py @@ -280,11 +280,10 @@ class ToolExecutor: logger.info( f"{self.log_prefix} 正在执行工具: [bold green]{function_name}[/bold green] | 参数: {function_args}" ) - + # 检查是否是MCP工具 try: from src.plugin_system.utils.mcp_tool_provider import mcp_tool_provider - if function_name in mcp_tool_provider.mcp_tools: logger.info(f"{self.log_prefix}执行MCP工具: {function_name}") result = await mcp_tool_provider.call_mcp_tool(function_name, function_args) @@ -297,7 +296,7 @@ class ToolExecutor: } except Exception as e: logger.debug(f"检查MCP工具时出错: {e}") - + function_args["llm_called"] = True # 标记为LLM调用 # 检查是否是二步工具的第二步调用 diff --git a/src/plugin_system/utils/mcp_connector.py b/src/plugin_system/utils/mcp_connector.py deleted file mode 100644 index 66b205f1e..000000000 --- a/src/plugin_system/utils/mcp_connector.py +++ /dev/null @@ -1,233 +0,0 @@ -""" -MCP (Model Context Protocol) 连接器 -负责连接MCP服务器,获取和执行工具 -""" - -from typing import Any - -import aiohttp - -from src.common.logger import get_logger - -logger = get_logger("MCP连接器") - - -class MCPConnector: - """MCP服务器连接器""" - - def __init__(self, server_url: str, api_key: str | None = None, timeout: int = 30): - """ - 初始化MCP连接器 - - Args: - server_url: MCP服务器URL - api_key: API密钥(可选) - timeout: 超时时间(秒) - """ - self.server_url = server_url.rstrip("/") - self.api_key = api_key - self.timeout = timeout - self._session: aiohttp.ClientSession | None = None - self._tools_cache: dict[str, dict[str, Any]] = {} - self._cache_timestamp: float = 0 - self._cache_ttl: int = 300 # 工具列表缓存5分钟 - - async def _get_session(self) -> aiohttp.ClientSession: - """获取或创建aiohttp会话""" - if self._session is None or self._session.closed: - timeout = aiohttp.ClientTimeout(total=self.timeout) - self._session = aiohttp.ClientSession(timeout=timeout) - return self._session - - async def close(self): - """关闭连接""" - if self._session and not self._session.closed: - await self._session.close() - - def _build_headers(self) -> dict[str, str]: - """构建请求头""" - headers = { - "Content-Type": "application/json", - "Accept": "application/json", - } - if self.api_key: - headers["Authorization"] = f"Bearer {self.api_key}" - return headers - - async def list_tools(self, force_refresh: bool = False) -> dict[str, dict[str, Any]]: - """ - 获取MCP服务器提供的工具列表 - - Args: - force_refresh: 是否强制刷新缓存 - - Returns: - Dict[str, Dict]: 工具字典,key为工具名,value为工具定义 - """ - import time - - # 检查缓存 - if not force_refresh and self._tools_cache and (time.time() - self._cache_timestamp) < self._cache_ttl: - logger.debug("使用缓存的MCP工具列表") - return self._tools_cache - - logger.info(f"正在从MCP服务器获取工具列表: {self.server_url}") - - try: - session = await self._get_session() - url = f"{self.server_url}/tools/list" - - async with session.post(url, headers=self._build_headers(), json={}) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"获取MCP工具列表失败: HTTP {response.status} - {error_text}") - return {} - - data = await response.json() - - # 解析工具列表 - tools = {} - tool_list = data.get("tools", []) - - for tool_def in tool_list: - tool_name = tool_def.get("name") - if not tool_name: - continue - - tools[tool_name] = { - "name": tool_name, - "description": tool_def.get("description", ""), - "input_schema": tool_def.get("inputSchema", {}), - } - - logger.info(f"成功获取 {len(tools)} 个MCP工具") - self._tools_cache = tools - self._cache_timestamp = time.time() - - return tools - - except aiohttp.ClientError as e: - logger.error(f"连接MCP服务器失败: {e}") - return {} - except Exception as e: - logger.error(f"获取MCP工具列表时发生错误: {e}") - return {} - - async def call_tool(self, tool_name: str, arguments: dict[str, Any]) -> dict[str, Any]: - """ - 调用MCP服务器上的工具 - - Args: - tool_name: 工具名称 - arguments: 工具参数 - - Returns: - Dict: 工具执行结果 - """ - logger.info(f"调用MCP工具: {tool_name}") - logger.debug(f"工具参数: {arguments}") - - try: - session = await self._get_session() - url = f"{self.server_url}/tools/call" - - payload = {"name": tool_name, "arguments": arguments} - - async with session.post(url, headers=self._build_headers(), json=payload) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"MCP工具调用失败: HTTP {response.status} - {error_text}") - return { - "success": False, - "error": f"HTTP {response.status}: {error_text}", - "content": f"调用MCP工具 {tool_name} 失败", - } - - result = await response.json() - - # 提取内容 - content = result.get("content", []) - if isinstance(content, list) and len(content) > 0: - # MCP返回的是content数组 - text_content = [] - for item in content: - if isinstance(item, dict): - if item.get("type") == "text": - text_content.append(item.get("text", "")) - else: - text_content.append(str(item)) - - result_text = "\n".join(text_content) if text_content else str(content) - else: - result_text = str(content) - - logger.info(f"MCP工具 {tool_name} 执行成功") - return {"success": True, "content": result_text, "raw_result": result} - - except aiohttp.ClientError as e: - logger.error(f"调用MCP工具失败(网络错误): {e}") - return {"success": False, "error": str(e), "content": f"网络错误:无法调用工具 {tool_name}"} - except Exception as e: - logger.error(f"调用MCP工具时发生错误: {e}") - return {"success": False, "error": str(e), "content": f"调用工具 {tool_name} 时发生错误"} - - async def list_resources(self) -> list[dict[str, Any]]: - """ - 获取MCP服务器提供的资源列表 - - Returns: - List[Dict]: 资源列表 - """ - logger.info(f"正在从MCP服务器获取资源列表: {self.server_url}") - - try: - session = await self._get_session() - url = f"{self.server_url}/resources/list" - - async with session.post(url, headers=self._build_headers(), json={}) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"获取MCP资源列表失败: HTTP {response.status} - {error_text}") - return [] - - data = await response.json() - resources = data.get("resources", []) - - logger.info(f"成功获取 {len(resources)} 个MCP资源") - return resources - - except Exception as e: - logger.error(f"获取MCP资源列表时发生错误: {e}") - return [] - - async def read_resource(self, resource_uri: str) -> dict[str, Any]: - """ - 读取MCP资源 - - Args: - resource_uri: 资源URI - - Returns: - Dict: 资源内容 - """ - logger.info(f"读取MCP资源: {resource_uri}") - - try: - session = await self._get_session() - url = f"{self.server_url}/resources/read" - - payload = {"uri": resource_uri} - - async with session.post(url, headers=self._build_headers(), json=payload) as response: - if response.status != 200: - error_text = await response.text() - logger.error(f"读取MCP资源失败: HTTP {response.status} - {error_text}") - return {"success": False, "error": error_text} - - result = await response.json() - logger.info(f"成功读取MCP资源: {resource_uri}") - return {"success": True, "content": result.get("contents", [])} - - except Exception as e: - logger.error(f"读取MCP资源时发生错误: {e}") - return {"success": False, "error": str(e)} diff --git a/src/plugin_system/utils/mcp_tool_provider.py b/src/plugin_system/utils/mcp_tool_provider.py deleted file mode 100644 index 90c921712..000000000 --- a/src/plugin_system/utils/mcp_tool_provider.py +++ /dev/null @@ -1,173 +0,0 @@ -""" -MCP工具提供器 - 简化版 -直接集成到工具系统,无需复杂的插件架构 -""" - -from typing import Any - -from src.common.logger import get_logger -from src.plugin_system.utils.mcp_connector import MCPConnector - -logger = get_logger("MCP工具提供器") - - -class MCPToolProvider: - """MCP工具提供器单例""" - - _instance = None - _initialized = False - - def __new__(cls): - if cls._instance is None: - cls._instance = super().__new__(cls) - return cls._instance - - def __init__(self): - if not MCPToolProvider._initialized: - self.connectors: dict[str, MCPConnector] = {} - self.mcp_tools: dict[str, dict[str, Any]] = {} - """格式: {tool_full_name: {"connector": connector, "original_name": name, "definition": def}}""" - MCPToolProvider._initialized = True - - async def initialize(self, mcp_servers: list[dict]): - """ - 初始化MCP服务器连接 - - Args: - mcp_servers: MCP服务器配置列表 - """ - logger.info(f"初始化MCP工具提供器,共{len(mcp_servers)}个服务器") - - for server_config in mcp_servers: - await self._connect_server(server_config) - - logger.info(f"MCP工具提供器初始化完成,共注册{len(self.mcp_tools)}个工具") - - async def _connect_server(self, config: dict): - """连接单个MCP服务器""" - name = config.get("name", "unnamed") - url = config.get("url") - api_key = config.get("api_key") - enabled = config.get("enabled", True) - - if not enabled or not url: - return - - logger.info(f"连接MCP服务器: {name} ({url})") - - connector = MCPConnector(url, api_key, config.get("timeout", 30)) - self.connectors[name] = connector - - try: - tools = await connector.list_tools() - - for tool_name, tool_def in tools.items(): - # 使用服务器名作前缀 - full_name = f"{name}_{tool_name}" - self.mcp_tools[full_name] = { - "connector": connector, - "original_name": tool_name, - "definition": tool_def, - "server_name": name, - } - - logger.info(f"从{name}获取{len(tools)}个工具") - - except Exception as e: - logger.error(f"连接MCP服务器{name}失败: {e}") - - def get_mcp_tool_definitions(self) -> list[tuple[str, dict[str, Any]]]: - """ - 获取所有MCP工具定义(适配Bot的工具格式) - - Returns: - List[Tuple[str, dict]]: [(tool_name, tool_definition), ...] - """ - definitions = [] - - for full_name, tool_info in self.mcp_tools.items(): - mcp_def = tool_info["definition"] - input_schema = mcp_def.get("input_schema", {}) - - # 转换为Bot的工具格式 - bot_tool_def = { - "name": full_name, - "description": mcp_def.get("description", f"MCP工具: {full_name}"), - "parameters": self._convert_schema_to_parameters(input_schema), - } - - definitions.append((full_name, bot_tool_def)) - - return definitions - - def _convert_schema_to_parameters(self, schema: dict) -> list[tuple]: - """ - 将MCP的JSON Schema转换为Bot的参数格式 - - Args: - schema: MCP的inputSchema - - Returns: - Bot的parameters格式 - """ - from src.plugin_system.base.component_types import ToolParamType - - parameters = [] - properties = schema.get("properties", {}) - required = schema.get("required", []) - - type_mapping = { - "string": ToolParamType.STRING, - "integer": ToolParamType.INTEGER, - "number": ToolParamType.FLOAT, - "boolean": ToolParamType.BOOLEAN, - } - - for param_name, param_def in properties.items(): - param_type = type_mapping.get(param_def.get("type", "string"), ToolParamType.STRING) - description = param_def.get("description", "") - is_required = param_name in required - enum_values = param_def.get("enum", None) - - parameters.append((param_name, param_type, description, is_required, enum_values)) - - return parameters - - async def call_mcp_tool(self, tool_name: str, arguments: dict[str, Any]) -> dict[str, Any]: - """ - 调用MCP工具 - - Args: - tool_name: 工具全名(包含前缀) - arguments: 参数 - - Returns: - 工具执行结果 - """ - if tool_name not in self.mcp_tools: - return {"content": f"MCP工具{tool_name}不存在"} - - tool_info = self.mcp_tools[tool_name] - connector = tool_info["connector"] - original_name = tool_info["original_name"] - - logger.info(f"调用MCP工具: {tool_name}") - - result = await connector.call_tool(original_name, arguments) - - if result.get("success"): - return {"content": result.get("content", "")} - else: - return {"content": f"工具执行失败: {result.get('error', '未知错误')}"} - - async def close(self): - """关闭所有连接""" - for name, connector in self.connectors.items(): - try: - await connector.close() - except Exception as e: - logger.error(f"关闭MCP连接{name}失败: {e}") - - -# 全局单例 -mcp_tool_provider = MCPToolProvider() diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 4a4ba85a0..c5bc0547f 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -580,28 +580,4 @@ enabled_group_chats = [] # 对于白名单中不活跃的私聊,是否允许进行一次“冷启动”问候 enable_cold_start = true # 冷启动后,该私聊的下一次主动思考需要等待的最小时间(秒) -cold_start_cooldown = 86400 # 默认24小时 - -# ===== MCP (Model Context Protocol) 工具服务器配置 ===== -# MCP允许连接外部工具服务器,AI可以调用这些工具来执行各种任务 -# 例如:文件操作、Git操作、数据库查询等 - -# 示例MCP服务器配置(需要取消注释才能启用) -#[[mcp_servers]] -#name = "filesystem" # 服务器名称,工具将以此为前缀(如 filesystem_read_file) -#url = "http://localhost:3000" # MCP服务器地址 -#api_key = "" # API密钥(如果服务器需要认证) -#timeout = 30 # 超时时间(秒) -#enabled = true # 是否启用此服务器 - -# 可以配置多个MCP服务器 -#[[mcp_servers]] -#name = "git_tools" -#url = "http://localhost:3001" -#enabled = true - -# 详细说明: -# 1. MCP服务器需要单独启动,Bot启动后会自动连接 -# 2. 每个服务器提供的工具会自动注册到Bot的工具系统 -# 3. AI会自动发现并在需要时调用这些工具 -# 4. 详细文档请参考: docs/MCP_TOOLS_INTEGRATION.md +cold_start_cooldown = 86400 # 默认24小时 \ No newline at end of file diff --git a/template/model_config_template.toml b/template/model_config_template.toml index b0858c6f4..69e992a96 100644 --- a/template/model_config_template.toml +++ b/template/model_config_template.toml @@ -30,15 +30,6 @@ max_retry = 2 timeout = 30 retry_interval = 10 -[[api_providers]] # MCP SSE协议支持(Model Context Protocol via Server-Sent Events) -name = "MCPProvider" -base_url = "https://your-mcp-server.com" # MCP服务器地址 -api_key = "your-mcp-api-key-here" -client_type = "mcp_sse" # 使用MCP SSE客户端 -max_retry = 2 -timeout = 60 # MCP流式请求可能需要更长超时时间 -retry_interval = 10 - # 内容混淆功能示例配置(可选) [[api_providers]] name = "ExampleProviderWithObfuscation" # 启用混淆功能的API提供商示例 @@ -130,15 +121,6 @@ api_provider = "SiliconFlow" price_in = 4.0 price_out = 16.0 -# MCP SSE模型示例配置 -#[[models]] -#model_identifier = "claude-3-5-sonnet-20241022" # 或其他支持MCP的模型 -#name = "mcp-claude-sonnet" -#api_provider = "MCPProvider" # 对应上面配置的MCP provider -#price_in = 3.0 -#price_out = 15.0 -#force_stream_mode = true # MCP SSE默认使用流式模式 - [model_task_config.utils] # 在麦麦的一些组件中使用的模型,例如表情包模块,取名模块,关系模块,是麦麦必须的模型 model_list = ["siliconflow-deepseek-ai/DeepSeek-V3.1-Terminus"] # 使用的模型列表,每个子项对应上面的模型名称(name) temperature = 0.2 # 模型温度,新V3建议0.1-0.3