typescript-sdk/.changeset/client-response-cache-substrate.md
Felix Weinberger 1823aae891
Implement MCP 2026-07-28 (#2286)
Co-authored-by: Matt <77928207+mattzcarey@users.noreply.github.com>
Co-authored-by: Konstantin Konstantinov <KKonstantinov@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-26 14:01:10 +01:00

2.3 KiB

@modelcontextprotocol/client
major

Client.listTools() / listPrompts() / listResources() / listResourceTemplates() now auto-aggregate every page when called without a cursor and return the complete result with nextCursor: undefined (matching the C#, Java, and mcp.d SDKs). Pass an explicit { cursor } string to fetch a single page; the per-page path is unchanged. Existing manual pagination loops keep working — the first iteration returns everything and the loop exits — but can be deleted. The aggregated result is written to the new pluggable ResponseCacheStore (default: a fresh per-instance InMemoryResponseCacheStore); a ClientResponseCache collaborator owns the eviction-generation guard and the derived tools/list index that callTool's output validation and SEP-2243 Mcp-Param-* mirroring read. New exports: ResponseCacheStore, CacheKey, CacheEntry, CacheScope, MaybePromise, InMemoryResponseCacheStore; new ClientOptions.responseCacheStore / ClientOptions.listMaxPages (caps the auto-aggregate walk at 64 pages by default; throws SdkError with SdkErrorCode.ListPaginationExceeded on overrun so a partial aggregate is never cached). The store interface is async-ready (MaybePromise<…>); the in-memory default stays synchronous. Entries are automatically scoped by the connected server's identity and (when set) the consumer-supplied cachePartition, so a shared store does not collide across servers or principals; evictions are likewise scoped to the connected server's partitions.

Behavior change (every era): output-schema validator compilation is now lazy — validators are compiled on the first callTool() against the cached tools/list entry, not eagerly inside listTools(). listTools() no longer throws on an uncompilable outputSchema (every tool stays listed; the compile failure is captured per-tool); calling callTool() on the affected tool throws ProtocolError(InvalidParams, "Tool 'X' has an invalid outputSchema: …") before the request is sent — output-schema validation is never silently skipped. A pluggable jsonSchemaValidator provider therefore observes compilation at callTool time, not listTools time. The legacy-era listTools() path is unchanged at the wire level but is observably different at the validator-lifecycle level.