MCP cacheScope: Stop Private Results Leaking Across Users
MCP cacheScope addresses a subtle problem: a response can be fresh and still be unsafe for another user. The stable 2026-07-28 MCP specification defines caching hints for reusable results. A server can mark a result public or private , while ttlMs says how long it may remain fresh. But a shared client cache still needs enough identity information to keep private entries apart. If Alice warms a…
MCP cacheScope tackles the issue of private results leaking to unauthorized users across sessions. The 2026-07-28 MCP specification allows servers to label results as public or private via cache hints, while TTL specifies result freshness. However, shared client caches need additional identity information to keep private entries separate.
If a user (Alice) populates a cache with results and another user (Bob) later accesses the same cache, incomplete cache keys may serve Alice's private results to Bob. This security boundary is worth testing.
The MCP caching specification covers various aspects like discovery, tool lists, resource lists, and resource reads. Public results can be reused across different authorization contexts, even if authentication is required. Private results, however, can only be reused within the same authorization context. For user-specific tool catalogs, servers can specify the result like so: `const server = new McpServer({...}, { cacheHints: { tools/list: { ttlMs: 60000, cacheScope: 'private', }, }, }, );` This cache hint communicates the server's caching intentions without revealing which user made the request. The client requires a separate partition to maintain privacy.
To demonstrate the leak, a shared store is required with two authorization contexts, one response cache, and no partition. Two in-process endpoints with the same MCP server identity can be used, one exposing an Alice-only tool and the other a Bob-only tool. Alice calls `tools/list` first, storing her private result in the shared cache.
Bob then requests the same method with identical parameters and server identity. Without cache partitioning, Bob's lookup cannot distinguish between authorization contexts, resulting in Bob receiving Alice's cached tool list and his endpoint handling zero `tools/list` requests.
The TypeScript SDK v2 caching guide warns that this configuration can expose one user's private response body to another. The demonstration succeeds when it reproduces the unsafe result, making the failure mode apparent without credentials, a network service, a model, or a paid API call. To resolve this, each authorization context should have a stable cache partition.
This ensures that Alice's private entries are isolated from Bob's. The same method, parameters, and server identity no longer resolve to the same private cache location, preventing unauthorized access. The partition should be based on stable, opaque authorization identities, including dimensions like tenant, subject, role, or effective scope.
A raw bearer token is insufficient as a partition because it is secret and can rotate while the underlying principal remains the same.
The SDK handles public entries differently, as they remain shareable across principal partitions due to explicit server declarations of safety for cross-context reuse. This maintains performance benefits without compromising private isolation. The review checklist includes marking authorization-dependent results as private, using cache partition whenever multiple principals share a store, and testing two principals against the same method, parameters, and server identity.
An example TypeScript regression sample demonstrating the unsafe configuration and the partitioned fix is provided.
However, cacheScope does not grant access; it only controls cache reuse. The server must still authenticate the caller and authorize every uncached request. TTLs are freshness hints, not immediate revocation mechanisms. If permissions change, waiting for a private entry to expire may be too slow. Compliant clients must invalidate affected entries upon receiving MCP notifications, and applications need policies for handling authorization changes outside this flow.
Other protocol boundaries, like multi-round-trip retries carrying inputResponses or requestState and input_required results, are also relevant and must be considered.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.