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
EncryptedFileTokenStorepersists tokens to one XChaCha20-Poly1305 encrypted, owner-only file (needsext-sodium).AuthorizedHttpClienttakes alocksemaphore, so workers sharing a token store can serialise renewals across processes.ClientBuilder::setMetaExtrasFactory()adds per-request_metakeys to every outbound request, such as the W3Ctraceparent.ClientRegistrationcarriesclientSecretExpiresAt, and a stored registration whose secret has expired is registered again instead of presented.
Changed
addRequestHandler()andaddNotificationHandler()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.ParameterHeaderValidationMiddlewareleaves its decoded envelope on the request underStreamableHttpServerTransport::ENVELOPE_ATTRIBUTE, so the transport parses a body once.VerifiedAccessTokenrequiresexpiresAt, now its second constructor argument, so no validator can hand the middleware a token it never checks for expiry. See BREAKING_CHANGES.md.TaskClienttakesminPollIntervalMs(default 100) and raises a shorter server-suggestedpollIntervalMsto it.SubscriptionStorerefuses a listen naming more thanmaxResourceSubscriptionsPerStreamresource URIs (default 256), and delivers a resource update by index rather than by scanning every stream.SecuredHttpEndpointcaps the request body at 1 MiB by default. PassmaxBodyBytes: nullto remove the cap.- The SDK's own validation failures are plain
\InvalidArgumentExceptions. OnlyAssertraisesExpectationFailedException, its subclass, so acatch (\InvalidArgumentException)still sees both. - A
resources/readURI is refused past 8192 bytes at decode, bounding thedata.uriecho. JwksAccessTokenValidatortakes the resource it protects and refuses a token whoseauddoes not name it. See BREAKING_CHANGES.md.SchemaValidatorInterface::validate()returnsSchemaViolationobjects, and atools/callargument failure lists them with their JSON pointers underdata.validation_errors. See BREAKING_CHANGES.md.
Fixed
- The streamable HTTP server reads
Acceptas RFC 9110 media ranges, so*/*andapplication/*are admitted and aq=0range is not. InMemoryTaskStoreholds at mostmaxRecords(default 10 000), and below that ceilingcreateTask()reclaims in amortised constant time instead of sweeping every record.- Task fibers are capped by
TasksServerExtension'smaxRunningTasks(default 1024), refusing a further task with-32603instead 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-Authenticatechallenge 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.
ToolAnnotationsacceptsdestructiveHintandidempotentHintbesidereadOnlyHint: true, so one tool no longer makes a wholetools/listundecodable.- An envelope naming a
methodalongside aresultor anerroris 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