Skip to content

v0.13.0

Choose a tag to compare

@paulbalandan paulbalandan released this 14 Aug 14:26
· 153 commits to 1.x since this release
Immutable release. Only release title and notes can be modified.
v0.13.0
20a3c6c

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) and Nexus\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 a ui:// 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 LogicException at registration, where #[AsTool] on __construct previously 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 object or \stdClass now receives the decoded arguments as an object, instead of failing the call with a TypeError.
  • Schema-validation diagnostics for tool arguments and structuredContent follow 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, throwing LogicException naming 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 outputSchema whose non-error result carries no structuredContent now 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://literal is reachable behind an earlier db://{table}. Ties keep registration order.
  • A discovered #[AsResourceTemplate] naming a template variable uri is refused at registration, instead of the variable silently shadowing the $uri parameter'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-Authenticate parameter 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.
  • CompleteRequestParams normalises a context carrying no resolved arguments to null, so the property agrees with the encoders that already omitted it.
  • ClientCapabilities and ServerCapabilities keep 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 same sizes list 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 fires onDrain and onClose twice on the HTTP transports and InMemoryTransport.
  • A malformed JSON line on the stdio transports, and a malformed or non-object body on the Streamable HTTP server, now reach the onError listeners. 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 explicit close() fires onDrain before onClose, and a close before start() fires both instead of neither, releasing a caller blocked on the close signal.
  • A second start() on the stdio client, or one after close(), 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 InMemoryTransport message listener stays on the receiving side's onError, instead of surfacing through the peer's send().
  • ElicitResult accepts an empty string inside a string[] 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