modelcontextprotocol / modelcontextprotocol/python-sdk
perf: JSONRPCMessage smart union scores all four branches on every inbound message
还没有人认领这个 Issue。
- 主要语言
- Python
- 星标
- 24.3k
- 派生
- 4k
- 平均合并
- 1 天 1 小时
- 30 天内合并 PR
- 31
描述
Description
jsonrpc_message_adapter (src/mcp-types/mcp_types/jsonrpc.py) validates the
JSONRPCRequest | JSONRPCNotification | JSONRPCResponse | JSONRPCError union in
Pydantic's smart-union mode, which attempts/scores every branch for every message.
This adapter is the single decode choke point for every transport:
server/streamable_http.py(POST body),server/sse.py,server/stdio.pyclient/streamable_http.py(SSE data + JSON responses),client/sse.py,client/stdio.py
The four variants are trivially distinguishable by key presence per JSON-RPC 2.0
(method [+ valid id] → request/notification, error → error, result → response),
so a callable Discriminator + Tags (available at the repo's pydantic floor,
>=2.12.0) selects exactly one branch instead.
Measured impact
scripts/bench_jsonrpc_codec.py (proposed alongside the fix), darwin arm64 / CPython 3.14.2 / pydantic 2.12.5, best-of-5:
| payload | path | smart union | discriminator | speedup |
|---|---|---|---|---|
| request 109B | validate_json | 1.76µs | 1.05µs | 1.69x |
| request 109B | from_json + validate_python | 1.45µs | 0.90µs | 1.60x |
| notification 105B | validate_json | 1.88µs | 0.86µs | 2.18x |
| notification 105B | from_json + validate_python | 1.29µs | 0.80µs | 1.60x |
| result 22.3KB | validate_json | 51.7µs | 32.1µs | 1.61x |
| result 22.3KB | from_json + validate_python | 12.7µs | 12.2µs | 1.04x |
For high-throughput agent communication (many small requests/notifications per
second per session) this halves envelope-decode CPU on the hot path of every
transport.
Behavior compatibility
The fix keeps classification parity with today's smart-union outcomes for all
spec-valid messages, verified empirically and pinned by tests:
{"id": null | 1.5 | true, "method": ...}and absent-id →JSONRPCNotification(current downgrade behavior preserved; note the interaction with #2057 / PR #2075 - if that lands, the discriminator's id-guard becomes the single, clean place to implement the rejection)- degenerate
{method, id, result}→JSONRPCRequest;{id, result, error}→JSONRPCError JSONRPCMessagestays a plain union (soisinstance(x, JSONRPCMessage)keeps working); theAnnotated/Tagwrapping lives only inside theTypeAdapter- wire output (
dump_json,by_alias=True,exclude_none=True) is byte-identical - deliberate divergence on spec-invalid hybrids (surfaced by cubic's review on the PR):
{method, error}hybrids classify as a call instead ofJSONRPCError, and
{method: <non-str>, result}is rejected instead of falling through to
JSONRPCResponse- smart union picked those via field-count scoring, which let a
malformed frame masquerade as an error response to a pending request; pinned by tests - only visible change:
ValidationErrorfor unclassifiable input becomes a single
jsonrpc_message_invaliderror with a clear message instead of a 4-branch error dump
(no in-repo test asserts the old text; error codes and HTTP statuses unchanged)
Additional finding worth recording: with a callable discriminator,
validate_json must materialize the payload to call the discriminator, so the
server's existing two-phase parse (pydantic_core.from_json → validate_python)
is the faster path on large bodies (12.2µs vs 32.1µs on 22KB) - the fix adds a
comment pinning that so it doesn't get "cleaned up" into a regression later.
Proposed fix
PR ready: key-presence callable discriminator on the adapter, zero call-site
changes, new parity/branch tests (100% coverage maintained), standalone
micro-benchmark script.
Disclosure: this issue and the accompanying PR were developed with AI assistance
(Claude Code); all measurements and parity checks were run and verified locally.
References
- Draft PR with the implementation, measurements, and tests: #3135 (kept in draft pending this issue)
- Related: #2057 / #2075 (null-id classification - the discriminator id-guard is the natural locus for that change if it lands)
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
从 src/mcp-types/mcp_types/jsonrpc.py 中的 jsonrpc_message_adapter 开始,然后检查 issue 中提到的 transport 调用点,以确认不需要进行任何必要的更改。运行 scripts/bench_jsonrpc_codec.py 和提议的 parity/branch 测试;完成的标准是单 branch 验证、wire 行为和分类 parity 均得到保留,并符合针对无效输入所记录的行为。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- python
- 领域
- backend-api-design, performance
- Issue 类型
- 重构
- 难度
- 3/5
- 预计耗时
- 1-2 天
- 活跃度
- 停滞
- 描述清晰度
- 描述清楚
- 新手友好度
- 25/100