All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
This release finishes the encoding fixes of 1.8.1 on the client and the HTTP transport, which no longer fail
a request or a stream on a frame or a reply they cannot encode. The Changed entry alters what
a subscriptions/listen stream honors under the exceptions described in VERSIONING.md:
a resources_subscribe_handler that raises for a URI now narrows the acknowledgement, and the handler runs
for listen streams without a matching resources_unsubscribe_handler call.
- Consult the
resources_subscribe_handlerfor the resource URIs of asubscriptions/listenstream (#632)
- Strip stdio client frames as bytes (#627)
- Skip a stdio ping whose id cannot be encoded (#628)
- Answer an HTTP request whose reply cannot be generated with Internal error (#629)
- Drop a buffered notification that cannot be generated (#630)
- Report failed session notifications through the server configuration (#631)
This release brings the server's JSON-RPC framing and capability advertisement, and the HTTP client's handling
of an initialize answered over SSE, in line with the MCP specification. The Changed entries alter answers on
the wire, as fixes of incorrect conformance under the exceptions described in VERSIONING.md.
Only senders of null ids and client code that read the error event of a 4xx or 5xx SSE answer, which the client
now raises as RequestHandlerError, have to change. A stdio server also survives a frame it cannot strip, parse
or answer.
- Refuse a request whose id is null and do not run a request method sent without an id (#612)
- Leave the id out of an error response when the request id cannot be read (#615)
- Return from
initializeas soon as its SSE response arrives and raise on an error-status SSE answer in the HTTP client (#617) - Leave
listChangedandsubscribeout of a stateless server'sinitializeresult (#620)
- Answer a method of an undeclared capability with Method not found (#611)
- Accept any string as a request id (#613)
- Ignore empty members of the
Acceptheader (#616) - Keep Ruby 2.7 from warning about keywords in
StdioTransport(#621) - Keep the stdio server running when a reply cannot be encoded (#623)
- Treat a JSON key that is not valid UTF-8 as a parse error (#624)
- Strip stdio frames as bytes (#625)
This release adds OAuth 2.1 resource server support to StreamableHTTPTransport: bearer tokens are verified
by a JWT or an RFC 7662 introspection verifier (or one of your own), requests without a valid token get RFC 6750
challenges, and a Rack middleware serves the RFC 9728 Protected Resource Metadata document; token issuance stays
with an external authorization server.
The bundled client transports now reject server messages that are not JSON-RPC 2.0, which rejects what earlier
releases accepted and ships in a minor release under the exceptions described in VERSIONING.md:
a server whose responses lack "jsonrpc": "2.0" has to send the member, since there is no opt-out.
- Add OAuth 2.1 resource server support (#545)
- Add a scope selector for OAuth authorization-code clients (#579)
- Reject server messages that are not JSON-RPC 2.0 in the client transports (#590)
- Redact the
Authorizationheader from the errors the HTTP transport retains (#591) - Close a refused SSE stream outside the registry lock (#592)
- Let the session reaper finish before the transport closes (#593)
- Bound the
subscriptions/listenteardown on transport close (#595) - Close a failed
subscriptions/listenstream only from the path that removed its entry (#596) - Accept any 2xx acknowledgement of a notification in the HTTP client (#600)
This release lets a web application finish the OAuth authorization in the request that receives the redirect,
bounds the initialize request a session retains and the resource URIs a subscriptions/listen stream retains,
validates icons given as Hashes through MCP::Icon, and repairs two subscriptions/listen cases: a request that
cannot be encoded is refused, and a removed stream is marked closed.
The two bounds and the icon validation reject what earlier releases accepted and ship in a minor release under
the exceptions described in VERSIONING.md; raise max_initialize_request_bytes: and
max_resource_subscription_bytes: (or set them to nil) where the byte bounds are too tight, and give Hash icons only
the MCP::Icon members with a valid src.
- Let a web application finish authorization in the request that receives the redirect (#573)
- Bound the resource URIs a
subscriptions/listenstream retains (#582) - Bound the
initializerequest a session retains (#583)
- Validate icons given as Hashes through
MCP::Icon(#586)
- Refuse
subscriptions/listenrequests that cannot be encoded (#585) - Mark a removed
subscriptions/listenstream closed (#587)
This release makes the OAuth client parse WWW-Authenticate headers and URLs in linear time and repairs
the lifecycle of subscriptions/listen streams: same-id streams are served, a keepalive thread ends with its slot,
and the acknowledgement precedes the closing result.
- Replace authorization request parameters the endpoint URL already carries (#566)
- Serve
subscriptions/listenstreams that carry the same request id (#567) - End a listen stream's keepalive thread when its slot is freed (#569)
- Parse the
WWW-Authenticatechallenge in linear time (#571) - Keep the acknowledgement first when the transport closes during a listen registration (#574)
- Resolve dot segments in linear time when canonicalizing a URL (#575)
This release lets an application configure, on the OAuth provider, the requests the flow makes to
the authorization server: token_request_params: adds the parameters a server requires beyond the grant itself,
and http_client_customizer: adds middleware to the connection the flow uses for discovery, registration,
and token requests, behind a guard that refuses a request leaving the origin the flow asked for.
The legacy 2025-03-26 discovery path is now taken only when the server publishes no Protected Resource Metadata:
a fetch that failed to reach the server, or that answered 5xx or 429, raises Flow::MetadataUnreachableError instead,
and the metadata served on that path must name the MCP server's origin as its issuer. The client_credentials
and jwt-bearer grants run on that path, and a stored refresh token no longer crashes their providers.
MCP::Icon.new now refuses an icon the specification's schema rejects, such as one without src or
with sizes given as a String.
Three entries under "Changed" reject what earlier releases accepted and ship in a minor release under
the exceptions described in VERSIONING.md.
- Add
token_request_params:to let a provider add parameters to the token requests it makes (#555) - Add
http_client_customizer:to let a provider customize the HTTP client the OAuth flow uses (#560)
- Validate the issuer of legacy authorization server metadata (#556)
- Surface unreachable Protected Resource Metadata instead of falling back to legacy discovery (#561)
- Validate the
srcandsizesof an icon against the specification (#563)
- Omit the body
client_idfrom token requests that authenticate with HTTP Basic (#548) - Preserve the token endpoint's
erroranderror_descriptionin the raised error (#549) - Let the
client_credentialsandjwt-bearergrants run without Protected Resource Metadata (#557) - Let providers without a CIMD URL reader refresh their tokens (#559)
This release keeps the HTTP client working with json 3.0. That release accepts the options of JSON.parse
as keywords only, and Faraday's JSON response middleware, through 2.14.3, passes them as a positional Hash,
which Ruby 3 no longer converts, so every JSON body the client received failed as an internal error.
The client now parses response bodies itself, the way it already handled streamed ones, and no longer
registers the middleware. A malformed JSON body delivered whole by an adapter without streaming support
now raises RequestHandlerError with error_type: :parse_error, as the streamed path already did.
- Parse HTTP client response bodies without Faraday's JSON middleware, which json 3.0 breaks (#546)
This release makes the client answer a server's ping with the empty result the specification requires.
Earlier clients replied with Method not found over Streamable HTTP and stayed silent over stdio,
so a server that checks liveness dropped their long-lived sessions. On the OAuth side,
the embedding application can refuse an authorization request through authorization_request_validator,
and stored tokens are refreshed only against the authorization server that issued them:
when the server named for a session differs, the client reauthorizes instead of refreshing,
and the validator sees the newly named server. Tokens stored by earlier releases carry no issuer and keep refreshing.
- Add
authorization_request_validatorto let the embedding application refuse an authorization request (#539)
- Answer server-to-client pings in the client (#541)
This release repairs the subscriptions/listen stream. A host that buffers its responses,
the documented Rails controller pattern among them, can decline the method with
serve_subscriptions_listen: false instead of answering a modern client with a streaming body
it cannot render, and the stream's ordering guarantees are enforced: no notification reaches
a client before the acknowledgement, and none follows the graceful closing result.
- Add
serve_subscriptions_listen:for hosts that cannot hold an SSE stream open (#533)
- Deliver
subscriptions/listennotifications only after the acknowledgement (#532) - Serialize
subscriptions/listenwrites so the graceful result is the final message (#535)
User-facing documentation now lives on the documentation site at https://ruby.sdk.modelcontextprotocol.io, and README.md keeps the quick start. Two entries under "Changed" reject traffic that earlier releases accepted and ship in a minor release under the exceptions described in VERSIONING.md.
- Add a
resources_list_handlerfor context-dependent resource lists (#509) - Pass a handler-returned
_metathrough the subscribe result (#510)
- Bound OAuth response bodies in the client (#520)
- Reject duplicate in-flight JSON-RPC request ids (#521)
- Move the documentation from README.md to the documentation site (#523)
This release completes the SEP-2575 stateless lifecycle of the 2026-07-28 specification, together with the SEP-2322, SEP-2549, and SEP-2243 features that revision builds on. Several entries under "Changed" are incompatible with 1.1.0 and ship in a minor release under the spec-conformance and security exceptions described in VERSIONING.md.
- Handle the SEP-2575 modern request envelope in the server core (#475)
- Serve both lifecycle eras over stdio with an era lock per SEP-2575 (#478)
- Serve the sessionless modern path over Streamable HTTP per SEP-2575 (#479)
- Finalize
server/discoverand add client modern lifecycle support per SEP-2575 (#480) - Let handlers return multi round-trip
input_requiredresults per SEP-2322 (#481) - Add
MCP::Elicitation::EnumSchemabuilders per SEP-1330 (#482) - Stamp the required
resultTypeon modern results per SEP-2322 (#487) - Enforce the modern lifecycle admission rules per SEP-2575 (#489)
- Stream modern request notifications and honor the envelope
logLevelper SEP-2575 (#490) - Expose the user-defined
server_contextin instrumentation data (#493) - Serve the
subscriptions/listennotification stream per SEP-2575 (#495) - Add opt-in
requestStatesealing viaMCP::Server::RequestStateSecurity(#496) - Mirror
x-mcp-headertool parameters intoMcp-Param-*headers per SEP-2243 (#498) - Stamp the required cache hints on modern cacheable results per SEP-2549 (#499)
- Drive multi round-trip
input_requiredresults on the client per SEP-2322 (#500) - Fulfill
input_requiredresults on the legacy wire per SEP-2322 (#501)
- Align modern envelope validation with the finalized specification (#491)
- Require the
Mcp-Methodheader on the modern path (#492) - Bound server-to-client requests with a timeout (#502)
- Refuse server-to-client requests in the modern lifecycle per SEP-2575 (#503)
- Bound the total wait across SSE reconnection attempts (#504)
- Bound automatic pagination in the MCP client (#505)
- Reject modern-removed methods before the connection era locks (#511)
- Stop negotiating modern protocol versions through the
initializehandshake (#516)
- Warn on modern client connects that declare the Roots or Sampling capabilities deprecated per SEP-2577 (#406, #516)
- Stop leaking exception messages to clients via JSON-RPC error data (#486)
- Return Invalid Params for unknown prompts and missing prompt arguments (#517)
- Restrict OAuth discovery to same-origin metadata URLs and refuse private-network destinations
- Support 2026-07-28 as the Latest Protocol Version (#476)
- Expose server tool annotations on MCP::Client::Tool (#445)
- Preserve explicit tool response content (#469)
First stable release. The public API is now stable: breaking changes ship only in major releases, apart from the narrow exceptions documented in VERSIONING.md.
- Add
ROADMAP.mdoutlining the path toward SEP-1730 (#465) - Add
VERSIONING.mddocumenting the versioning and breaking-change policy (#466)
- Support SEP-990 Cross-App Access via ID-JAG and the
jwt-bearergrant (#454) - Support client-side sampling via
MCP::Client#on_sampling(#458)
- Reject notifications carrying an unknown or expired session (#455)
- Handle an
initializerequest sent without an ID (#456) - Bound client-side message buffering in the HTTP transport (#459)
- Lowercase response header names in
StreamableHTTPTransport(#460)
- Support SSE reconnection per SEP-1699 in the HTTP client transport (#426)
- Validate the RFC 9207
issauthorization response parameter per SEP-2468 (#431) - Support
private_key_jwtclient authentication for theclient_credentialsgrant (#432) - Recognize multi round-trip
input_requiredresults per SEP-2322 (#433) - Add opt-in
ttlMs/cacheScopecache hints to List and Read results per SEP-2549 (#436) - Add
server/discoverand stateless lifecycle error codes per SEP-2575 (#438) - Associate server-to-client requests with the originating client request per SEP-2260 (#440)
- Add server-side MCP Apps helpers per SEP-1865 (#441)
- Support client-side elicitation with SEP-1034 schema defaults (#443)
- Support class-based Resource and ResourceTemplate definitions (#447)
- Bind stored client credentials to the authorization server issuer per SEP-2352 (#439)
- Handle non-object JSON-RPC messages without raising (#448)
- Perform SSE stream writes outside the session mutex (#449)
- Validate required params for
initializeandresources/subscribe(#451)
- Add a session-ownership hook to
StreamableHTTPTransport
- Require calling
MCP::Client#connectbefore sending requests on stdio transport (#427)
- Annotate Roots, Sampling, and Logging APIs as deprecated per SEP-2577 (#429)
- Fix an incorrect
result: nullresponse to an id-bearing notification message (#435) - Validate
HostandOriginheaders to prevent DNS rebinding per MCP 2025-11-25 - Bound stateful session retention to prevent an initialize-flood DoS
- Bound stdio frame reads with
max_line_bytes - Bound request body and frame reads to prevent memory-exhaustion DoS
- Add
audiencerole validation forMCP::Annotationsper MCP specification (#422) - Send SEP-2243
Mcp-MethodandMcp-Nameheaders per MCP specification (#423) - Support client-side
notifications/cancelledper MCP specification (#425)
- Conform Tool Schemas to JSON Schema 2020-12 per SEP-2106 (#417)
- Fix a
SyntaxErroron Ruby 2.7.0 caused by arguments forwarding syntax (#419)
- Support capability extensions per SEP-2133 (#405)
- Add range validation for
MCP::Annotations#priorityper MCP specification (#410) - Isolate stateless requests in ephemeral sessions per SEP-2567 (#415)
- Set OIDC
application_typeon Dynamic Client Registration per SEP-837 (#408) - Fall back to legacy 2025-03-26 OAuth discovery for servers without PRM (#414)
- Support W3C Trace Context Propagation via
_metaper SEP-414 (#397) - Support OAuth
client_credentialsgrant in OAuth client (#399) - Add
annotationsfield toMCP::ResourceandMCP::ResourceTemplateper MCP specification (#403)
- Re-run OAuth flow on 403
insufficient_scope(step-up) (#368) - Speed up
Tool::Schemavalidation by 5x to 100x (#369) - Use JSON-RPC error envelope for
StreamableHTTPTransporterrors (#371) - Pin RFC 8414 default well-known suffix per SEP-2351 (#395)
- Default missing
MCP-Protocol-Versionto2025-03-26inStreamableHTTPTransport(#392)
- Preserve the request ID in invalid request error responses (#400)
- Standardize Resource Not Found errors on -32602 with URI data per SEP-2164 (#402)
- Support Client ID Metadata Documents in OAuth client (#361)
- Request
offline_accessscope when supported (#365) - Add
sizefield toMCP::Resourceper MCP specification (#393)
- Support server-to-client
pingper MCP specification (#358)
- Warn on implicit stdio initialization (#338)
- Cache
Tool::Schemavalidation to avoid re-validating identical schemas (#363)
- Fix case-sensitive
Acceptheader comparison (#359)
- Add OAuth 2.1 client support for MCP authorization flow (#353)
- Validate
MCP-Protocol-Versionheader inStreamableHTTPTransport(#347) - Reject duplicate
initializerequests (#350) - Reject non-Hash JSON-RPC bodies in
StreamableHTTPTransport(#354)
- Add opt-in tool output validation (#344)
- Advertise JSON Schema 2020-12 dialect on emitted tool schemas (#342)
- Preserve client tool output schemas (#343)
- Fix missing
output_schemaargument indefine_toolAPI (#301)
- Support
notifications/cancelledper MCP specification (#332) - Add client-level
connectfor initialize handshake (#327) - Add client-level
connecthandshake to stdio transport (#336)
- Return tool argument validation failures as tool execution errors (#333)
- Remove obsolete
MCP::Transportsmodule (#331)
- Support pagination per MCP specification (#320)
- Support resource subscriptions per MCP specification (#313)
- Add
roots/listandnotifications/roots/list_changedsupport (#315) - Support JSON response mode for
StreamableHTTPTransport(#328) - Add HTTP client close for explicit session termination (#326)
- Track
Mcp-Session-Idand protocol version in HTTP client (#325) - Support
pingclient API per MCP specification (#324)
- Handle 202 Accepted response in HTTP client (#323)
- Parse SSE responses in HTTP client via
event_stream_parser(#322)
- Make
StreamableHTTPTransporta Rack application (#263) - Support
elicitation/createper MCP specification (#312) - Add
around_requesthook for request instrumentation (#309) - Add
_metafield to resource, content, and result classes (#310)
- Remove
Server#create_sampling_messagedirect call (#311)
- Support customizing the Faraday client in
MCP::Client::HTTP(#306)
- Auto-set
server.transportinTransport#initialize(#305)
- Validate Content-Type on POST requests (#304)
- Support
sampling/createMessageper MCP specification (#282) - Support
completion/completeper MCP specification (#289)
- Support POST response SSE streams for server-to-client messages (#294)
- Return protocol errors for invalid arguments and server errors (#285)
- Fix client methods silently swallowing JSON-RPC error responses (#281)
- Close streams outside mutex in session cleanup (#291)
- Session expiry controls for
StreamableHTTPTransportviasession_idle_timeout:option (#268)
ServerSessionfor per-connection state (#275)
- Remove
Server#notify_progressbroadcast API (#276) - Remove undocumented handler override methods (#270)
- Reject POST requests without session ID in stateful mode (#274)
- Use accessor method in
server_context_with_metainstead of ivar (#273) - Reject duplicate SSE connections with 409 to prevent stream hijacking
- Allow
Client#call_toolto accept a tool name (#266)
- Return 404 for invalid session ID in
handle_delete(#261)
MCP::Client::Stdiotransport (#262)- Progress notifications per MCP specification (#254)
- Automatic
_metaparameter extraction support (#172) - CORS and Accept wildcard support for browser-based MCP clients (#253)
- Use
autoloadto defer loading of unused subsystems (#255) - Reduce release package size (#239)
- Return 404 for invalid session ID in
handle_regular_request(#257) - Use mutex-protected
session_exists?inhandle_regular_request(#258)
Content::EmbeddedResourceclass for embedded resource content type (#244)Content::Audioclass for audio content type (#243)$refsupport inTool::Schemafor protocol version 2025-11-25 (#242)- MCP conformance test suite (#248)
- Handle
Errno::ECONNRESETin SSE stream operations (#249) - Fix default handler return values to comply with MCP spec (#247)
- Fix
Prompt#validate_arguments!crash when arguments arenil(#246) - Return 202 Accepted for SSE responses per MCP spec (#245)
- Fix
Content::Image#to_hto returnmimeType(camelCase) per MCP spec (#241)
- Fix
Resource::Contents#to_hto use correct property names per MCP spec (#235) - Return JSON-RPC protocol errors for unknown tool calls (#231)
- Fix
logging/setLevelto return empty hash per MCP specification (#230)
loggingsupport (#103)- Protocol version negotiation to server initialization (#223)
- Tool arguments to instrumentation data (#218)
- Client info to instrumentation callback (#221)
resource_templatestoMCP::Client(#225)
- Extract
MCP::Annotationsinto a dedicated file (#224)
Resource::Embeddednot setting@resourceininitialize(#220)
- Update licensing to Apache 2.0 for new contributions (#213)
- Omit
iconsfrom responses when empty or nil to reduce context window usage (#212)
- Protocol specification version "2025-11-25" support (#184)
iconsparameter support (#205)websiteUrlparameter inserverInfo(#188)descriptionparameter inserverInfo(#201)additionalPropertiessupport for schema validation (#198)- "Draft" protocol version to supported versions (#179)
statelessmode for high availability (#101)- Exception messages for tool call errors (#194)
- Elicitation skeleton (#178)
prompts/listandprompts/getsupport to client (#163)- Accept header validation for HTTP client transport (#207)
- Ruby 2.7 - Ruby 3.1 support (#206)
- Make tool names stricter (#204)
- Symlink path comparison in schema validation (#193)
- Duplicate tool names across namespaces now raise an error (#199)
- Tool error handling to follow MCP spec (#165)
- XSS vulnerability in json_rpc_handler (#175)
- Client resources support with
resources/listandresources/readmethods (#160) _metafield support for Tool schema (#124)_metafield support for Prompttitlefield support for prompt argumentscall_tool_rawmethod to client for accessing full tool responses (#149)- Structured content support in tool responses (#147)
- AGENTS.md development guidance documentation (#134)
- Dependabot configuration for automated dependency updates (#138)
- Set default
contentto empty array instead ofnil(#150) - Improved prompt spec compliance (#153)
- Allow output schema to be array of objects (#144)
- Return 202 response code for accepted JSON-RPC notifications (#114)
- Added validation to
MCP::Configurationsetters (#145) - Updated metaschema URI format for cross-OS compatibility
- Client tools functionality and test coverage (#166)
- Client resources test for empty responses (#162)
- Documentation typos and incorrect examples (#157, #146)
- Removed redundant transport requires (#154)
- Cleaned up unused block parameters and magic comments
- Tool output schema support with comprehensive validation (#122)
- HTTP client transport layer for MCP clients (#28)
- Tool annotations validation for protocol compatibility (#122)
- Server instructions support (#87)
- Title support in server info (#119)
- Default values for tool annotation hints (#118)
- Notifications/initialized method implementation (#84)
- Make default protocol version the latest specification version (#83)
- Protocol version validation to ensure valid values (#80)
- Improved tool handling for tools with no arguments (#85, #86)
- Better error handling and response API (#109)
- JSON-RPC notification format in Streamable HTTP transport (#91)
- Errors when title is not specified (#126)
- Tools with missing arguments handling (#86)
- Namespacing issues in README examples (#89)
- Custom methods support via
define_custom_method(#75) - Streamable HTTP transport implementation (#33)
- Tool argument validation against schemas (#43)
- Server context is now optional for Tools and Prompts (#54)
- Improved capability handling and removed automatic capability determination (#61, #63)
- Refactored architecture in preparation for client support (#27)
- Input schema validation for schemas without required fields (#73)
- Error handling when sending notifications (#70)
Initial release in collaboration with Shopify