Last 12 weeks · 173 commits
5 of 6 standards met
Motivation and Context SEP-2549 (modelcontextprotocol/modelcontextprotocol#2549, merged for the 2026-07-28 spec release) adds a shape with (freshness lifetime in milliseconds, max-age semantics, 0 = do not cache) and ( or , whether shared intermediaries may cache) to , , , , and , complementing the notifications. The field shape follows the Python SDK's implementation (python-sdk#2824, + with / defaults; the TypeScript SDK's earlier -seconds prototype #2070 was superseded by this shape). Because this SDK negotiates protocol versions up to 2025-11-25, where the fields do not exist, emission is opt-in: when neither nor is configured, responses are serialized exactly as before. The fields become REQUIRED in 2026-07-28; a follow-up can auto-emit defaults once that version enters . gains and keywords with validating writers modeled on ( must be nil or a non-negative Integer; must be nil, , or ). A private wraps the five emission points. When the user sets only one of the two values, the other is filled with the spec defaults (, the only universally safe freshness value, and , the spec and Python default). A may return a full hash to override the server-level hints per result; the documented bare-contents return shape keeps working unchanged. The client's / / / structs gain and members populated from the response fields. Resolves #387. How Has This Been Tested? New tests in : with , all four list results carry and the filled default with only , carries with neither configured, list and read results omit both fields (wire-format regression) the hints appear alongside when paginating a returning overrides the server-level value while the missing is filled invalid writer values (, , ) raise New tests in assert all four list result structs expose / , and that both are nil when the server omits the fields. Breaking Changes None. Emission is opt-in, the new constructor keywords default to nil, and the client structs gain additive members. Types of changes [ ] Bug fix (non-breaking change which fixes an issue) [x] New feature (non-breaking change which adds functionality) [ ] Breaking change (fix or feature that would cause existing functionality to change) [x] Documentation update Checklist [x] I have read the MCP Documentation [x] My code follows the repository's style guidelines [x] New and existing tests pass locally [x] I have added appropriate error handling [x] I have added or updated documentation as needed
Motivation and Context SEP-2322 (modelcontextprotocol/modelcontextprotocol#2322, merged for the 2026-07-28 spec release) introduces Multi Round-Trip Requests: instead of issuing in-flight server-to-client JSON-RPC requests, a server answers with a result whose is , carrying an map (of , , and request shapes) and an opaque ; the client fulfills the requests and re-issues the original request with and the echoed . The wire contract (the discriminator and the / shape) stayed stable across all three closed TypeScript prototype iterations (typescript-sdk#2062/#2065, the v2-stateless stack, and #2251) and the Python draft (python-sdk#2322), but the server-side suspend/resume mechanism is still unsettled in both SDKs (typescript-sdk#2251 was put on hold on 2026-06-08). This change therefore implements only the stable, additive vocabulary and the client-side recognition, leaving server emission and automatic resumption for a follow-up once the reference design lands: New module with and constants documenting the values. raises the new (exposing , , and the raw ) when any response carries , instead of silently returning a non-final result as if it were the answer. The check lives in the shared request path, so every client method is covered. Servers on stable protocol versions never emit , so default behavior is unchanged. Part of #382. How Has This Been Tested? New tests in : raises for an result and exposes , , and the full raw result returns normally when is and when it is absent (wire-compat regression for stable-protocol servers) also raises for results, proving the recognition covers the shared request path (tests, RuboCop, and conformance baseline) passes. Breaking Changes None for spec-compliant stable servers, which never send . A response that does carry now raises instead of being returned as a final result, which was always a misinterpretation of the draft semantics. Types of changes [ ] Bug fix (non-breaking change which fixes an issue) [x] New feature (non-breaking change which adds functionality) [ ] Breaking change (fix or feature that would cause existing functionality to change) [x] Documentation update Checklist [x] I have read the MCP Documentation [x] My code follows the repository's style guidelines [x] New and existing tests pass locally [x] I have added appropriate error handling [x] I have added or updated documentation as needed
Motivation and Context The MCP extension (SEP-1046, modelcontextprotocol/modelcontextprotocol#1046) specifies two client authentication methods for the grant: and the RECOMMENDED (RFC 7523 Section 2.2 JWT client assertion). The previous commit added the grant with secret-based authentication; this adds the JWT method, which is what the conformance scenario exercises. This mirrors the merged implementations in the TypeScript SDK (, typescript-sdk#1157) and the Python SDK ( / , python-sdk#1663), adapted to this SDK's provider design: New builds the RFC 7523 compact JWS with openssl, keeping the SDK free of a JWT gem dependency (the TypeScript and Python SDKs use jose and PyJWT). Claims follow SEP-1046: and carry the client_id, carries the authorization server's issuer identifier, plus (300-second lifetime, matching both SDKs), , and a unique . ES256 and RS256 are supported; ES256 signatures are converted from OpenSSL's ASN.1 DER form to the raw 64-byte encoding JWS requires (RFC 7518 Section 3.4). accepts with (PEM string or ) and a required (no default, so an algorithm mismatch with the server's fails loudly at construction; both reference SDKs also take the algorithm explicitly). The key stays on the provider and is never written to ; a throwaway assertion is signed at construction to fail fast on an unparseable key or a key/algorithm mismatch. sends and a freshly signed for , using the -validated issuer as the audience, and omits from the body per SEP-1046 (identity travels in the assertion's claims). The conformance client builds a provider when the harness injects , defaulting the algorithm to ES256 like the TypeScript and Python conformance clients, and is removed from . Scope notes: only ES256 and RS256 are supported for now (the TypeScript and Python SDKs accept whatever their JWT libraries support); adding further algorithms is additive. There is also no injection point yet for externally signed assertions (the TypeScript SDK's and the Python SDK's callback cover KMS/HSM-held keys); that is a separate follow-up. How Has This Been Tested? New covers the JWS construction: header and RFC 7523 claims, unpadded base64url segments, ES256 signature round-trip verification (raw -> DER -> ), RS256 verification, PEM and parsed-key input, lifetime override, unique per assertion, and rejection of unsupported algorithms, key/algorithm mismatches (including a P-384 key with ES256), unparseable PEM, and public-only keys. New tests in cover the provider surface: construction, the key and secret never reaching , required / , rejection of a simultaneous , fail-fast on key/algorithm mismatch, and producing a JWT with the expected claims. A new test in runs the full grant against WebMock stubs and asserts the token request carries , the RFC 7523 , an assertion whose / are the client_id and whose is the issuer, the RFC 8707 , and neither nor any secret in the body or headers. passes; the conformance scenario passes 7/7 checks and the baseline check passes with its expected-failure entry removed. Breaking Changes None. becomes optional on only in the signature; it is still required for the secret-based methods, and all existing construction paths behave unchanged. Types of changes [ ] Bug fix (non-breaking change which fixes an issue) [x] New feature (non-breaking change which adds functionality) [ ] Breaking change (fix or feature that would cause existing functionality to change) [ ] Documentation update Checklist [x] I have read the MCP Documentation [x] My code follows the repository's style guidelines [x] New and existing tests pass locally [x] I have added appropriate error handling [x] I have added or updated documentation as needed
Motivation and Context A JSON-RPC notification carries no id and must not receive any response. When a client erroneously sent a notification-only method such as with an id, the server replied with a successful , which is neither a valid MCP result nor a legal reply to a notification. Such a message is now treated as a request for a method that has no request handler and rejected with Method not found, matching the behavior of the TypeScript and Python SDKs. A well-formed notification that omits the id continues to receive no response. Fixes #434. How Has This Been Tested? Added regression tests covering id-bearing , , and , a batch that mixes an id-bearing notification with other messages, custom methods, and the same case over the HTTP transport. Breaking Changes None for spec-conformant clients, which never put an id on a notification. The only behavioral change is that a malformed id-bearing notification now receives a Method not found error instead of a spurious . Types of changes [x] 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 [x] I have read the MCP Documentation [x] My code follows the repository's style guidelines [x] New and existing tests pass locally [x] I have added appropriate error handling [x] I have added or updated documentation as needed
Describe the bug When the Ruby SDK server receives a JSON-RPC message with an and , it returns a successful JSON-RPC response with . A valid MCP notification does not include an and must not receive a response. If this id-bearing message is treated as a request, it should not be completed with a successful response, because MCP result responses use an object-valued . Environment Reproduced with stable release () Also reproduced on current () using a 2026-07-28-style request envelope Transport: stdio To Reproduce 1. Start a Ruby SDK MCP server over stdio. 2. Send a message with a JSON-RPC : 3. Observe the response. Expected behavior The server should not emit a successful JSON-RPC response for this message. A valid notification has no and receives no response. If an id-bearing message for this notification-only method is treated as a request, it should be rejected with a JSON-RPC error rather than completed with . Logs The server returns: The server remains healthy and continues to process subsequent requests. Additional context** In , is dispatched through a special-case handler: can return , for example when there's no session: That value then flows through the normal success-response path in : already exists for handlers that should not emit a JSON-RPC response, so this may be the appropriate sentinel for notification-style handling. This appears related to #398 / #400, which preserved request ids for JSON-RPC envelope error responses in the same area. This issue is different: the response id is preserved, but the message is completed as a successful response with .
Repository: modelcontextprotocol/ruby-sdk. Description: The official Ruby SDK for the Model Context Protocol. Stars: 858, Forks: 123. Primary language: Ruby. Languages: Ruby (99.7%), Shell (0.3%). Homepage: https://ruby.sdk.modelcontextprotocol.io Latest release: v0.22.0 (5d ago). Open PRs: 8, open issues: 12. Last activity: 11h ago. Community health: 87%. Top contributors: koic, atesgoral, topherbullock, kfischer-okarin, Ginja, dak2, sambostock, tylerrowsell, andyw8, jcat4 and others.