跳到主要内容

03 · 服务器三件套:Tools / Resources / Prompts

服务器通过三种**原语(primitive)**给模型提供能力。这一章把三件套讲透,并点出 draft 在无状态约束下对它们做的调整(缓存字段、显式状态 handle、订阅式变更通知)。

3.1 三件套与“谁来控制”

理解三件套的最好角度,是问“谁决定它何时被用”(docs/specification/draft/server/index.mdx:15):

原语谁控制一句话例子
Prompts用户控制用户主动选的模板/指令斜杠命令、菜单项
Resources应用控制应用塞给模型的上下文数据文件内容、Git 历史
Tools模型控制暴露给 LLM 自主调用的动作调 API、写文件

记忆口诀:Prompts 给用户点、Resources 给应用塞、Tools 给模型调。 这条控制层级决定了它们在 UI 和安全策略上的不同待遇。

3.2 Tools(工具)——给模型的“手”

工具让模型去做事。它是模型控制的:LLM 根据上下文自己决定调哪个、传什么参数(docs/specification/draft/server/tools.mdx:22)。但规范划了条安全红线——应当始终有人在回路里,能否决工具调用(docs/specification/draft/server/tools.mdx:30)。

两个核心方法:

  • tools/list — 列出可用工具,每个工具带 name + description + inputSchema(一份 JSON Schema,描述参数)(docs/specification/draft/server/tools.mdx:78)。
  • tools/call — 调用,回 content(非结构化内容数组)和/或 structuredContent(结构化 JSON)(docs/specification/draft/server/tools.mdx:134)。

输入输出 schema

工具用 JSON Schema(默认方言 2020-12)描述 inputSchema,还可以选配 outputSchema。给了 outputSchema 时,服务器必须返回符合它的 structuredContent,客户端据此校验(docs/specification/draft/server/tools.mdx:501)。这让“工具返回值”从一坨文本升级成有类型、可校验的结构化数据。

无参工具的写法有个推荐惯例(docs/specification/draft/server/tools.mdx:293):用 { "type": "object", "additionalProperties": false } 明确“只收空对象”。

无状态下的有状态工具:显式 handle

这里是无状态原则落到工具上的体现(docs/specification/draft/server/tools.mdx:675)。MCP 没有协议级会话,服务器不能靠“连接”把前后两次调用关联起来。要维持跨调用的状态(购物车、浏览器上下文、数据库事务),服务器应当:从创建工具返回一个显式 handle,后续调用把它当普通参数传回来。

// → tools/call:创建购物车
{ "name": "create_basket", "arguments": {} }
// ← 结果里返回一个 handle
{ "structuredContent": { "basket_id": "bsk_a1b2c3" } }

// → tools/call:后续操作带着 handle
{ "name": "add_item", "arguments": { "basket_id": "bsk_a1b2c3", "sku": "..." } }

设计 handle 时规范提醒几点:对鉴权服务器,handle 是“名字”不是“凭证”,每次都要校验调用者授权;handle 要不透明(别编码内部结构)、有有限生命周期、过期时返回能让模型自救的执行错误(docs/specification/draft/server/tools.mdx:713)。

x-mcp-header:把参数镜像进 HTTP 头

一个偏底层但巧妙的特性:工具可以在某个参数的 schema 里标 x-mcp-header,让客户端在 Streamable HTTP 上把这个参数值同时镜像到一个 Mcp-Param-{name} HTTP 头docs/specification/draft/server/tools.mdx:333)。这样负载均衡/代理/WAF 不必解析请求体就能按参数路由。约束很严(仅原始类型、不得是 number、不得含控制字符、必须从 schema 根静态可达),违反则客户端必须把该工具从 tools/list 结果里剔除。安全提醒:别把密码/密钥/PII 标成 header,因为头对中间件可见。

3.3 Resources(资源)——喂给模型的“资料”

