[MCP] 连接失败:客户端把 tools/list 的 params 序列化为 JSON null,服务端(MCP Kotlin SDK)报 -32603 拒绝
问题描述
连接本地 Burp Suite MCP 服务(burp-suite 扩展 v1.1.2,legacy SSE 传输,http://127.0.0.1:9876)时,initialize 握手成功,但卡在 tools/list:客户端发送的 params: null 被服务端严格反序列化器拒绝。手动用 params: {}(或省略 params)发同样的请求则成功,能返回完整工具列表(28 个工具)。
环境
- 客户端:ohmyagent(MonkeyCode),build f6b21ad,Windows 10
- 服务端:Burp Suite MCP 扩展 v1.1.2(MCP Kotlin SDK),
127.0.0.1:9876,legacy SSE(GET / 开流,消息端点 POST /?sessionId=…)
- 注意:服务端要求
Origin 请求头,非浏览器类客户端需手动配置(见下方配置)
当前配置(已正确)
{ "headers": { "Origin": "http://127.0.0.1:9876" }, "name": "burp", "transport": "sse", "url": "http://127.0.0.1:9876" }
实际报错(客户端 connect 输出)
tools/list: MCP error -32603: Expected JsonObject, but had JsonNull
as the serialized body of io.modelcontextprotocol.kotlin.sdk.ListToolsRequest
at element: $.primitive
JSON input: null
最小复现(curl)
# 1. 打开 SSE 流,记录 endpoint 事件(data: ?sessionId=…)
curl -s -N -H "Origin: http://127.0.0.1:9876" http://127.0.0.1:9876/
# 2. initialize 成功后,tools/list 两种形态:
curl -X POST -H "Origin: http://127.0.0.1:9876" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
"http://127.0.0.1:9876/?sessionId=<sid>"
# → 202,SSE 流正常返回 28 个工具(send_http1_request、get_proxy_http_history 等)
# 客户端实际发出的形态:
curl -X POST -H "Origin: http://127.0.0.1:9876" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":null}' \
"http://127.0.0.1:9876/?sessionId=<sid>"
# → 服务端返回 -32603 InternalError(JsonNull 请求体被拒绝)
期望行为与修复建议
按 MCP 规范,tools/list 无实质参数,客户端应发 "params": {} 或省略该字段,发显式 null 不符合规范,且会击穿使用严格反序列化器(kotlinx.serialization)的服务端。
修复点(客户端):JSON-RPC 请求序列化时,对所有无参方法(tools/list、resources/list 等)的空 params 输出 {} 或省略字段,而不是 null。
次级观察
- MCP 配置不支持热加载,且会被回写覆盖:修改配置文件只在进程启动时生效;且客户端被观测到至少 2 次用内存旧配置回写磁盘文件,覆盖掉用户手工修正。
- 报错可诊断性差:
streamable HTTP failed / SSE fallback failed 在 URL 解析错误、404、协议层 -32603 三种情况下报错形态几乎相同,且不回显实际使用的 URL/传输方式。
- 服务端(burp-suite 扩展):Origin 头强校验拦截标准非浏览器客户端;端点行为不一致(
/ 可用,/sse 时好时 404,/mcp 恒 404)。建议支持 Streamable HTTP 传输,并容错处理 null params。
临时规避方案
在客户端与 Burp 之间加一层本地 MCP 代理(如 supergateway),将 params: null 规范化为 {} 后转发。
证据附件
- 手动握手成功的 SSE 全量捕获(initialize + tools/list → 28 个工具):
bat_report.txt / sse_capture.txt
- 客户端日志:
03:58:25 [ERROR] MCP: failed to connect burp: … 404(旧配置)→ 重启修正配置后:tools/list: MCP error -32603: Expected JsonObject, but had JsonNull …
bat_report.txt
sse_capture.txt
[MCP] 连接失败:客户端把 tools/list 的 params 序列化为 JSON null,服务端(MCP Kotlin SDK)报 -32603 拒绝
问题描述
连接本地 Burp Suite MCP 服务(burp-suite 扩展 v1.1.2,legacy SSE 传输,
http://127.0.0.1:9876)时,initialize握手成功,但卡在tools/list:客户端发送的params: null被服务端严格反序列化器拒绝。手动用params: {}(或省略 params)发同样的请求则成功,能返回完整工具列表(28 个工具)。环境
127.0.0.1:9876,legacy SSE(GET /开流,消息端点POST /?sessionId=…)Origin请求头,非浏览器类客户端需手动配置(见下方配置)当前配置(已正确)
{ "headers": { "Origin": "http://127.0.0.1:9876" }, "name": "burp", "transport": "sse", "url": "http://127.0.0.1:9876" }实际报错(客户端 connect 输出)
最小复现(curl)
期望行为与修复建议
按 MCP 规范,
tools/list无实质参数,客户端应发"params": {}或省略该字段,发显式null不符合规范,且会击穿使用严格反序列化器(kotlinx.serialization)的服务端。修复点(客户端):JSON-RPC 请求序列化时,对所有无参方法(
tools/list、resources/list等)的空 params 输出{}或省略字段,而不是null。次级观察
streamable HTTP failed / SSE fallback failed在 URL 解析错误、404、协议层 -32603 三种情况下报错形态几乎相同,且不回显实际使用的 URL/传输方式。/可用,/sse时好时 404,/mcp恒 404)。建议支持 Streamable HTTP 传输,并容错处理 null params。临时规避方案
在客户端与 Burp 之间加一层本地 MCP 代理(如 supergateway),将
params: null规范化为{}后转发。证据附件
bat_report.txt/sse_capture.txt03:58:25 [ERROR] MCP: failed to connect burp: … 404(旧配置)→ 重启修正配置后:tools/list: MCP error -32603: Expected JsonObject, but had JsonNull …bat_report.txt
sse_capture.txt