v0.13.0
·
153 commits
to 1.x
since this release
Immutable
release. Only release title and notes can be modified.
What's Changed
Peer-visible diagnostics now speak one documented grammar, from schema validation through argument binding, and datetime fields are held to RFC 3339. The exception surface shrinks with it: twenty-eight message-only classes collapse into two shared ones, the one breaking change (see BREAKING_CHANGES.md). Registration refuses magic-method handlers and duplicate discovered entries, and extension notifications gain the client-side capability gate.
Changed
- Twenty-eight message-only exception classes are replaced by
Nexus\Mcp\Core\Exception\LogicException(SDK misuse) andNexus\Mcp\Core\Exception\RuntimeException(flow diagnostics), with messages unchanged. See BREAKING_CHANGES.md for the list.
Fixed
- An extension-owned notification from a server that did not advertise the extension is dropped with a warning, matching the request-side gate.
- A tool call refused with a header mismatch keeps that error when the binding refresh also fails, chaining the refresh failure as
previous. readAppResource()refuses aui://read that returned zero contents, instead of handing the host an empty result where it expected a document.- A discovery attribute on a magic method throws
LogicExceptionat registration, where#[AsTool]on__constructpreviously registered a tool that re-ran the constructor on the live handler. - A malformed error response whose recovered id matches a pending client request now fails that request with the parse diagnostic, instead of leaving the caller to wait out its deadline.
- Datetime fields are validated against the RFC 3339 grammar before parsing, where timezone names, colon-less and hour-only offsets, a space before the offset, and single-digit date or time fields previously parsed.
- A discovered handler's binding failure names the argument instead of the parameter's PHP class name, and the owning tool, prompt, or resource wraps it with its identity, matching the schema-validation stage's messages.
- A discovered handler parameter typed
objector\stdClassnow receives the decoded arguments as an object, instead of failing the call with aTypeError. - Schema-validation diagnostics for tool arguments and
structuredContentfollow the documented message conventions, and report up to eight violations instead of stopping at the first. ServerBuilder::register()refuses a discovered entry whose key an earlier source already declared, throwingLogicExceptionnaming both sources, instead of silently overwriting.- A resource template variable name longer than 32 characters is refused at registration, instead of compiling to a pattern PCRE rejects so the template silently never matches.
- A tool declaring an
outputSchemawhose non-error result carries nostructuredContentnow fails the call like a non-conforming result, instead of passing unvalidated. - Resource-template matching prefers the template with the most literal characters, so an exact
db://literalis reachable behind an earlierdb://{table}. Ties keep registration order. - A discovered
#[AsResourceTemplate]naming a template variableuriis refused at registration, instead of the variable silently shadowing the$uriparameter's request URI. - The stdio client's spawn log names only the subprocess binary and its argument count, keeping credentials passed in argv out of log records.
WWW-Authenticateparameter values are stripped of the control octets RFC 7230 forbids as they are parsed, so a hostile challenge cannot smuggle terminal escapes into logs and exception messages.CompleteRequestParamsnormalises acontextcarrying no resolved arguments to null, so the property agrees with the encoders that already omitted it.ClientCapabilitiesandServerCapabilitieskeep an empty array nested inside a vendor capability as[]when encoding. The capability slot itself still encodes as{}when empty.Icon's constructor applies the samesizeslist guard as its decoder, so an icon the SDK cannot re-read is refused at construction.- A
close()re-entered from a listener or a concurrent fiber during the drain no longer firesonDrainandonClosetwice on the HTTP transports andInMemoryTransport. - A malformed JSON line on the stdio transports, and a malformed or non-object body on the Streamable HTTP server, now reach the
onErrorlisteners. Before, only the stdio non-object arm did. - A JSON-RPC version-mismatch error names the offending method when the envelope carries one.
SupervisedTransport's explicitclose()firesonDrainbeforeonClose, and a close beforestart()fires both instead of neither, releasing a caller blocked on the close signal.- A second
start()on the stdio client, or one afterclose(), is refused before a subprocess is spawned, instead of spawning one only to kill it. - The SSE parser's frame budget restarts at every frame boundary, so a keep-alive-only stream is no longer torn down once the comments accumulate past the cap.
- The SSE parser no longer loses a chunk-final carriage return when the next chunk is empty, which split one multi-line frame into two.
- A readable stream whose
close()throws no longer costs the stdio transports their drain: the failure is logged and the drain proceeds. - A fault thrown by an
InMemoryTransportmessage listener stays on the receiving side'sonError, instead of surfacing through the peer'ssend(). ElicitResultaccepts an empty string inside astring[]content value, matching the spec's unconstrained item type, instead of failing the whole retry with-32602.
Full Changelog: v0.12.0...v0.13.0