* Address review feedback on resource_link Three follow-ups from review of the resource_link work: * PromptResultFormatter: add a regression test proving the optional fields of a typed resource_link block (title, description, mimeType, size, annotations, _meta) survive formatting. The delegation to ResourceLink::fromArray() already landed while rebasing onto the PromptResultFormatter refactor, but the existing test only supplied uri/name and so could not catch a reintroduced field drop. * ResourceLink::fromArray(): validate optional fields consistently, the way the equivalent ResourceDefinition::fromArray() already does. description, mimeType and size now raise InvalidArgumentException instead of surfacing a TypeError (or silently coercing, for size's (int) cast); annotations goes through Annotations::tryFromArray(), which carries the is_array() guard that icons already had; icons uses Icon::listFromArray() so a non-array entry is reported in context. * ToolReference::extractStructuredContent(): never emit a list as structuredContent. The Content guard added earlier fixed only one instance of the real invariant - structuredContent must be a JSON object, and a PHP list can never be one. Tool::fromArray() already enforces the matching rule by rejecting an outputSchema whose type is not "object", so this aligns the runtime path with it. The test asserting the opposite for an array-typed outputSchema contradicted the phpstan type and the fromArray() check introduced alongside it in the same commit, so it is replaced by tests for the two list shapes. * Apply the structuredContent object rule to object results too `extractStructuredContent()` guards raw array results against being emitted as a JSON array, but the object branch handed back whatever `json_decode()` produced. A `JsonSerializable` returning a list or a scalar slipped straight through, producing exactly the `structuredContent` the array guard exists to prevent — and a return value that contradicts the method's own `array<string, mixed>|null` signature. Check the decoded value before returning it, and cover the object branch, which had no tests at all. * Document structured tool output `outputSchema` and `structuredContent` were undocumented: the tool return value docs covered only the `content` side, and the schema generation section is about tool parameters. Add a "Structured Output" subsection covering how to declare the schema, which return values populate `structuredContent`, and why a list has to be wrapped in a key to get structured output at all. * Gate structuredContent on the negotiated protocol revision SEP-2106 (revision 2026-07-28) widens structuredContent to any JSON value; earlier revisions require an object. Resolve the revision per request and apply the matching rule, and warn when a declared outputSchema yields none. * Resolve the protocol revision on RequestContext Keeps the `_meta`-then-session lookup in one place instead of per handler, and exposes the negotiated revision to tool handlers. * Document reading the negotiated revision from RequestContext * Warn when a self-built CallToolResult carries an invalid structuredContent Returning a CallToolResult opts out of the extraction rules, so the value is still sent unchanged — but a JSON array is not valid before SEP-2106.
14 KiB
14 KiB
Changelog
All notable changes to mcp/sdk will be documented in this file.
0.8.0
- Always emit
{}for empty tool schemas:Toolrecursively normalizes every empty sub-schema —properties,items,additionalProperties,$defs, combinators and the other draft-07 to 2020-12 schema keywords — in the constructor, for bothinputSchemaandoutputSchema, so an object position is never serialized as[]. - Prompt generators returning content as typed arrays (
['type' => 'text', ...]etc.) no longer lose the optional fields:annotationson every content type, and_metaand an explicitmimeTypeon embedded resource contents, now carry through to the resultingPromptMessageinstead of being silently dropped. A missing resourcemimeTypestill defaults totext/plain/application/octet-streamas before. - Add
annotationssupport toImageContent(constructor,fromArray(),fromFile(),fromString(),jsonSerialize()), matchingTextContentandAudioContent. - Add client-side
roots/listhandler (ListRootsRequestHandler+RootsCallbackInterface) andClient::sendRootsListChanged(), plus server-sideClientGateway::listRoots()/supportsRoots()andListRootsResult::fromArray(). - Add
ClientGateway::supportsSampling(), so a tool can check the client's advertised capabilities before issuing asampling/createMessagerequest instead of asking and catching the refusal. Matches the existingsupportsRoots()andsupportsElicitation(). - [BC Break] Gate
structuredContenton the negotiated protocol revision:ToolReference::extractStructuredContent()takes an optionalProtocolVersionand, for revisions predating SEP-2106 (2025-11-25and earlier, wherestructuredContentmust be a JSON object), returnsnullfor a tool result that is a PHP list or an object serializing to a JSON array. From2026-07-28on both are emitted as-is. Objects serializing to a scalar and arrays holdingContentinstances are never emitted, in any revision.CallToolHandlerresolves the revision from the request's_meta(modern era) or the session (handshake era) and falls back to the strictest rule; it logs a warning when a tool declares anoutputSchemabut returns a value that cannot be sent, and when a self-builtCallToolResultcarries astructuredContentthe revision does not allow (that one is passed through unchanged). Tools returning a list against an older client keep their JSON-encoded value incontent; they just no longer advertise an invalidstructuredContent. - Add
Mcp\Schema\Content\ResourceLinkfor the spec'sresource_linkcontent block (protocol revision 2025-06-18+), letting tool results and prompt messages reference a resource by URI/name without embedding its contents. Accepted anywhereresource(EmbeddedResource) content is (de)serialized:CallToolResult::fromArray(),PromptMessage::fromArray(), andPromptResultFormatter. - Negotiate the protocol revision during the
initializehandshake: the server echoes a revision it supports and counter-offersProtocolVersion::latestHandshake()otherwise (Builder::setProtocolVersion()pins it to exactly one), and the client fails the handshake on a counter-offer it cannot speak rather than continuing on an unagreed revision. AddsClient::getProtocolVersion(), the2026-07-28revision, and the era helpers onProtocolVersion— revisions from2026-07-28on have noinitialize, so they are excluded from negotiation and fromProtocolVersionMiddleware's default supported set. - Add sampling with tools support: sampling requests now accept tools and tool-choice preferences, messages support tool-use/tool-result content blocks and multiple content blocks, and clients can advertise the
sampling.contextandsampling.toolscapabilities.
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