Skip to content

v0.16.0

Latest

Choose a tag to compare

@paulbalandan paulbalandan released this 24 Aug 17:46
· 6 commits to 1.x since this release
v0.16.0
6345d4a

What's Changed

The pre-v1 hardening release. Every default now bounds what a peer or a slow reader can cost (request body, SSE backlog, task fibers and records, subscription watch lists), the OAuth client seals discovery SSRF and binds the bearer token to the resource's path, and a schema failure surfaces its structured violations. Handler registration takes the envelope class, and a verified access token always carries its expiry (see BREAKING_CHANGES.md). New: an encrypted file token store and a worked PHP-FPM mount.

Added

  • EncryptedFileTokenStore persists tokens to one XChaCha20-Poly1305 encrypted, owner-only file (needs ext-sodium).
  • AuthorizedHttpClient takes a lock semaphore, so workers sharing a token store can serialise renewals across processes.
  • ClientBuilder::setMetaExtrasFactory() adds per-request _meta keys to every outbound request, such as the W3C traceparent.
  • ClientRegistration carries clientSecretExpiresAt, and a stored registration whose secret has expired is registered again instead of presented.

Changed

  • addRequestHandler() and addNotificationHandler() take the envelope class and the handler, reading the method from the class, and extensions declare their classes as a list. See BREAKING_CHANGES.md.
  • ParameterHeaderValidationMiddleware leaves its decoded envelope on the request under StreamableHttpServerTransport::ENVELOPE_ATTRIBUTE, so the transport parses a body once.
  • VerifiedAccessToken requires expiresAt, now its second constructor argument, so no validator can hand the middleware a token it never checks for expiry. See BREAKING_CHANGES.md.
  • TaskClient takes minPollIntervalMs (default 100) and raises a shorter server-suggested pollIntervalMs to it.
  • SubscriptionStore refuses a listen naming more than maxResourceSubscriptionsPerStream resource URIs (default 256), and delivers a resource update by index rather than by scanning every stream.
  • SecuredHttpEndpoint caps the request body at 1 MiB by default. Pass maxBodyBytes: null to remove the cap.
  • The SDK's own validation failures are plain \InvalidArgumentExceptions. Only Assert raises ExpectationFailedException, its subclass, so a catch (\InvalidArgumentException) still sees both.
  • A resources/read URI is refused past 8192 bytes at decode, bounding the data.uri echo.
  • JwksAccessTokenValidator takes the resource it protects and refuses a token whose aud does not name it. See BREAKING_CHANGES.md.
  • SchemaValidatorInterface::validate() returns SchemaViolation objects, and a tools/call argument failure lists them with their JSON pointers under data.validation_errors. See BREAKING_CHANGES.md.

Fixed

  • The streamable HTTP server reads Accept as RFC 9110 media ranges, so */* and application/* are admitted and a q=0 range is not.
  • InMemoryTaskStore holds at most maxRecords (default 10 000), and below that ceiling createTask() reclaims in amortised constant time instead of sweeping every record.
  • Task fibers are capped by TasksServerExtension's maxRunningTasks (default 1024), refusing a further task with -32603 instead of running unbounded.
  • An SSE stream whose reader falls behind is abandoned at maxBufferedBytes (default 1 MiB) instead of buffering without limit.
  • OAuth metadata discovery no longer follows redirects, so a hostile origin cannot point a well-known probe at an internal host.
  • A WWW-Authenticate challenge is emitted with the control octets stripped, so a resource metadata URL or scope carrying CR LF cannot inject a header.
  • The Mcp-Param-{Name} check no longer skips a float or a large integer, and refuses a header whose body argument is absent. The client mirrors an integral float as its integer. See BREAKING_CHANGES.md.
  • The bearer token is bound to the resource's path, not its whole origin: another path on the same host is requested without the credential, and a redirect off the resource is refused. See BREAKING_CHANGES.md.
  • ToolAnnotations accepts destructiveHint and idempotentHint beside readOnlyHint: true, so one tool no longer makes a whole tools/list undecodable.
  • An envelope naming a method alongside a result or an error is refused as an invalid request echoing its id, instead of being dropped unanswered.
  • A client whose peer answers a pending request with such an envelope now settles the awaiting call instead of leaving it to time out.

Full Changelog: v0.15.0...v0.16.0