Skip to content

fix(deps): update dependency mcp/sdk to v0.10.0 - #607

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/mcp-sdk-0.x
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/mcp-sdk-0.x

Conversation

@renovate

@renovate renovate Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
mcp/sdk v0.9.0 → v0.10.0 age confidence

Warning

Some dependencies could not be looked up. Check the Dependency Dashboard for more information.


Release Notes

modelcontextprotocol/php-sdk (mcp/sdk)

v0.10.0

Compare Source

TL;DR
  • The SDK is split into the Mcp\Schema, Mcp\Client and Mcp\Server namespaces, ahead of separate packages: most imports change
  • PHP 8.2 or higher is required, and JSON-RPC batches are gone
  • The client authorizes with OAuth: authorization code, client credentials, private_key_jwt and experimental cross-app access
  • Handshake-era servers over HTTP run without sessions unless Builder::setSession() is called, and sessions belong to the principal that opened them
  • Request handlers can run under an event loop (Revolt, ReactPHP) with the opt-in Builder::setFiberScheduler(), and stream their responses
  • Tool arguments keep their JSON types, and arguments that don't fit are answered with -32602
  • stdio works on Windows, in the server and the client
Namespaces
  • [BC Break] Split the root namespace into Mcp\Schema, Mcp\Client and Mcp\Server to prepare slicing them into separate packages; update your imports. Mcp\Schema depends on nothing, Mcp\Client and Mcp\Server depend on Mcp\Schema only — enforced by Deptrac (make deptrac).
    • Mcp\Server => Mcp\Server\Server
    • Mcp\Client => Mcp\Client\Client
    • Mcp\Capability\* => Mcp\Server\Capability\* (Attribute, Completion, Discovery, Formatter, Logger, Registry, Tool, plus Registry and RegistryInterface)
    • Mcp\Event\* => Mcp\Server\Event\*
    • Mcp\JsonRpc\MessageFactory => Mcp\Schema\MessageFactory
    • Mcp\Server\Stateless\RequestMeta => Mcp\Schema\RequestMeta
  • [BC Break] Distribute Mcp\Exception\* across the three namespaces.
    • Mcp\Exception\{ExceptionInterface,InvalidArgumentException,LogicException,RuntimeException} => the same names under Mcp\Schema\Exception\, Mcp\Client\Exception\ and Mcp\Server\Exception\ — pick the one matching the package you catch around
    • Mcp\Exception\Exception => Mcp\Client\Exception\Exception and Mcp\Server\Exception\Exception
    • Mcp\Exception\{InvalidInputMessageException,MissingRequestMetaException} => Mcp\Schema\Exception\*
    • Mcp\Exception\{ConnectionException,ElicitationException,RequestCancelledException,RequestException,RootsException,SamplingException,TimeoutException} => Mcp\Client\Exception\*
    • Mcp\Exception\{BadMethodCallException,ClientException,ConfigurationException,ContainerException,InvalidCursorException,MissingRequiredClientCapabilityException,NotFoundExceptionInterface,PromptGetException,PromptNotFoundException,RegistryException,RequestStateException,ResourceNotFoundException,ResourceReadException,ServiceNotFoundException,SessionLockException,SessionLockTimeoutException,SessionStoreException,ToolCallException,ToolNotFoundException} => Mcp\Server\Exception\*
    • Mcp\Exception\HandlerNotFoundException is removed; it was unused.
    • Mcp\Server\Exception\ExceptionInterface and Mcp\Client\Exception\ExceptionInterface extend Mcp\Schema\Exception\ExceptionInterface, so catching the latter still catches any SDK exception.
    • The named exceptions extend their package's RuntimeException/InvalidArgumentException instead of the global ones, so catch (Mcp\Server\Exception\RuntimeException) reaches ToolCallException, PromptGetException and friends.
  • [BC Break] Move ExtensionInterface::getRequestHandlers() to the new Mcp\Server\Extension\ServerExtensionInterface, so Mcp\Schema does not reference Mcp\Server. AbstractExtension no longer defines it. An extension that serves its methods must implement the new interface, or Builder::enableExtension() does not register its handlers.
