两端通过该子进程的标准流进行通信:
- 服务器从
stdin读取 JSON-RPC 消息,并向stdout写入 JSON-RPC 消息。 - 每条消息都是一条单独的 JSON-RPC 请求、通知或响应。
- 消息以换行符分隔,且 不得 包含嵌入的换行符。
- 服务器 可以 出于任何日志记录目的向
stderr写入 UTF-8 字符串,包括信息、调试和错误消息。 - 客户端 可以 捕获、转发或忽略服务器的
stderr输出,且 不应 假定stderr输出表示错误状态。 - 服务器 不得 向其
stdout写入任何不是有效 MCP 消息的内容。 - 客户端 不得 向服务器的
stdin写入任何不是有效 MCP 消息的内容。
stderr、通过关闭流进行关闭、进程重启)需要使用与通道相关的等价机制。
发送消息
客户端通过向服务器的stdin 写入 JSON-RPC requests 和 notifications 来发送消息,每行一条。客户端 不得 写入 JSON-RPC responses。
接收消息
客户端从stdout 读取服务器消息,每行一条。所有消息共享这一个通道;不存在按请求划分的独立流。
服务器写入三类消息:
- 对客户端请求的 responses,通过 JSON-RPC
id进行关联。 - 与进行中的请求相关的 notifications,例如
notifications/progress和notifications/message。 - 为一个活跃的
subscriptions/listen请求而投递的 notifications。客户端 必须 使用_meta中的io.modelcontextprotocol/subscriptionId字段来关联这些消息;参见SubscriptionsListenRequest。
stdout 写入 JSON-RPC requests。服务器到客户端的交互通过
InputRequiredResult 回复来承载;参见 多轮往返请求。
请求元数据
stdio 传输的所有请求元数据都以内联方式携带在 JSON-RPC 消息体中。协议版本、客户端身份以及每个请求的能力声明都位于_meta.io.modelcontextprotocol/*;方法名和参数则位于 JSON-RPC 规定的位置。这里没有头部层。
取消
要取消一个进行中的请求,客户端 必须 发送一条引用该请求 ID 的notifications/cancelled 通知。由于 stdio 是一个共享的双向通道,因此不存在可关闭的按请求流。服务器 应当 尽快停止处理已取消的请求,且 不得 再为其发送任何后续消息。完整规则见 取消。
关闭
客户端 应当 按以下方式发起关闭:- 关闭到子进程(服务器)的输入流。
- 等待服务器退出。
- 如果服务器未能在合理时间内退出,则使用适用于该操作系统的机制强制终止该进程。
SIGTERM 升级到 SIGKILL。在没有 POSIX 信号的 Windows 上,客户端可以使用 TerminateProcess 或 作业对象。
当标准输入被关闭或读取返回文件结束时,服务器 应当 立即退出。这是首选的优雅关闭信号,也是唯一可移植的信号,因此遵循它可以减少对强制终止的需求。
服务器 可以 通过关闭到客户端的输出流并退出来发起关闭。
异常终止
如果服务器进程意外退出,客户端 应当 重新启动它。由于协议是无状态的,任何进行中的请求都会直接丢失,客户端可以在新的进程上重试这些请求。活跃的subscriptions/listen 流在重启后也必须重新建立。
向后兼容性
同时支持现代(按请求元数据)MCP 版本以及需要initialize 握手的旧版协议的客户端,应当 在发送任何其他请求之前,先通过 server/discover 进行探测,并在 _meta 中设置其首选的现代版本。该探测有三种可能结果:
- 服务器返回
DiscoverResult:该服务器是现代版本。请从supportedVersions中选择一个双方都支持的版本并继续。 - 服务器返回一个可识别的现代 JSON-RPC 错误,例如
UnsupportedProtocolVersionError:该服务器是现代版本,但不支持所请求的版本。请使用其公布的supported列表中的某个版本。不要 回退到initialize。 - 服务器返回其他任何错误,或者未在合理超时时间内响应:该服务器是旧版。请回退到
initialize握手。
initialize 之前的未知请求会返回实现定义的错误(通常是 -32601 或 -32602),或者根本不返回。
仅支持现代版本的客户端不需要探测,但仍然推荐进行探测:某些旧版服务器不会校验请求是否在 initialize 之后到达,而会在旧语义下处理一个时代不明确的方法(例如 tools/call)。探测可以避免这种情况,并给出确定性的失败结果。
关于时代模型以及实现者兼容性矩阵,请参见 版本管理:向后兼容性。