Liking cljdoc? Tell your friends :D

im.ttalk.agent.mcp.protocol

MCP 协议的纯数据层:方法名、_meta 键、错误码、版本、能力、信封构造与解析。

零 I/O、零状态——传输在 mcp.transport,会话与回退策略在 mcp.client, 服务端派发在 mcp.server。本 ns 只回答「这条消息长什么样」。

两个时代(本 ns 同时认识,因为编解码是共用的)

规范 2026-07-28(SEP-2575 + SEP-2567)把 MCP 改成了无状态协议:

legacy(≤ 2025-11-25)modern(≥ 2026-07-28)
开场initialize 握手 + notifications/initialized没有握手,server/discover 可选
版本/能力/身份握手时协商一次,之后靠会话记住每条请求自带,放在 params._meta 里
HTTP 会话Mcp-Session-Id没有会话;Mcp-Method / Mcp-Name 变必填头
结果直接是 result 对象多一个 resultType(complete / input_required)
roots / sampling / logging正常特性全部 deprecated(SEP-2577)

「服务器不得从上一条请求推断任何东西」是 modern 的核心不变量——所以 request 每次都把版本与能力塞进 _meta,这不是冗余,是协议要求。

一处必须照做否则静默出错的地方

modern 的 HTTP 传输要求 MCP-Protocol-Version 头与 _meta 里的版本一致, 不一致 server 回 -32020 HeaderMismatch。所以「往信封里写版本」和「往头里写 版本」必须是同一个值的两次投影,不能各写各的——transport 那侧因此从信封里 反读版本(protocol-version-of),而不是自己记一份。

MCP 协议的**纯数据层**:方法名、`_meta` 键、错误码、版本、能力、信封构造与解析。

**零 I/O、零状态**——传输在 `mcp.transport`,会话与回退策略在 `mcp.client`,
服务端派发在 `mcp.server`。本 ns 只回答「这条消息长什么样」。

## 两个时代(本 ns 同时认识,因为编解码是共用的)

规范 `2026-07-28`(SEP-2575 + SEP-2567)把 MCP 改成了**无状态**协议:

| | legacy(≤ 2025-11-25) | modern(≥ 2026-07-28) |
|---|---|---|
| 开场 | `initialize` 握手 + `notifications/initialized` | **没有握手**,`server/discover` 可选 |
| 版本/能力/身份 | 握手时协商一次,之后靠会话记住 | **每条请求自带**,放在 `params._meta` 里 |
| HTTP 会话 | `Mcp-Session-Id` | **没有会话**;`Mcp-Method` / `Mcp-Name` 变必填头 |
| 结果 | 直接是 result 对象 | 多一个 `resultType`(`complete` / `input_required`) |
| roots / sampling / logging | 正常特性 | **全部 deprecated**(SEP-2577) |

「服务器不得从上一条请求推断任何东西」是 modern 的核心不变量——所以
`request` 每次都把版本与能力塞进 `_meta`,这不是冗余,是协议要求。

## 一处**必须**照做否则静默出错的地方

modern 的 HTTP 传输要求 `MCP-Protocol-Version` 头与 `_meta` 里的版本**一致**,
不一致 server 回 `-32020 HeaderMismatch`。所以「往信封里写版本」和「往头里写
版本」必须是同一个值的两次投影,不能各写各的——`transport` 那侧因此从信封里
反读版本(`protocol-version-of`),而不是自己记一份。
raw docstring

best-legacy-versionclj

(best-legacy-version supported)

从 server 报的 supported 列表里挑一个我们也支持的 legacy 版本; 挑不出就退到 2025-11-25(legacy 里最新的那个,initialize 本来就会再协商一次)。

从 server 报的 `supported` 列表里挑一个**我们也支持**的 legacy 版本;
挑不出就退到 `2025-11-25`(legacy 里最新的那个,`initialize` 本来就会再协商一次)。
sourceraw docstring

client-infoclj

(client-info)
(client-info name version)

Implementation:自报家门。协议明说它不做安全判断,只用于显示与排查。

`Implementation`:自报家门。协议明说它**不做安全判断**,只用于显示与排查。
sourceraw docstring

default-client-capabilitiesclj

我们作为 client 声明的能力。

只报真支持的:elicitation 我们能接(mcp.client 有回调挂点), roots / sampling 是 2026-07-28 起 deprecated 的特性,不报。 extensions 里报 MCP Apps——它只是说「UI 资源我认得」,渲染在前端。

我们作为 client 声明的能力。

**只报真支持的**:`elicitation` 我们能接(`mcp.client` 有回调挂点),
`roots` / `sampling` 是 2026-07-28 起 deprecated 的特性,不报。
`extensions` 里报 MCP Apps——它只是说「UI 资源我认得」,渲染在前端。
sourceraw docstring

encodeclj

(encode msg)

消息 map → JSON 文本。

消息 map → JSON 文本。
sourceraw docstring

error-codesclj

JSON-RPC 标准码 + MCP 在 -32020..-32099 这段里定义的码。