General
  • [BC Break] Require PHP 8.2 or higher, as PHP 8.1 reached its end of life.
  • [BC Break] Remove JSON-RPC batch support, as MCP dropped batching in 2025-06-18. The server refuses a JSON array with -32600, and HTTP status 400, on every revision, including 2025-03-26, which requires batches and which the server still negotiates. The client drops one with a warning.
  • [BC Break] Refuse OAuth URLs, in the server and the client, whose host is 0.0.0.0, ::, an IPv4 address within IPv6 or an IPv4 address in a non-standard form (e.g. 127.1), or that hold percent-encoding or non-ASCII characters (give an internationalized name in punycode). In the server, this applies to the issuer, the jwks_uri and the resource.
  • [BC Break] Require phpdocumentor/reflection-docblock ^6.0, with phpdocumentor/type-resolver ^2.0 and phpstan/phpdoc-parser ^2.0: older versions cannot parse types like non-empty-array<T>, and the SDK silently ignored such a @param. Require psr/container ^1.1, as Container is not compatible with 1.0.
  • ext-fileinfo is optional: without it, creating content from a file without a MIME type throws a LogicException.
Server
  • Sessions
    • [BC Break] Serve handshake-era clients over Streamable HTTP without sessions unless Builder::setSession() is called, instead of falling back to an InMemorySessionStore that PHP-FPM and php -S lose between requests. initialize is answered without Mcp-Session-Id, later requests run on an EphemeralSessionInterface that is never stored, and GET and DELETE get 405, so a scaled-out server needs no shared session store or lock. The protocol version comes from the MCP-Protocol-Version header. Stdio and the 2026-07-28 era are not affected. Without a session, requests to the client (elicitation, sampling, roots), Protocol::sendRequest(), Protocol::sendNotification() and saving the session throw a SessionRequiredException, answered with -32603 if a handler does not catch it. Input-required results (unless withoutInputRequiredShim() is used), resources/subscribe, resources/unsubscribe and logging/setLevel get -32603, initialize offers neither resource subscriptions nor logging, log messages are dropped and the client capability checks of ClientGateway return false. Keep sessions with e.g. setSession(new InMemorySessionStore()) in a long-running process.
    • [BC Break] Bind handshake-era sessions to the principal that opened them: AccessToken::getPrincipal(), or the StreamableHttpTransport::PRINCIPAL_ATTRIBUTE request attribute when set. A request of another principal, or to a session opened before the upgrade, gets 404. Protocol::processInput(), the transports' handleMessage(), handleSessionEnd(), handlePostRequest() and handleDeleteRequest(), and the onMessage() and onSessionEnd() listeners take a ?string $principal as last argument.
    • [BC Break] Add SessionInterface::isUnreadable(), which a custom session implements. A session the store holds without attributes, because a handler cleared it or its data is corrupt, gets 404 so the client opens a new one; only a session the store fails to return gets 503. FileSessionStore::exists() no longer reports a zero-byte file.
    • [BC Break] Answer a DELETE of an unknown or ended session with 404 instead of 200, as the spec requires: Protocol::destroySession(), BaseTransport::handleSessionEnd() and the listener of TransportInterface::onSessionEnd() return false for it. A listener that returns nothing keeps the 200.
    • [BC Break] Answer malformed JSON and an initialize sent with a session id with HTTP 400, and a failure before the session is checked with 500, instead of 200. Such an answer no longer carries, or removes, the messages queued for the session in Mcp-Session-Id.
    • Add Psr6SessionStore and Psr6NotificationBus, the PSR-6 counterparts of Psr16SessionStore and Psr16NotificationBus. Builder::setDiscovery() also accepts a PSR-6 pool as $cache.
    • Fix Session::save() wiping the other keys of a session the store failed to read, e.g. on a Psr16SessionStore cache error: it returns false instead.
    • Fix FileSessionStore serving a partial session on Windows when a write could not replace the session file: it no longer falls back to copy(), retries the move for up to a second while another process holds the file, and write() returns false if the move fails.
    • Fix InMemorySessionStore::exists() reporting a session past its TTL as existing.
    • Fix Psr16NotificationBus subscribers missing notifications published after the bus stayed quiet for longer than its TTL.
  • Authorization and HTTP security
    • [BC Break] Answer 503 with Retry-After and without a Bearer challenge when JwtTokenValidator cannot fetch the signing keys or reaches the limit of the new RateLimitedJwksClient, which replaces the rateLimit of CachedKeySet. CorsMiddleware exposes Retry-After by default.
    • [BC Break] AuthorizationMiddleware answers a body over its maxBodyBytes with 413 and a JSON-RPC error, like the transports, instead of 400 with a WWW-Authenticate challenge, and logs a warning to its new $logger.
    • [BC Break] ScopePolicy::requiredFor() takes one decoded message, renames its parameter from $payload to $message and returns only the default scopes for a list.
    • Fix JwtTokenValidator throwing a TypeError, or answering 503, on a token naming a JWKS key whose type or curve firebase/php-jwt does not support (e.g. AKP, P-521 since its v7.1, X25519): the token is rejected with 401.
    • Fix SecureUrl rejecting plain http on *.localhost hosts (RFC 6761) and IPv4 loopback addresses other than 127.0.0.1, which broke local setups like http://shop.localhost:8080. Plain http is accepted for localhost, *.localhost, 127.0.0.0/8 and ::1; every other host still requires https.
    • Fix SecureUrl accepting a URL with a backslash, whitespace or control character, so http://evil.example\@localhost passed as loopback although browsers read evil.example as the host.
  • Tools, arguments and results
    • [BC Break] Keep the JSON types of tools/call arguments, so validation sees what the client sent: {} and {"0":"x"} are objects, [] and ["x"] are arrays. Nested objects in CallToolRequest::$arguments and in the arguments of ToolHandlerInterface::execute() are \stdClass, as they are for parameters without a type or typed mixed, object or \stdClass; parameters typed array still receive arrays at every level. array<string, T> parameters generate {"type": "object"}, and array<int, T> parameters accept a JSON array or object.
    • [BC Break] Reject tools/call whose arguments is not a JSON object, including [], with -32602, and one whose message has a member name starting with a NUL byte with -32600.
    • [BC Break] Answer params that do not fit a known method (e.g. tools/call without name) with -32602 instead of -32600, as JSON-RPC requires. Request::fromArray() throws the new InvalidParamsException, a subclass of InvalidArgumentException, and InvalidInputMessageException::isInvalidParams() tells the protocols which code to send.
    • [BC Break] Answer an argument that cannot be cast to its parameter type with -32602 instead of -32603, for tools, prompts and resources. The message names the parameter. A string parameter refuses a list or an object instead of receiving "Array", and an int-backed enum refuses a value with a fraction instead of truncating it. A missing required argument of a prompt, or of a tool that gets past schema validation, gets -32602 too.
    • [BC Break] Modern tools with an outputSchema send and validate a null result as structured content, with JSON text null alongside it. A tool whose schema does not allow null, including a void tool, returns a tool error instead of the text (null). ToolReference::extractStructuredContent() returns StructuredContent::Absent when there is nothing to send.
    • [BC Break] ReferenceHandler resolves a string handler that is not an invokable class from the container by service id. The container goes first, so a function handler no longer runs if the container has an entry of that name.
    • Fix an int parameter receiving a wrong value: a number beyond the integer range wrapped around, also past the schema's minimum or maximum, a numeric string beyond it became PHP_INT_MAX or PHP_INT_MIN, and "--5" became 0. They are refused now.
    • Fix a nullable parameter with a default (e.g. ?string $category = 'books') receiving the default when the client sends null.
    • Fix DocBlock types producing wrong schemas, so valid arguments were rejected with -32602: PHPStan/Psalm pseudo-types and literal types (non-empty-string, positive-int, 'asc'|'desc', array<array-key, T>) get the JSON Schema type of their base type, nullable and union keys of an array shape (array{nickname: string|null}) and union elements of a generic (array<string|null>) get their own types instead of object, and a null inside them, or in a class name, no longer makes the parameter nullable.
    • Fix tool name validation to follow the spec (1 to 128 characters of A-Z, a-z, 0-9, _, - and .): names of 65 to 128 characters no longer log a warning, names with a / or a trailing newline do.
    • Fix content, messages and contents being sent as a JSON object when the handler returned a non-list array, e.g. from array_filter(), and named keys in nested mixed arrays overwriting each other.
    • Fix a scalar structuredContent on 2026-07-28 going out as raw text in content: 'sunny' is sent as the JSON "sunny", as SEP-2106 requires.
    • Fix a resource whose text is not valid UTF-8 failing resources/read: ResourceResultFormatter sends it as a blob with the same MIME type.
    • Encode arrays and objects in results and TextContent as compact JSON instead of pretty-printed JSON.
    • Fix EnumCompletionProvider and ListCompletionProvider offering all values for the prefix "0".
    • Log the name of a called tool at info level; its arguments stay at debug level.
  • Discovery and registration
    • [BC Break] Builder::build() and Builder::buildStateless() throw a RuntimeException when setDiscovery() is used without symfony/finder and without a custom discoverer, instead of logging a warning and discovering nothing.
    • [BC Break] DocBlockParser::parseDocBlock() no longer accepts null; pass ReflectionMethod::getDocComment() as is.
    • Fix CachedDiscoverer returning the elements of a discovery that only differs in its name patterns, and clearCache() wiping the whole cache instead of its own entries.
    • Fix discovery skipping a class whose file declares an enum before it, once that file has been loaded.
    • Fix a manually registered element with a bad handler answering every list and call with -32602 and the internal message: the client gets -32603 and the ConfigurationException is logged.
  • Protocol 2026-07-28
    • [BC Break] Answer server/discover with DiscoverHandler, which Builder::addRequestHandler() can replace, and dispatch RequestEvent, ResponseEvent and ErrorEvent for it. A StatelessProtocol built without the Builder needs DiscoverHandler and a MessageFactory with DiscoverRequest.
    • Add a binder argument to Builder::setRequestState() that binds requestState to the request it was minted for, as the spec recommends: a state echoed on a request that binds to another value is refused like a forged one. OriginatingRequestBinder binds the principal, the method, the tool, prompt or resource and a digest of the arguments; A token without a subject fails closed, and RequestStateBinderInterface takes custom bindings. Without a binder, nothing changes.
    • Rotate the requestState signing key without failing calls in flight: Builder::setRequestState() and RequestStateCodec take a list of keys, the first signing and all of them verifying. The wire format is unchanged.
    • Fix a subscriptions/listen stream that can carry nothing, or that asks for resource subscriptions without a notification bus, being held open for its whole lifetime and pinning a PHP-FPM worker: it is acknowledged with an empty filter and closed right away. server/discover no longer announces resources.subscribe without a bus, unless setCapabilities() sets it. Adds NotificationFilter::isEmpty().
    • Fix a retried resources/read leaving out the required ttlMs and cacheScope, which the TypeScript client rejects: it always carries ttlMs: 0 and cacheScope: private. A ReadResourceResult no longer sends its own hints to handshake-era clients.
    • Fix StandardHeaderValidator accepting an Mcp-Param-* header that does not match the body: a fraction that rounds to an integer argument, object and array arguments, and integers outside ±(2^53-1) are refused with -32020.
    • Fix a request with "id": null being taken for a notification and left unanswered: it is refused with -32600.
    • Fix a result that cannot be encoded as JSON escaping as an uncaught JsonException or ending a stream without an answer: the client gets -32603, with HTTP status 500 or as the last frame of a stream, and the failure is logged and dispatched as an ErrorEvent.
    • Fix messages with a jsonrpc other than "2.0" being served: a 2026-07-28 request that skips MessageFactory, like subscriptions/listen, is refused with -32600, and Notification::fromArray() throws, as Request::fromArray() already did.
    • [BC Break] Remove Rev2025Codec, which nothing used.
  • Event loops and fibers
    • Add Builder::setFiberScheduler() and FiberSchedulerInterface: under an event loop (Revolt, ReactPHP's async), a handler awaiting I/O suspends its fiber for the loop, and the server waits through the scheduler until the handler suspends for the server or returns. The scheduler must wake the server on a later turn of the loop; the guide shows one for Revolt and one for ReactPHP.
    • Add CallbackStream::chunks(), which hands the SSE text of a streamed response, in both protocol eras, to a server that writes the body itself (e.g. amphp/http-server, react/http). On an empty chunk, the server waits on its loop before it asks for the next one, and dropping the generator ends the stream. While a handshake-era handler waits for the client, the server keeps serving its loop. The Revolt and ReactPHP examples stream these responses instead of answering 501.
    • Add Builder::setFiberContext() and FiberContextInterface to carry execution context, e.g. the active OpenTelemetry context, into handler fibers.
    • [BC Break] A subscriptions/listen stream read from StatelessProtocol::handle() no longer waits between polls or checks connection_aborted(): its consumer paces it, as StatelessResponder does.
    • [BC Break] StreamableHttpTransport no longer has the protected flushOutgoingMessages() and handleFiberTermination().
    • [BC Break] A handler calling \Fiber::suspend() itself with anything but a NotificationSuspension or RequestSuspension fails the request without a scheduler, instead of being resumed with null.
    • Fix the server resuming a fiber the runtime had suspended, which made the handler's await return early or throw and could terminate a Revolt loop. Without a scheduler, such a handler fails the request with a LogicException naming Builder::setFiberScheduler(). Protocol and StatelessProtocol take the scheduler as optional last constructor argument.
    • Fix what a handler throws while the runtime drives its fiber escaping to the runtime: it reaches the server, and what it throws while its fiber is discarded is logged.
    • Fix a request failing with 503, or a resumed one losing its session writes, while another request of the session holds the lock across an await: both wait for the lock on the loop.
    • Fix a handler still running after its request was answered firing ResponseEvent or ErrorEvent: what it throws is logged, and calling the ClientGateway throws.
  • stdio
    • Fix a stdio server on Windows ending by itself after about 13.5 hours without input: StdioTransport reads only once stream_select() reports input. Requires PHP 8.5 on Windows.
    • Fix a stdio server never answering a message that reaches it in several reads, such as a tool call with large arguments.
    • Fix a stdio server answering a notification it refuses, e.g. one sent before initialize: the refusal is only logged.
    • Fix maxLineBytes of StdioTransport discarding lines two bytes shorter than the limit, and StdioTransport throwing a ValueError on an input that cannot be selected, like php://memory.
Client
  • Authorization
    • Add OAuth authorization: pass OAuth::forApplication('My App')->build() as auth and the client answers a 401 with discovery, dynamic or pre-registered client identity, the authorization code flow with PKCE and a retry. It refreshes tokens, handles step-up challenges and treats the server's metadata as untrusted input. See Authorization.
    • Add OAuth::forServiceAccount() for the client credentials grant, setPrivateKeyJwt() for private_key_jwt client authentication, the experimental setCrossAppAccess() to reach a server through an enterprise identity, and setClientMetadataUrl() for a client ID metadata document. setExpectedIssuer() is required with setClientCredentials(), so a hostile MCP server can't receive the secret or an authorization code.
    • Add LoopbackAuthorizationHandler, HeadlessAuthorizationHandler, ConsoleAuthorizationHandler for a shell without a browser, and RedirectAuthorizationHandler with OAuthAuthenticator::finishAuthorization() to finish in a later request, such as a web callback. Credentials go to InMemoryCredentialStorage or FileCredentialStorage, which also implements PendingAuthorizationStorageInterface.
    • Add the auth parameter of HttpTransport and AuthenticatorInterface, to authenticate every request and retry the ones the server refuses. BearerToken sends a token you already have, and AuthenticatingHttpClient offers the same as a PSR-18 decorator.
    • [BC Break] A 401 or 403 answer to a request, a notification or a stream resumption fails with an AuthorizationException holding the WWW-Authenticate header, instead of a RequestException. Client::connect() wraps it in a ConnectionException, without a retry.
  • HTTP transport
    • [BC Break] Turn off redirects on a symfony/http-client client, discovered or passed, in HttpTransport, AuthenticatingHttpClient and OAuth. HttpTransport fails on every 3xx answer with an error that names the status and target, while AuthenticatingHttpClient returns it unchanged. A redirect to another origin used to receive the configured headers, like an API key, and on a 307 or 308 the body: rotate those credentials if an endpoint of yours may have redirected to a host you don't control. Configure the final URL of an endpoint reached through a redirect. A client you pass must not follow redirects to another origin.
    • [BC Break] Limit what HttpTransport reads of a response that is not an SSE stream to the new maxResponseBodyBytes (default: 8 MiB): a larger successful response fails the request, and an error body is cut at the limit.
    • Fix HttpTransport taking an Mcp-Session-Id from any response instead of only the answer to initialize, which replaced the session mid-way and, on 2026-07-28, made close() send a DELETE.
    • Fix HttpTransport not reading the listening stream during a request answered with plain JSON.
  • stdio on Windows
    • [BC Break] Start a stdio server without cmd.exe, which corrupted arguments containing ", % or !. A batch file like npx needs command: 'cmd' and args: ['/c', 'npx', ...].
    • Fix the stdio client hanging while the server stays silent, ignoring its timeouts. Requires PHP 8.5.
  • Fix Client::connect() leaving the previous transport open when called again.
Schema
  • [BC Break] MessageFactory::create() returns one MessageInterface and throws InvalidInputMessageException for invalid input, instead of returning a list of messages and exceptions.
  • [BC Break] Remove MessageFactory::DEFAULT_MAX_BATCH_SIZE and the $maxBatchSize parameter of MessageFactory::__construct() and MessageFactory::make(), whose first parameter is now $additional.
  • Add UnknownMethodException, a subclass of InvalidInputMessageException for a method the factory has not registered.
  • [BC Break] An x-mcp-header annotation that is not on a property reached through properties alone, e.g. under items, anyOf or $defs, or that is null, makes the tool definition invalid: Tool throws an InvalidArgumentException, and the client drops such a tool from tools/list and refuses a sampling request carrying one.
  • [BC Break] CallToolResult keeps an explicit structuredContent: null when constructed and decoded. Code that passed null to omit it must leave the argument out (use meta: $meta for metadata) or pass StructuredContent::Absent. An explicit null that violates the output schema becomes a tool error, and strict handshake-era clients can reject it. hasStructuredContent() tells absent content from null; success() and error() stay without structured content.

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • Only on Sunday and Saturday (* * * * 0,6)
  • Automerge
    • At any time (no schedule defined)

🚦 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.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate

renovate Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor Author

⚠️ Artifact update problem

Renovate failed to update an artifact related to this branch. You probably do not want to merge this PR as-is.

♻ Renovate will retry this branch, including artifacts, only when one of the following happens:

  • any of the package files in this branch needs updating, or
  • the branch becomes conflicted, or
  • you click the rebase/retry checkbox if found above, or
  • you rename this PR's title to start with "rebase!" to trigger it manually

The artifact failure details are included below:

File name: composer.lock
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires mcp/sdk v0.10.0 (exact version match), found mcp/sdk[v0.10.0] but these were not loaded, likely because it conflicts with another require.
  Problem 2
    - symfony/mcp-bundle is locked to version v1.0.0-BETA1 and an update of this package was not requested.
    - symfony/mcp-bundle v1.0.0-BETA1 requires mcp/sdk ^0.9 -> found mcp/sdk[v0.9.0] but it conflicts with your root composer.json require (v0.10.0).


@renovate
renovate Bot force-pushed the renovate/mcp-sdk-0.x branch 2 times, most recently from a3cc3a3 to 7d0c8f0 Compare November 12, 2026 12:57
@renovate
renovate Bot force-pushed the renovate/mcp-sdk-0.x branch from 7d0c8f0 to 6642d6b Compare November 12, 2026 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants