Repository navigation
fix(deps): update dependency mcp/sdk to ^0.9 - #93
Merged
Merged
Conversation
renovate
Bot
force-pushed
the
renovate/mcp-sdk-0.x
branch
8 times, most recently
from
October 23, 2026 20:05
9d393d8 to
12283f2
Compare
renovate
Bot
force-pushed
the
renovate/mcp-sdk-0.x
branch
from
October 23, 2026 21:53
12283f2 to
b39456d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
^0.8→^0.9Release Notes
modelcontextprotocol/php-sdk (mcp/sdk)
v0.9.0Compare Source
TL;DR
Server
Builder::setSessionLock(), serializing the concurrent requests of one session so none of them overwrites what another saved.FileSessionLocklocks withflock()next toFileSessionStore, whosegc()also removes stale lock files;SymfonyLockSessionLockuses a symfony/lock store for sessions shared across machines;SessionLockInterfacetakes custom ones. A request that cannot get the lock in time, or hits a failing lock backend, is answered with503and a JSON-RPC error (SessionLockTimeoutExceptionandSessionLockException).Protocol::__construct()takes the lock as optional last argument.Session::save()writes only the keys it changed, onto what the store holds at that point. Writes of a handler after it resumed from an elicitation, sampling request or notification are saved too, and a session deleted while one of its requests still ran no longer comes back when that request saves it.Psr16SessionStorecache being answered as "session not found" (404): the failure is logged and answered with503, so clients retry instead of re-initializing.SessionStoreInterface::exists()may throw the newSessionStoreException.ClientGateway::progress()takes the token from its own request.StdioTransportkeeps every suspended fiber and resumes each with the answer to its own request. A call's notifications now go out before its response.Protocol::sendRequest(),Protocol::checkResponse()andProtocol::handleFiberYield()take and return aUuidinstead of anint, as does the response finder of a transport;handleFiberYield()returns the id of the request it sent. A transport passes the suspended\Fiberas last argument to the pending-requests provider and the fiber yield handler, andStreamableHttpTransport::handleFiberTermination()takes the terminated\Fiberas its first argument.TransportInterface::send()receives every response of a handler that does not suspend, and the outgoing queue only carries server-initiated requests and notifications; a custom transport must deliver each message it gets, as a batch gets several.StreamableHttpTransportbundling the session's queued messages (e.g.notifications/resources/updated) with a request's response into a JSON array, which clients on2025-06-18and later reject: such a request is answered as SSE, the queued messages first. A POST of only responses or notifications is accepted with202and no body, and the queued messages stay.Protocol::checkResponse()expires them with theRequest timed outerror, entries of a vanished stream are dropped a minute past their timeout, and an answer to a request the server is not waiting on is dropped instead of stored.nullwhen the transport fails to store what it sends to the client;ClientGateway::notify()then throws aClientException.OAuthProxyMiddleware,ClientRegistrationMiddleware,ClientRegistrarInterface,ClientRegistrationException,JwksProvider,JwksProviderInterface,OidcDiscoveryInterfaceand the OIDC metadata policies;OidcDiscoveryis internal.OAuthRequestMetaMiddlewarewithRequestContext::getAccessToken(), returning the validatedServer\Authorization\AccessToken;AuthorizationResult::allow()takes anAccessTokeninstead of request attributes.Protocol::processInput(),BaseTransport::handleMessage()andStreamableHttpTransport::handlePostRequest()take an?AccessToken $accessTokenparameter, as does theTransportInterface::onMessage()listener as fourth argument.JwtTokenValidatorfinal with a single issuer instead of a list of issuer aliases, and a$keysset (e.g.CachedKeySet);JwtTokenValidator::fromIssuer()discovers and caches keys in a PSR-6 pool, refetching on unknown key ids. Thealgallowlist is enforced,$tokenTypeand$leewayare added,requireScopes()is removed in favour ofScopePolicy.ScopePolicyas third argument ofAuthorizationMiddleware, answering403 insufficient_scopeper method and tool, with scope hierarchies; theresource_metadatachallenge URL comes from the configured resource instead of theHostheader.ProtectedResourceMetadatarequires$resource, serves at the path derived from it (RFC 9728 §3.1) and requires https except for loopback hosts; drops localized, policy, ToS, extra fields and$metadataPaths.401behindAuthorizationMiddleware, which now letsOPTIONSrequests pass without a token. The defaultCorsMiddlewareallowsMcp-Method,Mcp-NameandMcp-Param-*and exposesWWW-Authenticate; an$allowedHeadersentry ending in*allows each requested header with that prefix.DnsRebindingProtectionMiddlewarealways validatesHost(a missing one is rejected) and checksOriginseparately against the newallowedOrigins, including scheme and port and empty by default. Same-origin requests are always allowed and both lists accept['*'].allowedOriginsis the new second constructor argument; pass the factories by name.PassthroughMiddlewareto optStreamableHttpTransportout of its default middleware without the warning an empty$middlewarelist logs.StdioTransportsettles the era on the client's first request and serves2026-07-28requests,subscriptions/listenandnotifications/cancelledon the one channel.StatelessAwareTransportInterfacedeclaressetHandshakeVersions(), so a server without the modern era names only the revisions it negotiates when refusing a2026-07-28request.initializeon a2026-07-28-only endpoint with-32022naming the served revisions, and a request without a session on the handshake leg with its id.Mcp-Param-*header whose mirrored argument is absent from the body with-32020, instead of accepting the request (SEP-2243).listChangedonly where list changes are delivered: never in theinitializeresult, and onserver/discoveronly when the registry publishes them to a notification bus.RegistryInterfacedeclarespublishesListChanges(), whichRegistryanswers withtruewhen its dispatcher is aPublishingEventDispatcher; a decorating registry forwards the answer of the one it wraps.structuredContentagainst the tool'soutputSchema, which the specification requires the server to honour. A mismatch is answered with aCallToolResultcarryingisError: trueinstead of the non-conforming value, matching the TypeScript, Python and Java SDKs. Skipped when the tool declares nooutputSchema, when the result carries nostructuredContent, and when the result is already an error. Returnnew \stdClass()for an empty object, since[]is sent as an array.Builder::setSchemaValidator()to configure the validator used fortools/callinput and output, e.g. with a resolver for external$refschemas.SchemaValidatortakes an optionalOpis\JsonSchema\Validatoras its first constructor argument, moving$loggerto second (passlogger:by name), andvalidateAgainstJsonSchema()no longer validates an empty array as an object (passnew \stdClass()).SchemaValidatorfalling back to opis' wording for camelCase keywords likeminLength,multipleOforadditionalProperties, and reporting a pointer relative to the$reftarget inside$defs. Clients now see the SDK's messages and the full pointer.providerClassargument of#[CompletionProvider]. Useprovider:, which takes the same class-string and is now the first positional argument.ClientGateway::log()drops messages below the level set withlogging/setLevel, and belowwarningwhile the client has set none, andClientLoggerhonours a lower level a2026-07-28request asks for withio.modelcontextprotocol/logLevel.ClientLoggertakes only theClientGateway, which applies the level itself.Protocolfrom logging full JSON-RPC payloads (tool arguments, client replies) at info level: info records now carry only the method and id, the raw message is logged at debug level.RequestEvent,ResponseEventandErrorEventnot being dispatched for2026-07-28requests, andResponseEventandErrorEventnot when a suspended fiber completes.ServerRequestEventandClientResponseEventfor handshake-era server-initiated requests (elicitation/create,sampling/createMessage) and the client's replies.tools/callin long-lived processes like FrankenPHP workers or stdio servers.StdioTransportanswering a request on an idle connection only after ~50ms: it now waits on the input withstream_select()instead of sleeping.Client
2026-07-28, andconnect()probes withserver/discoverand falls back to theinitializehandshake on2025-11-25when the server does not speak the modern era.Builder::setFallbackProtocolVersion()picks the fallback revision, ornullfor a modern-only client. Against a server that speaks both, a default client now settles on2026-07-28, where a server can no longer sample or list roots throughClientGateway; return those asks as anInputRequiredResultinstead, or callBuilder::setProtocolVersion(ProtocolVersion::V2025_11_25)on the client.2026-07-28connection,Client::setLoggingLevel()stamps the level on every following request,Client::ping()sendsserver/discoverandClient::sendRootsListChanged()sends nothing.listenoption to the client'sHttpTransport, opening the standalone GET stream on which a 2025-era server sends requests and notifications outside of a client request, likeroots/list. Needs a PSR-18 client that streams response bodies, such assymfony/http-client.Client::isConnected()turns false.RequestException: it now throwsTimeoutExceptionand sendsnotifications/cancelledfor the abandoned request.TimeoutExceptionextendsRequestExceptioninstead ofException, so acatch (RequestException)now also catches timeouts.ClientStateInterface::getPendingRequests()reportstimestampas afloatfrommicrotime(true)instead of anint.HttpTransportwith a GET carryingLast-Event-ID, honouring the server'sretryinterval. Configure attempts and fallback backoff withReconnectPolicy, the scheduling clock with any PSR-20ClockInterface(default: a monotonic clock). Protocol version2026-07-28does not resume streams. A stream that ends without its response and without an event ID now fails the request instead of waiting for its timeout.onProgresscallback receiving progress for an earlier request that timed out: progress now only reaches the callback of the request whoseprogressTokenit carries.Client::getServerInfo(),getInstructions()andgetProtocolVersion()reporting the previous server's values after a reconnect, whether it failed or reached a2026-07-28server whoseserver/discoverleaves them out.Client::connect()rejecting an emptyserverInfo.version, as the Python SDK sends, and leaving the transport open when initialization fails on a malformed result: the transport is now closed and the failure wrapped in aConnectionException.HttpTransportfailing to connect through an amphp-based PSR-18 client, like Symfony'sPsr18Client(new AmpHttpClient()), whereconnect()failed at the init timeout withFutureIterator::consume(): Return value must be of type ?array: requests are now sent from outside the request fiber, where the event loop runs.Client::getServerCapabilities(), returning what the server declared ininitializeor, from2026-07-28on, inserver/discover.ClientStateInterfacedeclaressetServerCapabilities()andgetServerCapabilities(), and itssetProtocolVersion()andsetServerInfo()acceptnull, which a custom implementation has to follow.HttpTransport::getSessionId()to read the server-mintedMcp-Session-Id: a request-scoped caller can persist it and pass it back through the constructor's$headerson a later transport. Alwaysnullon2026-07-28, which removed protocol-level sessions.HttpTransportomitting theMCP-Protocol-Versionheader on requests after theinitializehandshake, including the closingDELETE.Client::getPrompt()without arguments sending"arguments": [], which servers validating the spec's object type (e.g. the TypeScript SDK) reject: empty arguments are now omitted.\JsonExceptionfor request params it cannot encode (e.g. invalid UTF-8): it now throwsInvalidArgumentException, and a request handler result it cannot encode is answered with an internal error instead of no answer.Schema
MessageInterface::PROTOCOL_VERSIONto2026-07-28. UseProtocolVersion::latestHandshake()where a handshake revision is needed, e.g. in aninitializeanswer.Tool::$execution(ToolExecutionwith aTaskSupportenum) andServerCapabilities::$tasks, which were dropped when parsing a 2025-11-25 server'stools/listandinitializeresults.Toolinput schema whosepropertiesis not an object or whoserequiredis neither a list nornull, instead of silently replacing the member. Reject acompletion/completewhoseargumentis missingnameorvalue, instead of completing against an empty prefix.ElicitationSchema::getDefaults(), returning the declareddefaultof each field to accept a form elicitation with.AbstractSchemaDefinitiondeclares an abstractgetDefault(), which a custom schema definition has to implement.Configuration
📅 Schedule: (in timezone Asia/Tokyo)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.