loading…
Search for a command to run...
loading…
A local skill assistant based on the Model Context Protocol that integrates with the Volcengine Doubao API to provide tools like calculators and weather queries
A local skill assistant based on the Model Context Protocol that integrates with the Volcengine Doubao API to provide tools like calculators and weather queries. It features a Web interface and a modular architecture that allows developers to easily register and deploy custom local AI skills.
一个基于 MCP (Model Context Protocol) 协议的本地技能助手,支持计算器、天气查询等自定义技能,提供 Web 界面和 API 接口。
..
├── .env # 大模型 API 配置
├── chat_history.db # SQLite 对话历史数据库(自动生成)
├── index.html # 前端 Web 界面
├── main.py # 主入口(命令行界面)
├── mcp_server.py # MCP 服务端(核心)
├── server.py # Flask 后端服务
├── requirements.txt # 依赖清单
├── README.md # 项目说明
├── tree.txt # 目录结构
├── client/ # 客户端目录
│ ├── doubao_mcp_client.py # 豆包 API 客户端
│ └── __init__.py
├── config/ # 配置目录
│ ├── settings.py # 全局配置
│ └── __init__.py
└── skills/ # 技能实现目录
├── calculator.py # 计算器技能
├── weather.py # 天气查询技能
├── web_search/ # 网络搜索技能目录
│ └── web_search.py # DuckDuckGo搜索实现
| └── SKILL.md # skill描述
| └── _init_.py
└── __init__.py
后端框架:Python + Flask 构建 Web 服务,提供 RESTful API 与 SSE 流式输出接口
AI 协议与模型调用:基于 OpenAI 兼容 SDK 对接大模型 API,支持豆包等 OpenAI 格式模型接入
核心协议:MCP(Model Context Protocol)实现工具调用标准化,统一技能注册与调度
异步架构:asyncio 异步处理 + 线程池隔离,解决 Flask 同步环境下异步调用阻塞问题
数据持久化:SQLite 实现多会话对话上下文存储,支持会话管理与历史加载
技能插件化:模块化技能系统,支持计算器、天气、网络搜索等可插拔工具扩展
前端:原生 HTML/JS 实现 Web 交互界面,支持 Markdown 渲染、流式打字效果、思维链展示
工程化 :api变量配置(.env)、依赖管理(uv/pip)、错误重试与降级机制、工具调用缓存
✅ 稳定的异步处理 - 修复了Flask路由中直接使用asyncio.run()的问题,使用线程池执行异步函数
✅ 对话历史持久化 - 使用 SQLite 存储对话历史,服务重启不丢失,支持多会话管理
✅ 工具调用容错 - 自动重试机制,工具调用失败时降级到模型直接回答
✅ MCP 工具缓存 - 首次获取工具列表后缓存,减少重复初始化开销
✅ 流式输出 - 实现了完整的SSE流式接口,支持逐字输出体验
✅ 工具调用提示 - 当调用技能时,会显示"【调用了工具:{工具名称}】"的提示信息
✅ 多端支持 - 提供Web界面和命令行界面两种交互方式
✅ 丰富的技能 - 内置计算器、天气查询和网络搜索技能
✅ 技能管理 - 前端可视化技能管理,可自由开关技能
✅ Markdown渲染 - 支持Markdown格式的回复,支持代码高亮、表格、列表、数学公式等
✅ 思维链展示 - 可折叠的AI思考过程展示,便于理解推理逻辑
✅ 多会话管理 - 支持创建多个独立对话,每个对话独立保存历史
✅ 历史对话加载 - 切换会话时自动加载历史对话,完整记录交互过程
安装 uv
# Windows
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm https://astral.sh/uv/install.ps1 | iex
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
克隆项目
git clone https://github.com/taffy123d/Doubao-MCP-agent
cd <项目目录>
创建虚拟环境
uv venv
安装依赖
uv sync
克隆项目
git clone https://github.com/taffy123d/Doubao-MCP-agent
cd <项目目录>
创建虚拟环境
python -m venv venv
激活虚拟环境
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate
安装依赖
pip install -r requirements.txt
.env 文件中填写 API 密钥:# OpenAI 兼容格式的 API 配置
OPENAI_API_KEY=你的API密钥
OPENAI_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
OPENAI_MODEL=你的模型ID
uv run server.py
#或者
python server.py
http://localhost:5000http://localhost:5000/api/*uv run main.py
#或者
python main.py
clear 或 清除历史 可以清除对话历史exit、quit 或 退出 可以退出程序| 接口 | 方法 | 说明 |
|---|---|---|
/ |
GET | 前端页面 |
/api/health |
GET | 健康检查 |
/api/tools |
GET | 获取技能列表 |
/api/config |
GET | 获取配置 |
/api/config |
POST | 保存配置 |
/api/test-connection |
POST | 测试 API 连接 |
/api/chat |
POST | 聊天(支持对话历史) |
/api/chat/stream |
POST | 流式聊天(SSE) |
/api/chat/clear |
POST | 清除对话历史 |
/api/sessions |
GET | 获取所有会话列表 |
/api/sessions/<id> |
DELETE | 删除指定会话 |
/api/sessions/<id>/history |
GET | 获取会话历史记录 |
curl -X POST http://localhost:5000/api/chat \
-H "Content-Type: application/json" \
-d '{
"api_key": "你的API密钥",
"model": "你的模型ID",
"base_url": "https://ark.cn-beijing.volces.com/api/v3",
"message": "北京天气",
"session_id": "default"
}'
curl -X POST http://localhost:5000/api/chat/stream \
-H "Content-Type: application/json" \
-d '{
"api_key": "你的API密钥",
"model": "你的模型ID",
"base_url": "https://ark.cn-beijing.volces.com/api/v3",
"message": "北京天气",
"session_id": "default"
}'
curl -X POST http://localhost:5000/api/chat/clear \
-H "Content-Type: application/json" \
-d '{
"session_id": "default"
}'
配置 API
聊天
计算 123+456北京天气搜索 最新AI新闻技能管理
多会话管理
查看结果
运行程序
python main.py
输入问题
计算 123+456北京天气查看结果
clear 或 清除历史 可以清除对话历史在 skills/ 目录下创建新的技能文件,例如 my_skill.py:
"""我的自定义技能"""
from mcp.server.fastmcp import FastMCP
def register_my_skill(mcp: FastMCP):
"""注册技能到 MCP 服务"""
@mcp.tool()
def my_skill(param1: str, param2: int = 1) -> str:
"""
我的自定义技能描述
示例:my_skill(param1="值", param2=2)
Args:
param1: 参数1描述
param2: 参数2描述(默认值)
Returns:
技能执行结果
"""
try:
# 技能逻辑实现
result = f"处理结果: {param1} - {param2}"
return result
except Exception as e:
return f"处理失败: {str(e)}"
编辑 skills/__init__.py,添加新技能的注册函数:
from .calculator import register_calculator_tool
from .weather import register_weather_tool
from .my_skill import register_my_skill
__all__ = [
"register_calculator_tool",
"register_weather_tool",
"register_my_skill"
]
编辑 mcp_server.py,添加新技能的注册:
from skills import register_calculator_tool, register_weather_tool, register_my_skill
# 注册所有技能工具
register_calculator_tool(mcp)
register_weather_tool(mcp)
register_my_skill(mcp) # 添加这一行
重新启动 MCP 服务和后端服务,新技能即可使用。
register_xxx_tool 格式@mcp.tool() 装饰对于功能较复杂的技能,建议创建独立的 skill 目录,包含技能实现和 SKILL.md 描述文件。
skills/
└── my_complex_skill/ # skill 目录
├── __init__.py # 导出配置(必选)
├── my_skill.py # 技能实现(必选)
└── SKILL.md # skill 描述文档(必选)
在 skills/ 目录下创建新的 skill 目录,例如 skills/my_complex_skill/
my_skill.py"""我的复杂技能实现"""
from mcp.server.fastmcp import FastMCP
from duckduckgo_search import AsyncDuckDuckGoSearcher # 示例依赖
def register_my_complex_skill(mcp: FastMCP):
"""注册复杂技能到 MCP 服务"""
@mcp.tool()
async def my_complex_skill(query: str, limit: int = 5) -> str:
"""
我的复杂技能描述
Args:
query: 查询关键词
limit: 返回结果数量,默认5
Returns:
格式化的搜索结果
"""
try:
async with AsyncDuckDuckGoSearcher() as searcher:
results = await searcher.atext(query, max_results=limit)
# 处理并返回结果
return f"找到 {len(results)} 条结果..."
except Exception as e:
return f"搜索失败: {str(e)}"
__init__.py 导出配置"""my_complex_skill - 我的复杂技能"""
from .my_skill import register_my_complex_skill
__all__ = ["register_my_complex_skill"]
SKILL.md 描述文档# 我的复杂技能
## 功能描述
一句话描述技能功能...
## 使用场景
### ✅ 适用场景
- 场景1
- 场景2
## 参数说明
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| query | string | 是 | - | 查询关键词 |
## 使用示例
```python
# 示例1
my_complex_skill(query="关键词")
| 错误类型 | 处理方式 |
|---|---|
| 网络错误 | 返回友好的错误提示 |
### 步骤 2:更新 skills/__init__.py
```python
from .calculator import register_calculator_tool
from .weather import register_weather_tool
from .web_search import register_web_search_tool
from .my_complex_skill import register_my_complex_skill # 新增
__all__ = [
"register_calculator_tool",
"register_weather_tool",
"register_web_search_tool",
"register_my_complex_skill" # 新增
]
from skills import (
register_calculator_tool,
register_weather_tool,
register_web_search_tool,
register_my_complex_skill # 新增
)
# 注册所有技能工具
register_calculator_tool(mcp)
register_weather_tool(mcp)
register_web_search_tool(mcp)
register_my_complex_skill(mcp) # 新增
如果新 skill 需要额外的 Python 包,在使用 uv add 导入或requirements.txt 中添加:
uv add 包名称
或
包名称 >=版本号 #requirements.txt
然后运行:
uv sync
# 或
pip install 包名称
重新启动服务,新技能即可使用。
| 字段 | 必填 | 说明 |
|---|---|---|
| # 标题 | 是 | 技能名称 |
| ## 功能描述 | 是 | 一句话说明技能作用 |
| ## 使用场景 | 建议 | 列出适用场景 |
| ## 参数说明 | 建议 | 表格形式说明参数 |
| ## 使用示例 | 建议 | 代码和对话示例 |
| ## 返回结果格式 | 建议 | 说明返回内容结构 |
| ## 异常处理 | 建议 | 错误处理方式 |
| ## 注意事项 | 建议 | 使用注意点 |
计算 (10+5)*2上海天气 或 北京天气 3天搜索 Python最新版本 或 搜索 今天科技新闻ddgs 库(pip install duckduckgo-search)chat_history.db 文件权限,确保可读写项目使用 SQLite 数据库持久化对话历史:
chat_history.db(项目根目录,首次运行自动生成)CREATE TABLE messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT NOT NULL, -- 会话ID,支持多会话隔离
role TEXT NOT NULL, -- 角色(user/assistant/tool)
content TEXT NOT NULL, -- 消息内容
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
sqlite3 chat_history.db "SELECT * FROM messages ORDER BY timestamp DESC LIMIT 10;"
httpx 直接 HTTP 请求改为 openai>=1.0.0 SDK 方式DOUBAO_API_KEY → OPENAI_API_KEYDOUBAO_ENDPOINT_ID → OPENAI_MODELDOUBAO_BASE_URL → OPENAI_BASE_URL(去掉了 /chat/completions 后缀)title、default 等豆包 API 不支持的字段_msg_to_dict() 函数,正确处理 OpenAI SDK 返回的 ChatCompletionMessage 对象_msg_to_dict() 辅助函数,统一消息格式转换chat() 路由的异常处理和日志输出Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"doubao-mcp-agent": {
"command": "npx",
"args": []
}
}
}