JSON-RPC 2.0 经换行分隔 stdio 本节摘要:模型客户端与工具服务器之间的传输是 JSON-RPC over stdio。亲手滚一次,你就懂每个成帧层在为什么付费。JSON-RPC 2.0 是两页规范,2013 年至今没变,因为它在 stdio、socket、websocket、HTTP 上对称,客户端只要双方守规范就能驱动一个从没见过的服务器。本节构建 stdio 变体——换行分隔的 JSON,每请求一行、每响应一行、传输边界是 。你会学到四种信封形状、五个标准错误码、批处理,以及「一行解析错不毒化整条流」的韧性。 对应原课程:Phase 19 · Lesson 22 · (原英文 )。本节属「Agent Harness 深度构建赛道」第三节。
本节摘要:模型客户端与工具服务器之间的传输是 JSON-RPC over stdio。亲手滚一次,你就懂每个成帧层在为什么付费。JSON-RPC 2.0 是两页规范,2013 年至今没变,因为它在 stdio、socket、websocket、HTTP 上对称,客户端只要双方守规范就能驱动一个从没见过的服务器。本节构建 stdio 变体——换行分隔的 JSON,每请求一行、每响应一行、传输边界是
\n。你会学到四种信封形状、五个标准错误码、批处理,以及「一行解析错不毒化整条流」的韧性。
对应原课程:Phase 19 · Lesson 22 ·
jsonrpc-stdio-transport(原英文phases/19-capstone-projects/22-jsonrpc-stdio-transport/docs/en.md)。本节属「Agent Harness 深度构建赛道」第三节。
阅读完本节,你应当能够:
io.BytesIO 构建自终止 demo,无需 spawn 子进程即可运行。一个 2026 编程 Agent 在单会话里会和大约十二个工具服务器对话,每个是独立进程或远程端点。线格式自 2013 年没变。JSON-RPC 2.0 是两页规范,它存活至今是因为替代方案(gRPC、每调用一次 HTTP、自定义二进制)都强加了 JSON-RPC 不需要的权衡:它们要么选流式、要么选批处理、要么绑传输。JSON-RPC 在 stdio、socket、websocket、HTTP 上对称,客户端只要双方守规范就能驱动一个从没见过的服务器。
本节构建 stdio 变体:换行分隔 JSON,每请求一行、每响应一行,传输边界是 \n。
四种信封形状:两种客户端说,两种服务器说。请求带 id,服务器必须以同 id 的成功或错误响应。通知没有 id,服务器不得响应——如果对通知返回响应,客户端没法把它挂到调用点,这一条规则让成帧数学保持简单。批处理是请求或通知的 JSON 数组,服务器以响应数组回复(任意顺序,每个非通知条目一个);若全是通知,服务器啥也不回。
五个错误码:
-32700 Parse error JSON 解析不了 -32600 Invalid Request 信封形状错 -32601 Method not found -32602 Invalid params -32603 Internal error
解析错有特殊规则:响应里的 id 是 null,因为请求从没解析到能取出 id 的程度。-32000 到 -32099 留给服务器自定义错误,其余是应用自定义。本节只用这五个。handler 抛异常,传输包成 -32603,异常类名放 data.exception。
换行成帧的韧性(一行错不毒化流):
def serve(stdin, stdout, handler): for line in stdin: # 一行 = 到 \n 含 \n 的字节 try: req = parse_request(line) except ParseError: write_error(stdout, id=None, code=-32700) # id: null continue # 继续读下一行,流不毒化 ... # 派发
传输按行读。一行解析不了,传输写一个 id: null 的 -32700 响应然后继续。流不被毒化,下一行重新解析。坏的 JSON 行不停循环;缺 method 字段不停循环;handler 异常不停循环——传输一直读到 EOF。
传输不知道有哪些方法。它交给外壳提供的一个 handler(method, params) 可调用对象。handler 返回结果或抛异常。三个异常类呈现特定码:MethodNotFound → -32601,InvalidParams → -32602,其余 → -32603(异常名放 data)。
传输从不见工具注册表,注册表坐在 handler 后面。这是我们要的分层:传输说 JSON-RPC,注册表说工具形状,第 23 节的分发器把它们缝起来。
通知是「发即忘」。外壳用通知发进度事件、取消信号、日志行。通知是长跑工具能流式发状态更新而无需每次往返的机制。本节实现一个出站通知助手 write_notification,服务器在请求在途时发进度——demo 展示:请求进来,handler 发两条进度通知,再写最终响应。
code/main.py 定义 StdioTransport、解析助手 parse_request、三个写助手(write_response/write_error/write_notification)与派发循环 serve,错误码常量在模块作用域。code/tests/test_transport.py 覆盖五个错误码、通知(不写响应)、批处理(数组进数组出,通知跳过)、坏 JSON(解析错后继续)、handler 中途写通知的非对称流。
JSON-RPC 2.0 在 LSP(语言服务器协议)、MCP(模型上下文协议)、DAP(调试适配器协议)里都是传输底子,这正是它的通用性证明。本节的 stdio 变体与 MCP 2026 的 StreamableHTTP 是兄弟——前者本地进程间,后者网络无状态,线格式都是 JSON-RPC 2.0。生产传输会在其上加三样:一是能扛转发的关联 id(你的 id 已是这个,但在网格里还需一个外层 trace id);二是取消通道(类似 $/cancelRequest 的通知带在途调用的 id);三是 content-type 协商握手,让同一 socket 能说 JSON-RPC 与 Streamable HTTP。这些都不改线格式,只加元数据。
$/cancelRequest 通知,取消一个在途长跑调用的 id。\n。下一节,我们建「函数调用分发器」——把超时、重试、去重、错误映射全压在一条缝上。