资源是服务器暴露的上下文数据,每个由一个 URI 唯一标识(docs/specification/draft/server/resources.mdx:7)。它是应用驱动的——由 host 决定怎么把这些数据并入上下文(树形选择器、搜索过滤、或按启发式自动包含)。

核心方法:

  • resources/list — 列资源。
  • resources/read — 按 URI 读内容,回 contents(文本或 base64 二进制)。
  • resources/templates/list — 列资源模板:用 URI 模板(如 file:///{path})暴露参数化资源(docs/specification/draft/server/resources.mdx:180)。

资源能力声明两个可选开关(docs/specification/draft/server/resources.mdx:42):listChanged(清单变了发通知)和 subscribe(支持针对单个资源的更新通知)。

常见 URI scheme(docs/specification/draft/server/resources.mdx:365):https://(客户端可自行从网上取,不必经服务器中转)、file://(像文件系统,但不必真映射到磁盘)、git://、以及自定义 scheme。

一个易错点(docs/specification/draft/server/resources.mdx:409):资源不存在时服务器必须-32602 错误,不得回一个空的 contents 数组——空数组语义含糊(到底是“存在但没内容”还是“不存在”)。

3.4 Prompts(提示词)——给用户点的“快捷指令”

提示词是服务器暴露的结构化消息模板用户控制——意图是让用户显式选用,典型形态是斜杠命令(docs/specification/draft/server/prompts.mdx:20)。

核心方法:

  • prompts/list — 列提示词,每个带 arguments(可定制参数列表)。
  • prompts/get — 取某个提示词,传入 arguments,回一组 messages(user/assistant 角色 + 内容)(docs/specification/draft/server/prompts.mdx:121)。

注意措辞精确:“用户控制”指的是谁决定何时用,不是谁写的内容——内容由服务器定义(docs/specification/draft/server/prompts.mdx:24)。

3.5 三件套的共性

三个原语在协议形态上高度对称,记住共性能省一半脑力:

  • 都有 */list都支持分页cursor / nextCursor)。
  • list 结果都可缓存:draft 新增的 CacheableResult 要求 list 类结果带 ttlMs(新鲜度提示,毫秒)和 cacheScope"public"/"private",控制共享中间件能否缓存)(changelog docs/specification/draft/changelog.mdx:36schema/draft/schema.ts:1065)。
  • 都有 listChanged 变更通知:清单变了,服务器发 notifications/{tools,prompts,resources}/list_changed——但只发给那些通过 subscriptions/listen 订阅了对应类型的客户端(见 05 章)。
  • 都内容多态:文本/图片/音频/资源链接/内嵌资源五种内容块通用,且都支持 annotations(audience、priority、lastModified)(docs/specification/draft/server/resources.mdx:335)。
  • list 结果都不得随连接变化(无状态原则),但可以随请求携带的授权变化——因为凭证是每请求输入,不是连接状态(docs/specification/draft/server/tools.mdx:64)。

关于 Utilities 里的“日志”:旧版把 Logging 当作常规横切工具,但 draft 已把它与 Sampling/Roots 一并废弃docs/specification/draft/changelog.mdx:73,SEP-2577),并移除了 logging/setLevel 方法(changelog :20)——日志级别改为每请求在 _meta.io.modelcontextprotocol/logLevel 里声明(见 01 章 §1.6)。新实现应改用 stderr(stdio)或 OpenTelemetry,本章 notifications/message 等日志通知按此理解。

代码地图

主题文件路径符号 / 锚点
三件套总览与控制层级docs/specification/draft/server/index.mdxcontrol hierarchy 表
工具定义、调用、错误、状态 handledocs/specification/draft/server/tools.mdxToolCallToolRequest、Stateful Tools
工具相关类型schema/draft/schema.tsToolCallToolResultToolAnnotations
资源、模板、订阅docs/specification/draft/server/resources.mdxResourceResourceTemplate
资源类型schema/draft/schema.tsResourceResourceTemplateResourceContents
提示词docs/specification/draft/server/prompts.mdxPromptPromptMessage
可缓存结果schema/draft/schema.tsCacheableResult