modelcontextprotocol / modelcontextprotocol/python-sdk
perf: JSONRPCMessage smart union scores all four branches on every inbound message
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Python
- Star
- 24.3k
- Fork
- 4k
- Merge trung bình
- 1 ngày 1 giờ
- Pull request đã merge (30 ngày)
- 31
Mô tả
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)
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu trong src/mcp-types/mcp_types/jsonrpc.py tại jsonrpc_message_adapter, sau đó xem lại các điểm gọi transport được nêu trong issue để xác nhận rằng không có thay đổi bắt buộc nào. Chạy scripts/bench_jsonrpc_codec.py và các bài kiểm tra parity/branch được đề xuất; được xem là hoàn tất khi việc xác thực một branch duy nhất, hành vi wire và parity phân loại được giữ nguyên, cùng với hành vi đã được ghi nhận đối với đầu vào không hợp lệ.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- python
- Lĩnh vực
- backend-api-design, performance
- Loại issue
- Tái cấu trúc
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức độ hoạt động
- Đình trệ
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức phù hợp với người mới
- 25/100