MCP TypeScript SDK (V2) / @modelcontextprotocol/client / client/responseCache
client/responseCache
Classes
ClientResponseCache
Defined in: packages/client/src/client/responseCache.ts:268
Internal
The Client's cache-coordination collaborator.
Owns the per-connection cache state that used to live as five private fields on Client — the backing ResponseCacheStore, the per-method eviction-generation counter, the user-supplied/default flag, and the stamp-memoized derived indices over the tools/list entry. Client holds exactly one instance and never reaches past it to the store.
Not exported from the package index — internal to the client package.
Constructors
Constructor
new ClientResponseCache(
_store,_isUserSupplied,_reportError?,_cachePartition?,_now?):ClientResponseCache
Defined in: packages/client/src/client/responseCache.ts:312
Parameters
_store
_isUserSupplied
boolean
Whether _store was supplied by the caller. A user-supplied store is never clear()ed by resetForReconnect (defeats the only reason to supply one).
_reportError?
(error) => void
Sink for a custom store's set()/evict() failure. write never lets a store rejection cost the caller a result it already fetched — the failure is reported here and the write resolves. The Client wires this to onerror.
_cachePartition?
string = ''
The opaque per-principal identifier for this client (the private-scope storage slot within the connected server's namespace). '' (the default) makes the private slot identical to the server's shared public slot — the safe single-tenant posture. See _partitionFor.
_now?
() => number
Clock seam (testing). The freshness check (entry.expiresAt > now()) and the expiresAt = now() + ttlMs stamp both read it via now. Default Date.now.
Returns
Methods
captureGeneration()
captureGeneration(
method,params?):number
Defined in: packages/client/src/client/responseCache.ts:474
Snapshot the eviction generation for {method, params} before issuing the request (a list walk's page 1, or a resources/read for params). Records the key so an interleaved evictKey for the same {method, params} knows there is an in-flight write to suppress and bumps; without the record, evictKey's recorded-only bump would skip and the stale body would be cached.
Parameters
method
string
params?
string
Returns
number
evict()
evict(
method):Promise<void>
Defined in: packages/client/src/client/responseCache.ts:412
Bump the per-method generation (so an in-flight write for the same method becomes a no-op) and drop the connected server's two list singletons (own + shared partition; params: ''). The generation bump is unconditional and FIRST — the write race guard relies on the bump, not on the store's deletes completing.
Eviction is scoped to this client's [serverIdentity, principal] partitions (mirroring evictKey) — the method-wide store.evict() is NOT called, so on a shared store one server's list_changed cannot wipe a co-tenant's entry. A custom store's delete() may throw or reject; each partition is guarded independently so a failure on one does not skip the other, the failure is reported via the constructor's sink, and the call resolves so dispatch proceeds.
Parameters
method
string
Returns
Promise<void>
evictKey()
evictKey(
method,params):Promise<void>
Defined in: packages/client/src/client/responseCache.ts:452
Drop the single logical entry {method, params} from BOTH the private and public partitions for this client's connected server (mcp.d's invalidateLogical). Used for notifications/resources/updated's per-URI eviction. The per-key generation is bumped FIRST (so an in-flight write for the same {method, params} becomes a no-op and cannot re-cache the now-stale body) but only when the key was already recorded by captureGeneration — bounding the map to keys the client has actually read. A custom store's delete() may throw or reject; each partition's delete is guarded independently so a failure on one does not skip the other, and the call resolves so dispatch proceeds.
Parameters
method
string
params
string
Returns
Promise<void>
now()
now():
number
Defined in: packages/client/src/client/responseCache.ts:344
The clock used for every freshness computation and check.
Returns
number
outputValidator()
outputValidator<
V>(name,compile):Promise<V|undefined>
Defined in: packages/client/src/client/responseCache.ts:646
The compiled output-schema validator for tool name, derived from the cached tools/list entry — same source and same stamp-keyed memoization as toolDefinition. The name → validator index re-derives only when the backing entry's stamp changes (a refetched tools/list recompiles; a list_changed eviction drops it). Returns undefined when no tools/list is held, the tool is absent, or it has no outputSchema.
compile is the caller-supplied validator-compile callback (the Client passes its _jsonSchemaValidator wrapper) so this class carries no validator-provider dependency. One tool's uncompilable outputSchema (e.g. an invalid pattern regex or unresolvable $ref) must not poison every other tool's callTool — the callback isolates that compile error per tool by returning a per-tool error variant which the index stores alongside the good ones, and callTool surfaces it as a typed InvalidParams only for that name. Because the error is held on this stamp-keyed substrate (not a parallel map), it inherits the substrate's invalidation lifecycle: a list_changed eviction drops it, a refetched tools/list re-derives it, and resetForReconnect clears the lot.
Type Parameters
V
V
Parameters
name
string
compile
(tool) => V | undefined
Returns
Promise<V | undefined>
read()
read(
method,params?):Promise<{value:unknown; } |undefined>
Defined in: packages/client/src/client/responseCache.ts:562
Serve the fresh cached result for {method, params}, or undefined. Lookup is the two-probe order (own-partition then this server's shared partition, gated on scope === 'public'); freshness is entry.expiresAt > now() (a missing expiresAt is never fresh), checked BEFORE decoding so stale entries cost no parse. Every hit is freshly parsed, so the caller owns the value outright. An entry whose document does not parse or is not an object (corrupted external store) is reported, deleted, and treated as a miss — deleted because a fresh-but-corrupt entry would otherwise re-parse and re-report on every read until its expiresAt passes.
Parameters
method
string
params?
string
Returns
Promise<{ value: unknown; } | undefined>
resetForReconnect()
resetForReconnect():
void
Defined in: packages/client/src/client/responseCache.ts:588
Connection reset. The per-instance default store IS cleared (connection-scoped); a user-supplied store is NOT — that would defeat the only reason to supply one. The generation map and every derived index are dropped regardless: they are connection-scoped even when the backing store survives, so the next read re-derives from whatever the store still holds. The server identity returns to the pre-connect sentinel. The default impl is synchronous, so the MaybePromise<void> return is a plain void here and the caller need not await.
Returns
void
setServerIdentity()
setServerIdentity(
identity):void
Defined in: packages/client/src/client/responseCache.ts:355
Record the connected server's identity. Called by Client immediately after a successful connect (serverInfo.name@version, or the transport's sessionId when serverInfo is unavailable). Every partition derived after this call is scoped to this identity; entries written under the pre-connect '' sentinel are no longer reachable.
Parameters
identity
string
Returns
void
toolDefinition()
toolDefinition(
name):Promise<{_meta?: {[key:string]:unknown; };annotations?: {destructiveHint?:boolean;idempotentHint?:boolean;openWorldHint?:boolean;readOnlyHint?:boolean;title?:string; };description?:string;execution?: {taskSupport?:"optional"|"required"|"forbidden"; };icons?:object[];inputSchema: {[key:string]:unknown;properties?: {[key:string]:string|number|boolean| {[key:string]: string | number | boolean | { [x: string]: string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | ... 4 more ... | null)[] | null)[] | null)[] | null)[] | null; } | (string | ... 4 more ... | null)[] | null; } | string | number | boolean | { [x: string]: string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | ... 4 more ... | null)[] | null)[] | null)[] | null)[] | null; } | (string | ... 4 more ... | null)[] | null[] |null; };required?:string[];type:"object"; };name:string;outputSchema?: {[key:string]:unknown;$schema?:string; };title?:string; } |undefined>
Defined in: packages/client/src/client/responseCache.ts:606
The descriptor for tool name taken from the cached tools/list entry. The name → Tool index is memoized against the entry's stamp and re-derived only when the backing entry changes (mcp.d's cachedTool). Returns undefined only when no tools/list response is held at all, or the held list does not contain name.
Consumed by callTool()'s SEP-2243 _resolveXMcpHeaderScan (mirroring) and, via outputValidator, its output-schema validation.
Parameters
name
string
Returns
Promise<{ _meta?: {[key: string]: unknown; }; annotations?: { destructiveHint?: boolean; idempotentHint?: boolean; openWorldHint?: boolean; readOnlyHint?: boolean; title?: string; }; description?: string; execution?: { taskSupport?: "optional" | "required" | "forbidden"; }; icons?: object[]; inputSchema: {[key: string]: unknown; properties?: {[key: string]: string | number | boolean | {[key: string]: string | number | boolean | { [x: string]: string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | ... 4 more ... | null)[] | null)[] | null)[] | null)[] | null; } | (string | ... 4 more ... | null)[] | null; } | string | number | boolean | { [x: string]: string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | number | boolean | ... | (string | ... 4 more ... | null)[] | null)[] | null)[] | null)[] | null; } | (string | ... 4 more ... | null)[] | null[] | null; }; required?: string[]; type: "object"; }; name: string; outputSchema?: {[key: string]: unknown; $schema?: string; }; title?: string; } | undefined>
write()
write(
method,value,capturedGen,freshness?):Promise<void>
Defined in: packages/client/src/client/responseCache.ts:517
Write value under {method} unless the per-method generation moved since capturedGen was taken — a list_changed that landed mid-walk has already invalidated the result the caller is about to write, and overwriting the eviction with the stale aggregate would lose the invalidation.
The value is stored as its JSON-serialized document; serialization doubles as the mutation barrier, so a caller mutating the returned aggregate cannot reach the cache or its derived indices. A value that is not JSON-serializable (reachable only via in-process transports) fails the write loudly into the reportError sink. A custom store whose set() throws or rejects is routed to the same sink and the write resolves — cache bookkeeping never costs the caller a result it already fetched.
freshness carries the client-computed expiresAt (absolute ms epoch, now + ttlMs) and the server-reported cacheScope. The storage partition is derived from the scope via _partitionFor: 'public' → [serverIdentity, ''] (shared within this server); 'private' → [serverIdentity, cachePartition] (so a shared store never serves a private entry to another identity). Absent freshness preserves the substrate write (no expiresAt, private partition) — the tools/list retain-for-schema posture: never served by read's freshness gate, always readable by toolDefinition.
After storing under the derived partition, the same {method, params} is deleted from the OPPOSITE partition (mirroring evictKey's two-partition posture). A server that flips a result's cacheScope for the same key would otherwise leave the previous entry in the other slot — and since _probe checks own-partition first, a stale private entry would shadow the fresh public one (or a stale public entry would keep serving co-tenants). Both store calls are independently guarded so a custom store's failure on one does not skip the other.
Parameters
method
string
value
unknown
capturedGen
number
freshness?
expiresAt
number
params?
string
scope
Returns
Promise<void>
InMemoryResponseCacheStore
Defined in: packages/client/src/client/responseCache.ts:160
In-memory default. Bounded by an insertion-ordered size cap (default 512; see InMemoryResponseCacheStoreOptions.maxEntries) on the resources/read keyspace so an unbounded stream of distinct URIs cannot grow it without limit; the list-singleton methods are exempt and never evicted by the cap. Map preserves insertion order, so the oldest live capped key is the first matching iteration entry.
Implements
Constructors
Constructor
new InMemoryResponseCacheStore(
options?):InMemoryResponseCacheStore
Defined in: packages/client/src/client/responseCache.ts:167
Parameters
options?
InMemoryResponseCacheStoreOptions
Returns
Accessors
size
Get Signature
get size():
number
Defined in: packages/client/src/client/responseCache.ts:172
Number of held entries (for diagnostics / bounding tests).
Returns
number
Methods
clear()
clear():
void
Defined in: packages/client/src/client/responseCache.ts:218
Drop every entry (connection reset).
Returns
void
Implementation of
delete()
delete(
key):void
Defined in: packages/client/src/client/responseCache.ts:203
Drop the single entry under key (no-op if absent). Called for both notifications/resources/updated (per-URI) and the list_changed notifications (the list singletons live at {method, params: '', partition}).
Parameters
key
Returns
void
Implementation of
evict()
evict(
method):void
Defined in: packages/client/src/client/responseCache.ts:207
Drop every entry for method across every partition. The Client does NOT call this (its list_changed path issues two partition-scoped delete() calls so co-tenants on a shared store keep their entries); kept on the interface for callers that want a method-wide bulk-clear.
Parameters
method
string
Returns
void
Implementation of
get()
get(
key):CacheEntry|undefined
Defined in: packages/client/src/client/responseCache.ts:176
Parameters
key
Returns
CacheEntry | undefined
Implementation of
set()
set(
key,entry):number
Defined in: packages/client/src/client/responseCache.ts:180
Writes entry under key and returns the store-generated stamp the resulting CacheEntry carries. The store owns the stamp counter; callers do not supply one. The caller owns expiresAt and scope (the client-computed freshness metadata); the store MUST persist them and hand them back on get.
Parameters
key
entry
expiresAt?
number
scope?
value
string
Returns
number
Implementation of
Interfaces
CacheEntry
Defined in: packages/client/src/client/responseCache.ts:65
One cached response body. value is the JSON-serialized result document — a store is a dumb string carrier and persists it verbatim; the cache owns both codec halves. stamp is the store-generated monotonically increasing write counter — opaque to callers. Derived views (e.g. a name → Tool index) memoize against it and re-derive only when it changes. expiresAt (absolute ms epoch, now + ttlMs) and scope are the client-computed freshness metadata; the store MUST persist them and hand them back on get so the read path can decide freshness and gate the shared-partition fallback on scope === 'public'.
Properties
expiresAt?
readonlyoptionalexpiresAt?:number
Defined in: packages/client/src/client/responseCache.ts:68
scope?
readonlyoptionalscope?:CacheScope
Defined in: packages/client/src/client/responseCache.ts:69
stamp
readonlystamp:number
Defined in: packages/client/src/client/responseCache.ts:67
value
readonlyvalue:string
Defined in: packages/client/src/client/responseCache.ts:66
CacheKey
Defined in: packages/client/src/client/responseCache.ts:48
A logical cache address. params is the canonical result-affecting params key ('' for the four list ops, the uri for resources/read); omitted is equivalent to ''. partition namespaces the entry by connected-server identity AND per-principal scope: the Client writes a JSON-encoded [serverIdentity, principal] pair (so a server-controlled serverInfo string cannot bleed into the principal slot regardless of what characters it contains). A 'public'-scoped entry lives at [serverIdentity, '']; a 'private'-scoped entry at [serverIdentity, cachePartition]. Omitted is equivalent to ''.
Properties
method
readonlymethod:string
Defined in: packages/client/src/client/responseCache.ts:49
params?
readonlyoptionalparams?:string
Defined in: packages/client/src/client/responseCache.ts:50
partition?
readonlyoptionalpartition?:string
Defined in: packages/client/src/client/responseCache.ts:51
InMemoryResponseCacheStoreOptions
Defined in: packages/client/src/client/responseCache.ts:120
Options for InMemoryResponseCacheStore.
Properties
maxEntries?
optionalmaxEntries?:number
Defined in: packages/client/src/client/responseCache.ts:133
Maximum number of held resources/read entries (the only unbounded-keyspace method). When inserting a new resources/read key would exceed this, the oldest such entry (by insertion order) is evicted first. The list-singleton methods (tools/list, prompts/list, resources/list, resources/templates/list, server/discover) are exempt — they hold at most one entry per partition and back the tools/list-derived index, so an unbounded URI working set never displaces them. The default of 512 bounds growth on a long-lived client against template-expanded URIs. 0 disables the bound.
ResponseCacheStore
Defined in: packages/client/src/client/responseCache.ts:92
The pluggable response-cache store. The interface is intentionally narrow; the in-memory default is the only implementation the SDK ships.
Every method is async-ready (MaybePromise) so a Redis-style store can implement the same interface without a later breaking change; the in-memory default stays synchronous (plain values are valid under MaybePromise). The Client awaits every call site.
Entries are keyed by {method, params, partition} where partition is the Client-derived [serverIdentity, principal] JSON pair, so one store instance is safe to share across Client instances connected to different servers and/or principals: writes from distinct connections never collide, the shared-partition read fallback is gated on the stored scope === 'public', and list_changed / HEADER_MISMATCH evictions are scoped to the connected server's two partitions — co-tenants on a shared store are unaffected. The Client constructor still allocates a fresh InMemoryResponseCacheStore per instance by default; supply your own to share or persist.
Methods
clear()
clear():
MaybePromise<void>
Defined in: packages/client/src/client/responseCache.ts:116
Drop every entry (connection reset).
Returns
MaybePromise<void>
delete()
delete(
key):MaybePromise<void>
Defined in: packages/client/src/client/responseCache.ts:107
Drop the single entry under key (no-op if absent). Called for both notifications/resources/updated (per-URI) and the list_changed notifications (the list singletons live at {method, params: '', partition}).
Parameters
key
Returns
MaybePromise<void>
evict()
evict(
method):MaybePromise<void>
Defined in: packages/client/src/client/responseCache.ts:114
Drop every entry for method across every partition. The Client does NOT call this (its list_changed path issues two partition-scoped delete() calls so co-tenants on a shared store keep their entries); kept on the interface for callers that want a method-wide bulk-clear.
Parameters
method
string
Returns
MaybePromise<void>
get()
get(
key):MaybePromise<CacheEntry|undefined>
Defined in: packages/client/src/client/responseCache.ts:93
Parameters
key
Returns
MaybePromise<CacheEntry | undefined>
set()
set(
key,entry):MaybePromise<number>
Defined in: packages/client/src/client/responseCache.ts:101
Writes entry under key and returns the store-generated stamp the resulting CacheEntry carries. The store owns the stamp counter; callers do not supply one. The caller owns expiresAt and scope (the client-computed freshness metadata); the store MUST persist them and hand them back on get.
Parameters
key
entry
expiresAt?
number
scope?
value
string
Returns
MaybePromise<number>
Type Aliases
CacheMode
CacheMode =
"use"|"refresh"|"bypass"
Defined in: packages/client/src/client/responseCache.ts:35
Per-call cache disposition for the cacheable verbs (listTools() / listPrompts() / listResources() / listResourceTemplates() / readResource()):
'use'(the default) — serve a still-fresh cached entry without a round trip; on miss/stale, fetch and write.'refresh'— always fetch (ignore any held entry) and write the fresh result.'bypass'— fetch without consulting OR writing the cache (the result is not stored). Thetools/list-derived index (mirroring / output validation) is therefore unaffected by a'bypass'call.
CacheScope
CacheScope =
"public"|"private"
Defined in: packages/client/src/client/responseCache.ts:20
The freshness scope of a cached entry (SEP-2549 cacheHints.scope).
MaybePromise
MaybePromise<
T> =T|Promise<T>
Defined in: packages/client/src/client/responseCache.ts:17
A value or a promise of one. The store interface is async-ready; the in-memory default returns plain values.
Type Parameters
T
T
Variables
MAX_CACHE_TTL_MS
constMAX_CACHE_TTL_MS:86400000=86_400_000
Defined in: packages/client/src/client/responseCache.ts:253
Upper bound on the server-supplied ttlMs honoured by ClientResponseCache (24h). A server cannot pin an entry indefinitely.