-32000..-32019 是历史遗留段,规范明令新实现不得再分配; -32002(资源不存在)只在读老 server 时接受,自己不发。

JSON-RPC 标准码 + MCP 在 `-32020..-32099` 这段里定义的码。

**`-32000..-32019` 是历史遗留段**,规范明令新实现不得再分配;
`-32002`(资源不存在)只在读老 server 时接受,自己不发。
sourceraw docstring

error-keyclj

(error-key code)

错误码 → 关键字(认不出就 nil)。

错误码 → 关键字(认不出就 nil)。
sourceraw docstring

error-responseclj

(error-response id code message)
(error-response id code message data)

服务端:错误响应。code 可以是关键字(查 error-codes)或整数。

服务端:错误响应。`code` 可以是关键字(查 `error-codes`)或整数。
sourceraw docstring

input-required?clj

(input-required? result)

MRTR:server 说「还差点信息」(resultType = input_required)。

MRTR:server 说「还差点信息」(`resultType = input_required`)。
sourceraw docstring

latest-versionclj

本实现首选的版本。modern 时代从这一版开始。

本实现首选的版本。modern 时代从这一版开始。
sourceraw docstring

legacy-versionsclj

走 initialize 握手的版本,从新到旧——回退时按这个顺序挑。

走 `initialize` 握手的版本,**从新到旧**——回退时按这个顺序挑。
sourceraw docstring

meta-client-capsclj

source

meta-client-infoclj

source

meta-log-levelclj

source

meta-progress-tokenclj

source

meta-protocol-versionclj

source

meta-server-infoclj

source

meta-subscription-idclj

source

methodclj

(method k)

取方法名;给了未知关键字当场抛(装配期错误好过运行期 -32601)。

取方法名;给了未知关键字**当场抛**(装配期错误好过运行期 -32601)。
sourceraw docstring

method-namesclj

全部方法名。字符串常量集中一处——散在各处拼错了不会报错,只会收到 -32601 MethodNotFound,而那看起来像「server 不支持」。

全部方法名。**字符串常量集中一处**——散在各处拼错了不会报错,只会收到
`-32601 MethodNotFound`,而那看起来像「server 不支持」。
sourceraw docstring

missing-capability-errorclj

(missing-capability-error id required)

-32021:告诉客户端缺哪几个能力,否则它不知道该补什么。

`-32021`:告诉客户端**缺哪几个能力**,否则它不知道该补什么。
sourceraw docstring

modern-versionsclj

走每请求 _meta 的版本(无 initialize 握手)。

走**每请求 `_meta`** 的版本(无 `initialize` 握手)。
sourceraw docstring

modern?clj

(modern? v)

这个版本是 modern 时代的吗?

这个版本是 modern 时代的吗?
sourceraw docstring

notificationclj

(notification method-name)
(notification method-name params)
(notification method-name params {:keys [protocol-version] :as opts})

组一条通知(没有 id,对方 MUST NOT 回)。

组一条通知(**没有 id**,对方 MUST NOT 回)。
sourceraw docstring

parseclj

(parse s)

JSON 文本 → 消息 map(键保持字符串,与信封构造侧一致)。解析失败返回 nil。

JSON 文本 → 消息 map(键保持**字符串**,与信封构造侧一致)。解析失败返回 nil。
sourceraw docstring

protocol-version-ofclj

(protocol-version-of msg)

从已组好的请求信封里反读版本——HTTP 传输要拿它写 MCP-Protocol-Version 头。反读而不是各记一份,是因为头与信封不一致 server 会回 -32020。

从**已组好的请求信封**里反读版本——HTTP 传输要拿它写 `MCP-Protocol-Version`
头。反读而不是各记一份,是因为头与信封不一致 server 会回 `-32020`。
sourceraw docstring

requestclj

(request method-name params {:keys [id protocol-version] :as opts})

组一条 JSON-RPC 请求。

opts:

  • :id 请求 id(必填;协议禁止 null,也禁止重复未回的 id)
  • :protocol-version 给了就写 _meta(modern 信封),传 nil 就不写(legacy)
  • :client-info / :client-capabilities / :log-level / :extra-meta

「写不写 _meta」由调用方决定,不在这里按版本号猜:时代是 client 判出来的 (见 mcp.client),这里只负责照做。曾经在这儿加过一道 (modern? version) 的 守卫,结果是探测一个我们没列进 modern-versions 的新版本时,_meta 被静默 丢掉——请求于是变成 legacy 形状,server 按老语义服务,双方都不报错。

legacy 不写 _meta:老 server 见到陌生的 _meta 未必报错,但可能把它 当成参数的一部分(initialize 那条路最容易出这种事),没必要冒险。

组一条 JSON-RPC 请求。

`opts`:
- `:id`                 请求 id(必填;协议禁止 null,也禁止重复未回的 id)
- `:protocol-version`   **给了就写 `_meta`**(modern 信封),传 nil 就不写(legacy)
- `:client-info` / `:client-capabilities` / `:log-level` / `:extra-meta`

