第 12 章 / 共 12 章
实战:把笔记库服务做完并自评
12.1 一张完整的任务卡
前面十一章是拆开讲的。这一章把它们合成一件事:独立做完一个可以交付的 MCP Server。
下面这张任务卡就是验收标准。不要一次做完,按四轮推进,每一轮都跑一次 Inspector 确认没坏。
任务:笔记库服务 v1.0
为本地 Markdown 笔记目录提供一个 MCP Server,让 AI 助手能在对话中自主搜索、读取和追加笔记,并且不会访问笔记目录之外的任何文件。
第一轮:能跑(对应第 2、4 章)
| 要做的 | 验收 |
|---|---|
search_notes 工具 | Inspector 里能调用并返回命中行 |
read_note 工具 | 能按文件名返回全文 |
notes://index 资源 | 资源标签页能读到目录清单 |
用 logging 而不是 print | 全局搜索 print( 结果为零 |
第二轮:调用得准(对应第 5、6 章)
| 要做的 | 验收 |
|---|---|
| 三个能力的描述都回答了「搜什么/返回什么/何时用」 | 拿描述给一个不了解项目的人看,他能说出什么时候该用 |
参数约束写进 Annotated[..., Field(...)] | tools/list 的 schema 里能看到 minimum/maximum/enum |
只读工具标了 read_only_hint=True | tools/list 里有 readOnlyHint: true |
| 返回类型是 Pydantic 模型 | 返回里有结构化内容,不是被包了一层的裸字符串 |
| 结果超过上限时截断并给引导 | 造一个高频词,确认返回里带「建议换更具体的词」 |
错误用 ToolError 抛出 | 读一个不存在的文件,模型端能看到完整提示 |
第三轮:不会崩、有状态(对应第 7 章)
| 要做的 | 验收 |
|---|---|
| lifespan 里建索引,工具走索引 | stderr 里「building index」只出现一次 |
| 笔记目录从环境变量读,带默认值 | 不设变量也能启动 |
加 reload_index 工具 | 改一篇笔记后调用它,搜索能搜到新内容 |
第四轮:别人敢装(对应第 9、11 章)
| 要做的 | 验收 |
|---|---|
| 路径边界校验 | 传 ../../../etc/passwd 被拒绝 |
append_note 做成两步(先预览再执行) | 预览不写文件,执行需要带上预览返回的 id |
接进 Claude Code,--scope project | /mcp 显示已连接,.mcp.json 里没有硬编码的绝对路径 |
| README 四要素齐全 | 给别人看,他能回答「敢不敢装」 |
| 过一遍 11.5 的清单 | 每一项都有明确的是或否 |

12.2 自评量表
每一项按「做到了 / 部分做到 / 没做」三档打分。低于 12 分的,先别发布。
| 维度 | 2 分 | 1 分 | 0 分 |
|---|---|---|---|
| 能跑 | 换一台机器 clone 下来照 README 就能跑起来 | 自己机器上能跑 | 要口头解释才能跑起来 |
| 原语选对 | 每个能力都能说出为什么是这个原语 | 大致合理 | 全做成了工具,没想过 |
| 描述质量 | 别人只读描述就知道何时该用 | 说清了做什么,没说何时用 | 一句话带过 |
| 参数设计 | 约束在 schema 里,名字无歧义 | 名字还行,约束在函数体里 | q、n、data |
| 错误设计 | 分清了给模型看的和给自己看的 | 都能返回错误,但信息笼统 | 直接崩 |
| 返回设计 | 有截断、有引导、结构化 | 会截断 | 全量返回 |
| 状态管理 | 准备工作在 lifespan,跨调用用 handle | 有 lifespan | 每次调用重建 |
| 可调试性 | 日志够定位问题,故障能在五分钟内查到 | 有日志 | 靠猜 |
| 安全边界 | 过了 11.5 全部清单 | 做了路径校验 | 没想过 |
| 文档 | README 四要素齐全,说清了数据访问范围 | 有安装说明 | 只有代码 |
12.3 常见的三种「差一点」
改到这一步的 server,通常卡在三个地方,各自的症状很好认。
第一种:能调用,但模型从不主动调用。 症状是你必须点名工具它才用。这不是连接问题,是第 5 章的问题——描述里缺少「什么时候该用」的触发线索。回去改描述,改完用 9.5 的第三步验证(不点名工具,只描述需求)。
第二种:调用了,但上下文很快就满。 症状是聊几轮之后模型开始变笨、忘事。这是第 6 章的问题——返回体没有节制。加截断、加详略开关、把返回里模型用不上的字段删掉。
第三种:自己用没问题,给别人就崩。 症状永远是路径和环境。这是第 9 章的问题——绝对路径、显式环境变量、能自己找到依赖的启动命令,三件事漏了一件。
12.4 接下来学什么
做完这个 server,有三条自然的延伸路径,按投入产出排序:
第一条,把它做深。 给现有的 server 加提示词原语,把你反复输入的那段整理指令固化下来;给工具加进度上报,让长任务在宿主里有反馈;把索引换成真正的全文检索。这条路不需要新知识,只需要把前十一章的东西用得更熟。
第二条,把它做远。 按第 10 章走一遍 Streamable HTTP 部署,加上认证,让同事能用。这条路会逼你真正搞懂授权那一块,也是最能体现「从玩具到服务」差别的一段。
第三条,换个方向:从写 server 到写 client。 你现在理解协议的两端了,写一个自己的 MCP client(或者用 Agent SDK 把 agent 装进自己的程序)是很自然的下一步。回头看第 3 章那些 JSON,你会发现另一半其实并不神秘。
不管走哪条,有一件事请保持:每次动手前,用官方文档核对一遍版本相关的东西。 这份教程里的每个版本号、每条命令、每个字段名,都是 2026 年 9 月核实的。协议还在快速演进——你已经在第 2 章和第 3 章亲眼见过一次破坏性变更长什么样了。教程会过期,但「先核对再动手」这个习惯不会。
12.5 最后一个检查点
不看教程,回答这五个问题。答不上来的,回对应章节再读一遍:
- 一个能力该做成工具还是资源,判断依据是什么?(第 4 章)
- 模型决定「要不要调用你的工具」时,读到的是哪几个字段?(第 5 章)
- 你在工具里抛一个普通异常,模型会收到什么?想让它收到你写的提示,该怎么做?(第 6 章)
- 2026-07-28 之后,需要跨调用保持的状态应该放在哪里?(第 3、7 章)
- 工具标注能提供什么,不能提供什么?(第 11 章)
五个都答得上来,你已经不需要这份教程了。去写你自己的那个 server 吧。