这是一个面向学习者的 MCP(Model Context Protocol)完整示例。它不是生产级图书系统,而是一间足够小、可以逐行读懂的“教学图书馆”。
项目使用官方 Python SDK mcp 2.x 和 MCP 2026-07-28 协议。服务端同时兼容旧握手协议客户端。
| MCP 概念 | 本项目中的位置 | 作用 |
|---|---|---|
| Server / Client | server.py / client_demo.py |
能力提供方与能力消费方 |
| Tools | search_books、checkout_book 等 |
允许模型触发计算或副作用 |
| Structured Output | Pydantic 返回模型 | 同时生成 outputSchema 和 structuredContent |
| Tool Annotations | 每个 @server.tool |
声明只读、幂等、破坏性和开放世界提示 |
| Resources | library://catalog |
应用控制的只读上下文 |
| Resource Templates | library://books/{book_id} |
带参数的资源 URI |
| Prompts | make-research-plan |
可发现、可参数化的消息模板 |
| Elicitation | Resolve(ask_checkout_approval) |
绕过模型,直接向用户确认敏感操作 |
| Progress | audit_inventory |
长任务进度通知 |
| Errors | ToolError |
区分可公开业务错误与内部异常 |
| Transports | in-process、stdio、Streamable HTTP | 测试、本地宿主和网络部署 |
| Discovery | list_tools/resources/prompts |
客户端运行时能力发现 |
| Protocol negotiation | client.protocol_version |
v2 自动发现并兼容旧协议 |
src/mcp_library/
├── domain.py # 纯业务层,不依赖 MCP
├── server.py # MCP 能力注册与两种传输入口
└── client_demo.py # 发现、读取、调用、确认和进度处理
tests/
└── test_server.py # 使用进程内传输的协议集成测试
建议按 domain.py → server.py → client_demo.py → tests 的顺序阅读。
本仓库当前验证环境是 Python 3.13 和 mcp 2.1.1。使用当前终端的 Python 安装:
python -m pip install -e ".[dev]"也可以使用 uv:
uv sync确认解释器与 SDK:
python -c "import sys, mcp; print(sys.executable); print(mcp.__file__)"
python -m pip show mcp不启动端口,Client 与 Server 仍经过完整的 MCP 类型和分发层:
$env:PYTHONPATH = "src"
python -m mcp_library.client_demo这个演示会依次完成协议协商、能力发现、资源读取、工具调用、Prompt 获取、借阅确认,以及盘点进度通知。
stdio 适合 Claude Desktop、Codex 等本地宿主拉起子进程。协议消息走标准输入输出,因此服务端不要向 stdout 随意 print,日志应写 stderr。
$env:PYTHONPATH = "src"
python -m mcp_library.server --transport stdio客户端配置示例(路径按实际解释器修改):
{
"mcpServers": {
"teaching-library": {
"command": "E:\\aaa_SpecializedSoftware\\MiniConda\\envs\\python_3_13\\python.exe",
"args": ["-m", "mcp_library.server", "--transport", "stdio"],
"cwd": "E:\\program\\agent\\0000personal-projects\\08MCP",
"env": {"PYTHONPATH": "src"}
}
}
}终端一:
$env:PYTHONPATH = "src"
python -m mcp_library.server --transport streamable-http --host 127.0.0.1 --port 8000终端二:
$env:PYTHONPATH = "src"
python -m mcp_library.client_demo --url http://127.0.0.1:8000/mcp网络部署时需要进一步增加 HTTPS、认证、Host/Origin 校验、限流、超时和持久化存储。本项目只监听 127.0.0.1,不应直接暴露到公网。
服务启动后,可用 Inspector 检查 schema 和手动发起调用:
npx -y @modelcontextprotocol/inspector连接 Streamable HTTP 地址 http://127.0.0.1:8000/mcp。Inspector 是独立的 Node 工具,因此首次运行需要 Node.js 和联网下载。
- Tool:模型决定何时调用;适合搜索、计算、写入或外部 API。
- Resource:应用决定何时提供;适合文件、记录、配置和稳定上下文。
- Prompt:用户或应用选择模板;适合固化高质量工作流提示。
不要仅因为某个 Python 函数容易写,就把它注册成 Tool。涉及写操作时应最小化权限、明确注解,并在真正执行前确认。
FastMCP在 SDK v2 中更名为MCPServer。- 默认 Client 会先尝试
server/discover;client.protocol_version可查看协商结果。 - 现代协议没有长期会话和服务端主动回调通道。
Resolve(...)可把 elicitation 变成多轮请求结果,在新旧协议中使用同一套工具实现。- 旧式
ctx.elicit()、sampling、roots 和协议级 logging 属于旧协议时代能力;学习遗留系统时仍会遇到,但不应作为新项目主路径。
SDK 会隐藏普通未处理异常,只向客户端返回通用工具错误,以免泄露堆栈或内部数据。可预期、可以公开的业务错误应转换为 ToolError。不要把密钥、数据库异常或内部路径放进 ToolError。
ToolAnnotations 是给客户端和模型的提示,不是权限控制。生产环境仍需独立实现身份认证、授权、参数校验、审计和速率限制。
python -m pytest -q测试不占用端口,覆盖能力发现、结构化输出、参数化资源、elicitation、副作用、进度通知和错误结果。
- 增加
library://loans/{member_id}资源模板。 - 为搜索加入游标分页,并观察 list API 自身的分页结构。
- 把内存
Library替换为 SQLite,同时保持 MCP 层不变。 - 给 HTTP 服务增加 OAuth 资源服务器配置。
- 编写一个会拒绝借阅的 elicitation callback,并断言库存不变化。
- 人为抛出普通异常,对比它与
ToolError的客户端结果和服务端日志。