Chapter 06
自测题库:合上教程,自己答
前五章建好了 MCP 的结构图:控制权分层(01)、协议机制(02)、双向会话(03)、信任模型(04)、设计与选型(05)。这一章不教新东西,只做一件事——把"读得顺"逼成"答得出"。三层梯度题 + 五道跨章判别 + 一次默写。
怎么用这一章
- 合上前五章,把答案写在纸上或编辑器里,再展开对照
- 第一层考"记得",第二层考"讲得清机制",第三层考"分得清场景"
- 读得顺只到第一层;真正的检验在第三层那五道判别题
- 最后一节合上教程默写架构图——这一步最值钱
一个提醒:直接点开答案等于把这一章当复习读一遍,留不下任何东西。每道题先停下来、自己写一个版本,哪怕写错——写错并被纠正,比"看一眼觉得懂了"记得牢得多。
6.1第一层 · 回忆
原子题,每题一个点。先全部写完,再展开对照。
- host : client : server 的数量关系是什么?client 与 server 是几对几?(01)
- 三大 server primitive 是哪三个?各由谁控制(模型 / 应用 / 用户)?(01)
initialize握手的三步消息,按顺序是哪三条?(02)- JSON-RPC 的 notification 和 request 在报文结构上差哪一个字段?这个差别带来什么后果?(02)
- sampling 的请求方向是 client→server 还是 server→client?(03)
- 截至 2026-06,最新稳定的 spec 版本号是多少?哪一种传输已被弃用、被谁取代?(02 / 05)
- 为什么 stdio server 不能往 stdout 打日志?该打到哪里?(02 / 04)
答案(先做完再展开)
- host : client : server = 1 : N : N;client 与 server 严格 1:1,每个 client 维护一条独立隔离的会话。
- tools(模型控制)、resources(应用控制)、prompts(用户控制)。
- ① client→server
initialize请求;② server→clientinitialize响应;③ client→servernotifications/initialized通知。必须是会话的第一组交互。 - notification 没有
id字段。后果:它不期待、也不会收到响应——所以*/list_changed、进度、取消这类"通知一声即可"的消息都走 notification。 - server→client。server 反过来向 client 请求(借用 client 的 LLM)。把方向记反是最常见的错误。
- 最新稳定版是
2025-11-25。旧的 HTTP+SSE 双端点传输已弃用(2025-03-26起),被 Streamable HTTP 取代。 - 因为 stdio 传输下,stdout 就是 JSON-RPC 线路本身;任何
print()/ 日志写进 stdout 会插入非 JSON 字节、污染消息流,导致连接中断。日志必须打到 stderr。
6.2第二层 · 理解
这些题考"机制为什么成立",答案要给出因果,不能只复述结论。
- 能力为什么要在
initialize时协商,而不是各发各的?运行时用了一个没协商的能力会怎样?(02) - 为什么 sampling 能让 server 不需要自己的 LLM API key?(03)
- 为什么说工具的
description是"可执行的不可信 prompt",而不是普通文档?(04) - 为什么说 MCP 不替代 function calling,而是坐在它下面?去掉 function calling 会怎样?(05 / 01)
- 为什么 spec 禁止把用户的 token 转发给下游服务?(04)
- elicitation 的
requestedSchema为什么被故意限制成扁平结构(不许嵌套)?(03)
答案(先做完再展开)
- 因为 client(host 应用)和 server 各自独立发版、版本不同步。协商让一个最小 server 和一个全功能 client 不必锁版本就能互通——双方只用"成功协商过"的能力。运行时用了没协商的能力是协议违规,对端有权拒绝。
- 因为 sampling 是 server 反过来请 client 跑模型——模型访问权和 API key 都在 client/host 这一侧。server 只发"帮我补全这段"的请求,自己不持有任何 LLM SDK 或密钥,因此天然跨供应商、跨模型。
- 因为模型会读取工具的 metadata(description)来决定怎么用工具,而这段文字用户在 UI 里通常看不到。于是 description 是进入模型上下文的、用户不可见的指令——是代码,不是文档。tool poisoning、line jumping 都利用这一点。
- function calling 是模型能力(模型输出"调用工具 X、参数 Y",但从不执行);MCP 是供给并执行这些工具的基础设施(发现、会话、传输、进程外执行)。模型仍靠 function calling 选工具,MCP 只是把工具目录送进这个机制。去掉 function calling,MCP 没有"谁来决定调用"的入口,什么也调不动。
- 因为转发 token 破坏 audience 校验:下游无法确认 token 是发给它的,审计与限流失效,server 还会沦为数据外泄的代理。spec 规定 server 必须只接受"明确签发给自己"的 token,调用上游要用另一个 token。
- 为了让 client 渲染表单的逻辑保持简单——扁平的原始字段可以无脑生成一个输入框列表。这是有意的约束,不是能力缺口;需要复杂输入时应换设计,而不是往 elicitation 里塞嵌套结构。
6.3第三层 · 判别(跨章场景)
下面五道题,每道给一个具体场景,要你在来自不同章的方案之间做选择,并说出判断依据。这是这份教程真正想训练的能力——把零散的概念用成一张能定位问题的网。图 6.2 标出每道题在考哪几章。
产品需求:用户点一个按钮,助手就把"当前 Jira 看板上所有未完成卡片"拉进上下文供后续提问参考。这件事里——按钮触发、把卡片内容入上下文——更像 tool / resource / prompt 中的哪些?(先停十秒)
展开答案(先自己判断)
对每一步问"谁发起":按钮触发是用户主动 → prompt 风格的入口;把看板内容作为背景注入由应用按按钮规则拉取、稳定喂给模型 → resource(应用控制),不是 tool。若把"拉卡片"做成 tool,决定权就交给了模型,可能在该带时没带。判据始终是图 1.3 那条"谁决定它被调用"的轴。
你在做一个内部脚本助手:单一 LLM 供应商,5 个稳定工具,全部在同一进程里执行,不打算给别的应用复用。该上 MCP 还是直接用原生 function calling?(先停十秒)
展开答案(先自己判断)
直接用 function calling。MCP 坐在 function calling 之上,解决的是跨应用 / 跨供应商复用、运行时发现、进程外执行——这些你都不需要。5 个稳定工具、单应用单供应商,跑一套 server 基建无从摊销。把 tools = 模型控制(01)接到"模型仍用 function calling 选工具"即可。MCP 的门槛大约在 10+ 工具、多供应商、或工具要跨团队复用时才划算(05)。
两个现象各是什么威胁?A:你刚接入一个第三方 server、还没调用任何工具,模型就在回答里执行了一条 chmod -R 0666 ~。B:你本地起的 dev server,被一个你打开的网页驱动着发起了调用。(先停十秒)
展开答案(先自己判断)
A = line jumping:恶意指令在 tools/list 列出工具阶段就注入了模型上下文,发生在任何调用之前,绕过了 human-in-the-loop 审批。B = DNS rebinding:本地 server 没校验 Host / Origin,网页把域名 rebind 到 127.0.0.1 后驱动它——你的 localhost dev server 是个远程攻击面。两者都印证 04 章那句:server 不是被动的工具箱。
你的 server 部署在一个无状态的 Streamable HTTP 后端(每个请求落到哪个实例不固定、不维护会话)。你想用 sampling 让 server 反借宿主的 LLM。能成吗?(先停十秒)
展开答案(先自己判断)
不能可靠地成。sampling 是 server→client 的反向请求,依赖一条有状态、持久的会话(03);无状态传输撑不起反向请求和请求 / 响应关联。这正是 03 章"招牌能力部署最少"的根因——理念上最核心的 sampling,恰恰被无状态扩展架构挡在门外。要么改用有状态会话,要么放弃 sampling、让 server 自带模型。
团队有两个自治 agent 要互相委派任务,每个 agent 内部又各自调用一堆工具。MCP 与 A2A 各管哪一段?能只用其中一个吗?(先停十秒)
展开答案(先自己判断)
A2A 管 agent↔agent 的委派与能力发现;MCP 管每个 agent↔它自己的工具。两者在不同层、正交,通常叠用:agent 之间走 A2A,agent 内部取工具走 MCP。不是二选一,"MCP vs A2A 是协议之争"是常见误解(05)。
合上教程,画出 MCP 的核心拓扑
在纸上画:一个 host、两个 client、两个 server,标出 client 与 server 的数量关系;在 server 下画出三类 primitive 并标注各自的控制方;最后补上那条最容易漏的反向箭头——它从哪指向哪、叫什么名字。
画完再展开对照
翻回起点页图 0逐项核对四个最容易错的点:① client 在 host 内部、与 server 严格 1:1(不是一个 client 接多个 server);② 三类 primitive 的控制方是 tools=模型、resources=应用、prompts=用户;③ 反向箭头是 server→client(sampling / roots / elicitation),方向不能反;④ 两个 server 之间互相隔离、互不可见。哪一项画错或漏了,回对应章节再过一遍——错在哪里,恰好标出你 schema 上的缺口。
如果只带走一句:MCP 把"模型用外部能力"重构成一条有状态、双向、按控制权分层的会话——tools 归模型、resources 归应用、prompts 归用户;server 不只被调用,还能反过来借你的模型、问你的用户;而它最大的风险面,是"描述即指令"这一信任反转。能把这句话拆开讲清每一个分句,你就已经在大多数只读过 quickstart 的人之上了。