Every cacheable result went out as ttlMs 0, private — the letter of SEP-2549 and none of its point, with no way to change it. CachePolicy adds a default and per-method overrides; a resource can override per read. The conservative default stands, because public means a shared cache may serve one caller's answer to another.
18 KiB
18 KiB
Changelog
All notable changes to mcp/sdk will be documented in this file.
0.8.0
- Add
Mcp\Server\Wire\CachePolicy, set withBuilder::setCachePolicy(), to configure the SEP-2549 caching hints the 2026-07-28 lifecycle stamps on a cacheable result. The conservativettlMs: 0, cacheScope: privatestays the default, sincepubliclets a shared proxy serve one caller's answer to another and only the operator can make that call. AReadResourceResultmay also carry its ownttlMs/cacheScope, which win over the policy. - Add typed readers to
Mcp\Server\Stateless\InputContext:elicitResult(),samplingResult()androotsResult()deserialize a multi round-trip answer instead of handing back the raw arrayresponse()returns. A malformed answer reads as absent rather than throwing, so a handler asks again — which is what the spec says a server SHOULD do when the information it needs is still missing. - Answer a request over a response stream under the 2026-07-28 lifecycle:
StatelessProtocolruns handlers in a fiber, so$gateway->progress()and$gateway->log()work there as they do in the handshake era. The stream opens only if the handler actually emits something and the client'sAcceptadmitstext/event-stream, and the choice is made after the handler's first suspension, so a request that turns out to need-32021or-32602is still answered with the status the spec fixes for it. - Honour
io.modelcontextprotocol/logLevel(SEP-2575), which replaced thelogging/setLevelRPC: a request naming no level receives nonotifications/messageat all, one naming a level receives the messages at or above it. AddsLoggingLevel::severity()andLoggingLevel::isAtLeast(). - [BC Break] Answer a not-found subject with
-32602(Invalid params) instead of-32002, which the 2026-07-28 revision reserves and forbids emitting (SEP-2164).resources/readpicks the code from the revision serving the request —-32602with the uri inerror.datafrom2026-07-28on,-32002below.prompts/getfor an unknown prompt,completion/completefor an unknown reference andtools/callfor an unknown tool switch to-32602in every revision:-32002was never the code for those. AddsProtocolVersion::usesInvalidParamsForResourceNotFound(). - Add the multi round-trip requests pattern for the 2026-07-28 lifecycle (SEP-2322): a
tools/callorprompts/gethandler returningMcp\Schema\Result\InputRequiredResultcomes back asresultType: "input_required"carrying theinputRequestsit needs answered and an opaquerequestState; the client retries the same request withinputResponses, which the handler reads throughRequestContext::getInputContext().Mcp\Server\Stateless\RequestStateCodecsigns and time-bounds the state — set the key withBuilder::setRequestStateKey(). - Validate the standard MCP request headers under the 2026-07-28 lifecycle (SEP-2243):
Mcp\Server\Stateless\StandardHeaderValidator, set withBuilder::setHeaderValidator(), checks thatMcp-MethodandMcp-Nameagree with the body they travel with and that aMcp-Param-*mirrors the argument its tool markedx-mcp-header, answering-32020when they disagree. Intermediaries route on these headers, so a value contradicting the body has to be refused rather than ignored. - [BC Break]
Mcp\Schema\JsonRpc\Erroracceptsnullas its$id, andgetId()may return it. An error response whose id could not be read now omits the member instead of sending"id": ""— which claimed the peer had issued a request with an empty-string id. All thefor*()factories default tonull,fromArray()accepts a missing or explicitly-null id, andMessageFactorydecodes both as an id-less error rather than rejecting them. - Preserve the original request
idon an invalid-but-parseable message (-32600) instead of answering it id-less:InvalidInputMessageExceptionnow carries the recoverable id viagetRequestId()/setRequestId(), threaded fromMessageFactorythrough to the error response. - [BC Break] Add the extensions framework SEP-2133 defines, which MCP Apps sits on.
ExtensionInterface::getId()now returns the newMcp\Schema\Extension\ExtensionIdentifiervalue object instead of a string, which validates the identifier against the_metakey naming rules at construction time.ExtensionInterfacealso gainsgetMessages()/getRequestHandlers(), so an extension can contribute the message classes its methods decode into — without which its methods cannot be decoded at all — and the handlers serving them; extensions that only announce a capability can extend the newMcp\Schema\Extension\AbstractExtensionand skip both.MessageFactory::make()takes an$additionallist of message classes, andRequestHandlerInterface's result template is now covariant. - [BC Break] Drop the SDK-only name pattern on
ResourceDefinition/ResourceTemplate$name— the spec allows any string (its own examples usemain.rsandProject Files). URI/URI-template validation is unchanged. - Add
ClientGateway::supportsExtension(),Client\Builder::enableExtension(), andClientCapabilities::withExtensions()so clients can negotiate and check protocol extensions (e.g. MCP Apps) the same way servers already do. [BC Break]ServerExtensionInterfaceis replaced by the side-agnosticMcp\Schema\Extension\ExtensionInterface. - Deprecate Roots, Sampling and Logging per SEP-2577 (protocol revision
2026-07-28, earliest removal2027-07-28). They keep working but using them now triggers a deprecation notice — migrate to tool arguments/resource URIs, a direct LLM provider API, and stderr/OpenTelemetry respectively. - [BC Break] Gate
structuredContenton the negotiated protocol revision: a tool result that's a PHP list (or an object serializing to a JSON array) is now only sent asstructuredContenton protocol revisions2026-07-28+; older revisions omit it and keep the JSON-encoded value incontent. - Add protocol revision negotiation during
initialize: the server counter-offers a revision it supports, and the client now fails the handshake instead of continuing on an unagreed revision. AddsClient::getProtocolVersion()and the2026-07-28revision. - Add sampling-with-tools support: sampling requests can include tools and tool-choice preferences, messages support tool-use/tool-result content blocks, and clients advertise
sampling.context/sampling.toolsviaClientGateway::supportsSamplingTools()/supportsSamplingContext(). A request that violates the spec's tool-flow rules is now rejected with a proper JSON-RPC error instead of being left unanswered. - [BC Break]
SamplingMessage::$contentandCreateSamplingMessageResult::$contentmay now be a list of content blocks instead of just one — use the newgetContentBlocks()to always get a list. - [BC Break]
CreateSamplingMessageResultnow rejects any role other thanassistant, and rejects empty content, per spec. - Close the remaining schema gaps for
2025-06-18/2025-11-25and add the2026-07-28surface (SEP-2106): url-mode elicitation (ClientGateway::elicitUrl()/supportsElicitationUrl()),Implementation::title, andoutputSchema/structuredContentaccepting any JSON value rather than only objects. - Add
Mcp\Schema\Content\ResourceLink— reference a resource by URI/name in tool results and prompt messages without embedding its contents. - Add client-side Roots support:
RootsCallbackInterface,Client::sendRootsListChanged(), and server-sideClientGateway::listRoots()/supportsRoots(). - Add
ClientGateway::supportsSampling()to check the client's advertised capabilities before sending a sampling request, matchingsupportsRoots()/supportsElicitation(). - Fix empty tool/resource schemas serializing as
[]instead of{}ininputSchema/outputSchema. - Fix
PromptResultFormatterdroppingannotations,_meta, andmimeTypewhen a prompt generator returns content as a plain array. - Add
annotationssupport toImageContent, matchingTextContent/AudioContent.
0.7.0
- Add client-side elicitation support:
ElicitationCallbackInterface,ElicitationRequestHandler, andElicitationExceptionlet clients respond to server elicitation requests. - Defer element loading to the first registry read: loaders now run at request time (first
has*/get*call) instead of eagerly atBuilder::build(), fixing empty registries under persistent runtimes (e.g. FrankenPHP worker mode) where a loader's data source is not ready at build time. AddsBuilder::setLazyLoading()(default on), a publicRegistry::load(), and an optionalLoaderInterfaceconstructor argument onRegistry. - [BC Break] Element loading is lazy by default: loader failures now surface on the first request rather than at
Builder::build(), andinitializeadvertises capabilities from the configured sources rather than the loaded registry. CallBuilder::setLazyLoading(false)to restore eager build-time loading. - Allow
[$instance, 'methodName']as an element handler inBuilder::addTool(),addResource(),addResourceTemplate(), andaddPrompt(). Unblocks handlers with constructor dependencies that the container-lessnew $className()fallback cannot build. - Always emit an
itemsschema for array tool parameters: untyped arrays getitems: {}and nullable typed arrays (e.g.string[]|null) keep their element type. Fixes strict clients rejecting tools with "array type must have items" (#151). - Harden JSON-RPC input parsing: single-message vs batch is now decided from the decoded JSON type (object → single, list array → batch) instead of the raw first byte. Scalars, empty payloads, and non-object batch elements are surfaced as
InvalidInputMessageExceptionentries instead of triggering warnings or aTypeError. - Add
maxBatchSize(default100) toMessageFactory— oversized JSON-RPC batches are rejected before any message is constructed, guarding against amplification. - Add
maxBodyBytes(default 4 MiB) toStreamableHttpTransport— POST bodies exceeding the cap are rejected with413. Unknown-size/chunked bodies are read incrementally and stopped at the cap so they cannot exhaust memory. - Reject malformed
Mcp-Session-Idheaders with a400response: a repeated header or a value that is not a valid UUID is now rejected up front instead of surfacing as an uncaughtUuid::fromString()error. - Extract RFC 9728 metadata serving into
ProtectedResourceMetadataHandler, a transport-neutral PSR-15RequestHandlerInterfacethat can be mounted directly as a Symfony/Laravel controller;ProtectedResourceMetadataMiddlewarenow delegates to it (no BC break).
0.6.0
- Add
Builder::add(Tool|ResourceDefinition|ResourceTemplate|Prompt $definition, ElementHandlerInterface $handler)for explicit registration of elements whose schema is only known at runtime. - Add handler interfaces
ToolHandlerInterface,ResourceHandlerInterface,ResourceTemplateHandlerInterface,PromptHandlerInterface, and theElementHandlerInterfacemarker. - [BC Break] Renamed
Mcp\Schema\ResourcetoMcp\Schema\ResourceDefinition. No alias. - [BC Break] Renamed
Mcp\Capability\Registry\Loader\ArrayLoadertoMcp\Capability\Registry\Loader\ReflectedElementLoader. - [BC Break] Bump default protocol version to
2025-11-25 - Add support for MCP Apps extension in schema and server
- Add
extensionstoServerCapabilitiesandClientCapabilitiesandBuilder::enableExtension() - Allow overriding the default name pattern for Discovery
- Add configurable session garbage collection (
gcProbability/gcDivisor) - Add optional
titlefield toResourceDefinitionandResourceTemplatefor MCP spec compliance - Add
ChainLoaderto compose multipleLoaderInterfaceimplementations via explicit ordering. - Add
RegistryInterface::unregisterTool(),unregisterResource(),unregisterResourceTemplate(),unregisterPrompt()— idempotent removals. - Add
RegistryInterface::hasTool(),hasResource(),hasResourceTemplate(),hasPrompt()— by-name existence checks. DiscoveryLoadernow refreshes only its own previously written entries; manual registrations (viaBuilder::addTool()etc. or runtime$registry->registerTool()calls) survive rediscovery, and a same-name manual registration takes precedence over discovery on collision.- [BC Break] Removed
ElementReference::$isManualpublic property and thebool $isManualparameter from all*Referenceconstructors. Origin tracking is no longer carried on the element; manual-over-discovered precedence is encoded by loader execution order. - [BC Break]
RegistryInterface::registerTool(),registerResource(),registerResourceTemplate(),registerPrompt()lost their trailingbool $isManual = falseparameter. Callers using positional arguments must drop the flag. - [BC Break] Removed
RegistryInterface::clear(),getDiscoveryState(),setDiscoveryState(). Rediscovery now goes throughDiscoveryLoader::load()directly. Registry::register*()semantics changed to plain last-write-wins (overwrites silently) and the methods now return the stored*Reference. The previous "discovered registration is ignored when a manual one already exists" precedence rule still applies, but is now enforced byDiscoveryLoadervia reference-identity tracking — and still emits a debug log when a discovery is skipped due to a conflicting registration.- Add optional
titleparameter toBuilder::addResource()andBuilder::addResourceTemplate()for MCP spec compliance - [BC Break]
Builder::addResource()signature changed —$titleparameter added between$nameand$description. Callers using positional arguments must switch to named arguments. - [BC Break]
Builder::addResourceTemplate()signature changed —$titleparameter added between$nameand$description. Callers using positional arguments must switch to named arguments. - Add
CorsMiddleware,DnsRebindingProtectionMiddleware, andProtocolVersionMiddlewareforStreamableHttpTransport, composed automatically as the default stack viaStreamableHttpTransport::defaultMiddleware() - [BC BREAK]
StreamableHttpTransportconstructor:$corsHeadersparameter removed; CORS is now configured viaCorsMiddleware. The$middlewareparameter is nullable —null(or omitted) installs the default stack;[]disables all defaults. DefaultAccess-Control-Allow-Originis no longer set (was*). - [BC Break]
ResourceDefinition::__construct()signature changed —$titleparameter added between$nameand$description. Callers using positional arguments must switch to named arguments. - [BC Break]
ResourceTemplate::__construct()signature changed —$titleparameter added between$nameand$description. Callers using positional arguments must switch to named arguments. - [BC Break]
McpResourceandMcpResourceTemplateattribute signatures changed —$titleparameter added between$nameand$description. Callers using positional arguments must switch to named arguments.
0.5.0
- Add built-in authentication middleware for HTTP transport using OAuth
- Add client component for building MCP clients
- Add
Builder::setReferenceHandler()to allow customReferenceHandlerInterfaceimplementations (e.g. authorization decorators) - Add elicitation enum schema types per SEP-1330:
TitledEnumSchemaDefinition,MultiSelectEnumSchemaDefinition,TitledMultiSelectEnumSchemaDefinition - [BC break] Make Symfony Finder component optional. Users would need to install
symfony/findernow themselves - Add
LenientOidcDiscoveryMetadataPolicyfor identity providers that omitcode_challenge_methods_supported(e.g. FusionAuth, Microsoft Entra ID) - Add OAuth 2.0 Dynamic Client Registration middleware (RFC 7591)
- Add optional
titlefield toPromptandMcpPromptfor MCP spec compliance - [BC Break]
Builder::addPrompt()signature changed —$titleparameter added between$nameand$description. Callers using positional arguments for$descriptionmust switch to named arguments. - Add optional
titlefield toToolandMcpToolfor MCP spec compliance - [BC Break]
Tool::__construct()signature changed —$titleparameter added between$nameand$inputSchema. Callers using positional arguments must switch to named arguments or passnullfor$title. - [BC Break]
McpToolattribute signature changed —$titleparameter added between$nameand$description. Callers using positional arguments for$descriptionmust switch to named arguments. - [BC Break]
Builder::addTool()signature changed —$titleparameter added between$nameand$description. Callers using positional arguments for$descriptionmust switch to named arguments.
0.4.0
- Rename
Mcp\Server\Session\Psr16StoreSessiontoMcp\Server\Session\Psr16SessionStore - Add missing handlers for resource subscribe/unsubscribe and persist subscriptions via session
- Introduce
SessionManagerto encapsulate session handling (replacesSessionFactory) and move garbage collection logic fromProtocol.
0.3.0
- Add output schema support to MCP tools
- Add validation of the input parameters given to a Tool.
- Rename
Mcp\Capability\Registry\ResourceReference::$schematoMcp\Capability\Registry\ResourceReference::$resource. - Introduce
SchemaGeneratorInterfaceandDiscovererInterfaceto allow custom schema generation and discovery implementations. - Remove
DocBlockParser::getSummary()method, useDocBlockParser::getDescription()instead.
0.2.2
- Throw exception when trying to inject parameter with the unsupported names
$_sessionor$_request. Throwableobjects are passed to log context instead of the exception message.
0.2.1
- Add
RunnerControlforStdioTransportto allow break out from continuously listening for new input. - Open range of supported Symfony versions to include v5.4
0.2.0
- Make
Protocolstateless by decouple if fromTransportInterface. RemovedProtocol::getTransport(). - Change signature of
Builder::addLoaders(...$loaders)toBuilder::addLoaders(iterable $loaders). - Removed
ClientAwareInterfacein favor of injecting aRequestContextwith argument injection. - The
ClientGatewaycannot be injected with argument injection anymore. UseRequestContextinstead. - Removed
ClientAwareTrait - Removed
Protocol::getTransport() - Added parameter for
TransportInterfacetoProtocol::processInput()
0.1.0
- First tagged release of package
- Support for implementing MCP server