todo-full
by Q11v
README.md
# todo-full · 串起手册要点的完整示例
一个 todo list,两条路对比实现,接真 DeepSeek。用一份可运行代码把 MCP 全套手册的要点串起来。
## 覆盖的要点
- **三原语**:MCP server 里 Tools + Resources + Prompts 都有
- **function calling**:MCP client 把工具翻译成 DeepSeek 的 tools,跑工具循环
- **JSON-RPC 原始消息**:`VERBOSE=1` 打印底层 tools/call 请求与响应
- **手搓 vs MCP**:`manual_way.py` 是不用 MCP 的对照组
- **换个 Host 也能跑**:同一个 server 经 `.mcp.json` 接进 Claude Code,代码一行不改
## 文件
| 文件 | 是什么 |
|------|--------|
| `db.py` | 共用的 SQLite 数据库层(两条路都调它) |
| `mcp_server.py` | 路 A:MCP server,三原语齐全 |
| `mcp_client.py` | 路 A:MCP client + DeepSeek 工具循环(主程序) |
| `manual_way.py` | 路 B:手搓 prompt + 解析 + 分发(对照组) |
| `对照说明.md` | 代码 ↔ 手册要点的逐条对照 |
| `pyproject.toml` / `uv.lock` | uv 项目清单与锁文件,统一管理依赖 |
## 跑起来
需要 [uv](https://docs.astral.sh/uv/) 和一个 DeepSeek API key。
```bash
# 首次运行前安装依赖(uv run 也会自动同步,这一步可省略)
uv sync
# 路 A:MCP(主示例)
DEEPSEEK_API_KEY=sk-xxx uv run mcp_client.py
# 路 A + 看底层 JSON-RPC 消息
DEEPSEEK_API_KEY=sk-xxx VERBOSE=1 uv run mcp_client.py
# 路 B:手搓对照
DEEPSEEK_API_KEY=sk-xxx uv run manual_way.py
```
`mcp_client.py` 会自动把 `mcp_server.py` 当子进程拉起(stdio),你不用单独启动它。
依赖统一声明在 `pyproject.toml` 里,`uv run` 会按 `uv.lock` 自动建虚拟环境并安装,无需手动管理。
## 配合 Claude Code 使用
除了跑 `mcp_client.py`(自己当 Host+Client),也可以让 Claude Code 直接当 Host 连这个 server —— 同一个
`mcp_server.py` 一行不用改,这正是「server 不关心对面是谁」的体现。
`.mcp.json` 已经把它注册好了:
```json
{
"mcpServers": {
"todo": {
"command": "uv",
"args": ["run", "--directory", "/path/to/mcp-primer", "mcp_server.py"]
}
}
}
```
在项目目录里启动 `claude` 即可自动加载,`/mcp` 面板能看到连接状态和三原语。用法:
- **Tools**:直接说「加一个任务:写周报」,模型会调 `mcp__todo__add_task`
- **Resources**:`@todo:todos://all` 引用全部任务;`todos://{status}` 是模板资源,
`@todo:todos://done` / `@todo:todos://pending` 同样可读(模板资源不会出现在资源列表里,但能直接取)
- **Prompts**:`daily_review` 也会一并暴露,具体入口见 `/mcp` 面板
### 这条路看不到 JSON-RPC 原始消息
`VERBOSE=1` 是 `mcp_client.py` 自己在应用层打印的,走 Claude Code 这条路不生效。Claude Code 的
`claude --debug mcp --debug-file <path>` 只给连接生命周期的**摘要**,不含协议报文:
```
[DEBUG] MCP server "todo": Successfully connected (transport: stdio) in 313ms
[DEBUG] MCP server "todo": Connection established with capabilities: {"hasTools":true,...}
[DEBUG] ToolSearchTool: selected mcp__todo__add_task
```
实测这份日志里 grep `jsonrpc` / `tools/call` / `tools/list` 命中数为 0。`/mcp` 面板同理,是状态面板不是协议探针。
想看原始报文,就在 Host 和 server 之间插一层 tee —— stdio 传输本质就是两条管道,原样转发不影响功能:
```bash
#!/bin/bash
# mcp_server_debug.sh,记得 chmod +x
DIR="$(cd "$(dirname "$0")" && pwd)"
mkdir -p "$DIR/mcp_logs"
tee -a "$DIR/mcp_logs/in.jsonl" \
| uv run --directory "$DIR" mcp_server.py \
| tee -a "$DIR/mcp_logs/out.jsonl"
```
把 `.mcp.json` 的 `command` 指向这个脚本(`"command": "/path/to/mcp_server_debug.sh", "args": []`),重连后
`in.jsonl`(Host→server)和 `out.jsonl`(server→Host)会记下**全部**方法的报文 —— 不只是 `tools/call`,
`initialize`、`tools/list`、`resources/read` 都在里面,比 `VERBOSE=1` 只覆盖 `tools/call` 更全。
日志是追加写且含任务内容,记得加进 `.gitignore`。
## 说明
- 数据库文件 `todos.db` 首次运行自动创建在项目目录,重启后任务不丢。
- 若访问 DeepSeek 需要代理,脚本已带 `httpx[socks]`,设置 `ALL_PROXY` 即可。
- mcp 锁在 1.x(`mcp>=1.28,<2`):2.0 是大改版、API 不同,示例按 1.x 写。
TDQS
B3.4/5.0
Scored across 3 tools
Disambiguation5/5
Each tool targets a distinct action on a task: adding, completing, or deleting. There is no overlap or ambiguity between them.
Naming Consistency5/5
All tools follow the same verb_noun pattern: add_task, complete_task, delete_task. Naming is uniform and predictable.
Tool Count5/5
With only 3 tools, the set is minimal but each tool serves a clear purpose in the todo domain. The count is well-scoped and appropriate.
Completeness2/5
The set includes create, complete, and delete operations but lacks a way to list or retrieve tasks. This is a significant gap since agents cannot view existing tasks, making the tool surface incomplete for a todo workflow.
Maintenance
ActivitySlowing
ResponsivenessNo issues