**「写不写 `_meta`」由调用方决定,不在这里按版本号猜**:时代是 client 判出来的
(见 `mcp.client`),这里只负责照做。曾经在这儿加过一道 `(modern? version)` 的
守卫,结果是**探测一个我们没列进 `modern-versions` 的新版本时,`_meta` 被静默
丢掉**——请求于是变成 legacy 形状,server 按老语义服务,双方都不报错。

**legacy 不写 `_meta`**:老 server 见到陌生的 `_meta` 未必报错,但可能把它
当成参数的一部分(`initialize` 那条路最容易出这种事),没必要冒险。
sourceraw docstring

response-errorclj

(response-error resp)

响应里的错误(没有就 nil)。返回 {:code :message :data}。

响应里的错误(没有就 nil)。返回 `{:code :message :data}`。
sourceraw docstring

response-resultclj

(response-result resp)

响应里的 result(没有就 nil)。

响应里的 result(没有就 nil)。
sourceraw docstring

result-responseclj

(result-response id result)
(result-response id result {:keys [protocol-version server-info result-type]})

服务端:成功响应。

resultType 是 2026-07-28 新增的必填字段;对 legacy 客户端不发—— 它们不认识,而规范说客户端把「缺省」当作 complete。

服务端:成功响应。

**`resultType` 是 2026-07-28 新增的必填字段**;对 legacy 客户端不发——
它们不认识,而规范说客户端把「缺省」当作 `complete`。
sourceraw docstring

result-typeclj

(result-type result)

结果类型。缺省当 complete——老 server 不发这个字段,规范明确要求这样兜底。

结果类型。**缺省当 `complete`**——老 server 不发这个字段,规范明确要求这样兜底。
sourceraw docstring

routing-nameclj

(routing-name msg)

Streamable HTTP 的 Mcp-Name 头取哪个值。

规范只对三个方法要求:tools/call / prompts/get 取 params.name, resources/read 取 params.uri。别的方法不发这个头。

Streamable HTTP 的 `Mcp-Name` 头取哪个值。

规范只对三个方法要求:`tools/call` / `prompts/get` 取 `params.name`,
`resources/read` 取 `params.uri`。别的方法不发这个头。
sourceraw docstring

sep-2575-error-codesclj

modern 时代的三个「你确实在跟新 server 说话」的信号。

双时代回退的判据全在这三个码上:收到它们说明对面懂 modern,只是这条请求 有问题(版本不支持 / 能力不够 / 信封头不匹配)——不该回退到 initialize, 回退也治不好。收到别的任何 JSON-RPC 错误,才说明对面是 legacy server。 (规范 PR #2844 明确要求回退不得只认单个错误码。)

**modern 时代的三个「你确实在跟新 server 说话」的信号**。

双时代回退的判据全在这三个码上:收到它们说明对面**懂** modern,只是这条请求
有问题(版本不支持 / 能力不够 / 信封头不匹配)——**不该回退到 `initialize`**,
回退也治不好。收到**别的**任何 JSON-RPC 错误,才说明对面是 legacy server。
(规范 PR #2844 明确要求回退不得只认单个错误码。)
sourceraw docstring

sep-2575-error?clj

(sep-2575-error? code)
source

ui-extension-idclj

MCP Apps 扩展在 capabilities.extensions 里的标识。

MCP Apps 扩展在 `capabilities.extensions` 里的标识。
sourceraw docstring

ui-meta-keyclj

MCP Apps 扩展:工具 _meta 里那个 ui 对象的键。

规范(/extensions/apps/overview)写的是 _meta.ui.resourceUri—— 一个嵌套对象,不是扁平的 ui/resourceUri 那种写法。同一个对象里还可能有 csp (app 能加载哪些外部源)与 permissions(麦克风/摄像头之类)。

猜错这个键不会报错,只会「明明是 App 工具却没画出界面」——所以这里写死, 并在 mcp.tools/ui-resource-uri 里同时兼容那个扁平写法。

MCP Apps 扩展:工具 `_meta` 里那个 **`ui` 对象**的键。

规范(`/extensions/apps/overview`)写的是 **`_meta.ui.resourceUri`**——
一个嵌套对象,不是扁平的 `ui/resourceUri` 那种写法。同一个对象里还可能有 `csp`
(app 能加载哪些外部源)与 `permissions`(麦克风/摄像头之类)。

猜错这个键**不会报错**,只会「明明是 App 工具却没画出界面」——所以这里写死,
并在 `mcp.tools/ui-resource-uri` 里同时兼容那个扁平写法。
sourceraw docstring

unsupported-version-errorclj

(unsupported-version-error id requested supported)

-32022:带上我们支持的版本列表,客户端据此重挑一个重试。 不带 supported 的话客户端只能猜——规范因此把它写成 data 的必备内容。

`-32022`:带上**我们支持的版本列表**,客户端据此重挑一个重试。
不带 `supported` 的话客户端只能猜——规范因此把它写成 data 的必备内容。
sourceraw docstring

version-2024-11clj

source

version-2025-03clj

source

version-2025-06clj

source

version-2025-11clj

source

version-2026-07clj

source

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close