Title: feat!: enforce required MCP spec fields in McpSchema; lenient wire deserialization by chemicL · Pull Request #928 · modelcontextprotocol/java-sdk · GitHub
Open Graph Title: feat!: enforce required MCP spec fields in McpSchema; lenient wire deserialization by chemicL · Pull Request #928 · modelcontextprotocol/java-sdk
X Title: feat!: enforce required MCP spec fields in McpSchema; lenient wire deserialization by chemicL · Pull Request #928 · modelcontextprotocol/java-sdk
Description: Motivation and Context Every wire-serialized record in McpSchema now validates spec-required fields at construction time. Wire deserialization is intentionally lenient: a missing required field is replaced with a documented default and a WARN is logged, instead of failing the parse. A required-first builder convention is introduced across the schema so it is no longer possible to obtain a builder that is missing required state. Required-field guards Every wire record's compact constructor asserts non-null (and non-empty for String identifiers like name, uri, uriTemplate, version) on its spec-required components. Passing null now throws IllegalArgumentException at construction time instead of silently producing invalid JSON via @JsonInclude(NON_ABSENT). This applies to the JSON-RPC envelopes, lifecycle types, resource/prompt/tool requests and results, sampling and elicitation, content records, root, complete, logging and progress notifications, and the two CompleteReference implementations. See MIGRATION-2.0.md for the full list. Lenient wire deserialization For each of those records (except JSONRPCResponse.JSONRPCError, which still fails fast) a @JsonCreator static fromJson factory substitutes a documented default for any absent required field — "" for strings, [] for collections, {} for maps, 0 / 0.0 for numerics, INFO for LoggingLevel, USER for SamplingMessage.role, ASSISTANT for CreateMessageResult.role, CANCEL for ElicitResult.action, {values=[]} for CompleteResult.completion — and logs a WARN naming the field and the value used. Application code can still observe a malformed message and react, but the SDK no longer halts the conversation. Builder convention Records that have a builder gain a required-first factory method builder(req1, req2, …); setters for required fields are removed from the builder so it cannot be left in an invalid state. Existing no-arg builder() factories and required-field setters are kept where source compatibility demands it but are marked @deprecated. New builders are also added for several records that previously had none (ProgressNotification, JSONRPCError, CompleteRequest, list/result types, content records, ...). JSON-RPC envelope ergonomics Previously every JSON-RPC envelope had to be constructed via the canonical record constructor and the literal "2.0" string had to be threaded through every call site, e.g. new JSONRPCRequest("2.0", "tools/call", id, params) new JSONRPCResponse("2.0", id, result, null) new JSONRPCResponse("2.0", id, null, new JSONRPCError(code, message, null)) Now: new JSONRPCRequest("tools/call", id, params) // jsonrpc defaulted new JSONRPCNotification("notifications/initialized") // params optional JSONRPCResponse.result(id, result) // factory JSONRPCResponse.error(id, new JSONRPCError(code, message)) // 2-arg error JSONRPCResponse's compact constructor additionally enforces the JSON-RPC invariant that exactly one of result / error is set — previously the SDK could build envelopes that violated the protocol. CompleteReference changes PromptReference.equals/hashCode now key on name only (previously derived from identifier()+type()). Two refs with the same name but different titles now compare equal — code using PromptReference as a map/set key should be audited. PromptReference's compact constructor pins type to "ref/prompt" and logs a WARN if the caller supplies a different non-null value. The legacy two-arg PromptReference(String type, String name) constructor remains @deprecated. ResourceReference's record components are reduced from (type, uri) to (uri) — positional construction breaks. The legacy ResourceReference(String type, String uri) constructor stays @deprecated and ignores type. CompleteReference.identifier() is @deprecated and now returns null via a default method on the interface. Tests / refactor Conformance harness, integration tests, sample apps, and internal callers were migrated to the new builder factories and convenience constructors. No semantic changes to client/server runtime behaviour beyond the McpSchema changes above. Docs MIGRATION-2.0.md: required-field section now covers the broader record set and the lenient-deserialization behaviour; PromptReference WARN behaviour and ResourceReference component reduction are documented; the JSON-RPC envelope section is rewritten to compare the pre-2.0 surface with the new one. CONTRIBUTING.md: the "Evolving wire-serialized records" recipe is split into two cases — adding a new optional field (existing rules) and adding/maintaining a spec-required field (new rules covering Assert in compact constructor, @JsonCreator fromJson with defaults and WARN, required-first builder factory). How Has This Been Tested? Test code that constructed these records without required fields was updated to supply valid values; those tests were testing capability-check or error-path logic that fires after construction, so the missing fields were incidental rather than intentional. Breaking Changes PromptReference.equals/hashCode now key on name only (previously derived from identifier()+type()). Two refs with the same name but different titles now compare equal — code using PromptReference as a map/set key should be audited. PromptReference's compact constructor pins type to "ref/prompt" and logs a WARN if the caller supplies a different non-null value. The legacy two-arg PromptReference(String type, String name) constructor remains @deprecated. ResourceReference's record components are reduced from (type, uri) to (uri) — positional construction breaks. The legacy ResourceReference(String type, String uri) constructor stays @deprecated and ignores type. CompleteReference.identifier() is @deprecated and now returns null via a default method on the interface. Types of changes Bug fix (non-breaking change which fixes an issue) New feature (non-breaking change which adds functionality) Breaking change (fix or feature that would cause existing functionality to change) Documentation update Checklist I have read the MCP Documentation My code follows the repository's style guidelines New and existing tests pass locally I have added appropriate error handling I have added or updated documentation as needed
Open Graph Description: Motivation and Context Every wire-serialized record in McpSchema now validates spec-required fields at construction time. Wire deserialization is intentionally lenient: a missing required field is ...
X Description: Motivation and Context Every wire-serialized record in McpSchema now validates spec-required fields at construction time. Wire deserialization is intentionally lenient: a missing required field is ...
Opengraph URL: https://github.com/modelcontextprotocol/java-sdk/pull/928
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:191bb926-a70e-8207-bd05-0dc8e4474b6b |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | 9742:3E70D2:122380B:19CFB0E:6A5F9542 |
| html-safe-nonce | 0161f1eab8c4da5a0e830a80a844239142e0d5a07e3531b6fb9e165d2d214598 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5NzQyOjNFNzBEMjoxMjIzODBCOjE5Q0ZCMEU6NkE1Rjk1NDIiLCJ2aXNpdG9yX2lkIjoiNDI5ODM4MzU4MzQ5MTM2NDE2MiIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 21554619741039347b803922038e11e72df70bbfb657374c4005215b5f7243b4 |
| hovercard-subject-tag | pull_request:3557441502 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/modelcontextprotocol/java-sdk/pull/928/files |
| twitter:image | https://avatars.githubusercontent.com/u/2554306?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/2554306?s=400&v=4 |
| og:image:alt | Motivation and Context Every wire-serialized record in McpSchema now validates spec-required fields at construction time. Wire deserialization is intentionally lenient: a missing required field is ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 419b66b5f218029570caa18c30987e7dbabe4f5a6767918b33a504d02739c9c7 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/modelcontextprotocol/java-sdk git https://github.com/modelcontextprotocol/java-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 919609219 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/java-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 919609219 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/java-sdk |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | de5b18aa36dbc0b61e03aeb111e84e90fde499b8 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width