Build1 distinct publisher3 min readUpdated
The stable 2026-07-28 spec lets a server mark a result private. A dev.to walkthrough shows a shared client cache serving that private tool catalog to a second principal anyway.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
The stable 2026-07-28 MCP specification defines caching hints for reusable results, letting a server declare a `cacheScope` of `public` or `private` alongside a `ttlMs` freshness window [1]. A dev.to walkthrough demonstrates that the declaration alone does not hold: with one shared client response cache and no partition, Bob's `tools/list` call returns Alice's private catalog [11][13].
The hint vocabulary is narrow and clear. Public results may be reused across authorization contexts even when the endpoint requires authentication [6]; private results may be reused only within the same authorization context [7]. The caching rules apply to discovery, tool and prompt lists, resource lists, resource templates, and resource reads [5] - which is to say, exactly the surfaces that differ per tenant. A server can attach a hint for `tools/list` with `ttlMs: 60_000` and `cacheScope: "private"` [8], a 60 second staleness budget [1].
The gap is on the client side. A cache key needs the method and every parameter that can change the result, but authorization identity normally travels outside the `tools/list` parameters, so the client needs a separate partition for it [9][10]. The hint communicates the server's intent and nothing about which caller made the request [4]. The unsafe configuration is small: two authorization contexts, one response cache, no partition [11].
The author's reproduction uses two in-process endpoints sharing the same MCP server identity, one exposing an Alice-only tool and one a Bob-only tool [12]. Alice populates the shared store first. Bob then issues the same method with the same parameters against the same server identity, and receives Alice's cached tool list while his own endpoint handles zero `tools/list` requests [13]. That detail is the operational sting: the leak leaves no request on the serving side to log, alert on, or rate limit. The official TypeScript SDK v2 caching guide warns that this configuration can serve one user's private response body to another, according to the post [14]. The regression test is written to pass when it reproduces the unsafe result, and needs no credentials, network service, model, or paid API call [15].
The fix is a stable `cachePartition` per authorization context, such as `subject:alice` and `subject:bob`; with it, both endpoints receive one request each and each client sees only its own tool [16][17]. The author's guidance is to derive that partition from a stable, opaque authorization identity covering every dimension that can change visibility - tenant, subject, role, effective scope - and warns that a raw bearer token is a poor partition because it is secret material and can rotate while the principal stays the same [18][19]. Public entries stay shareable across principal partitions, since the server explicitly declared them safe [20].
Two limits are worth writing on the whiteboard. `cacheScope` controls reuse, not access: the server must still authenticate the caller and authorize every uncached request [22]. And a TTL is a freshness hint, not revocation, so waiting for a private entry to expire may be too slow after a permissions change [23]; the spec puts invalidation on the client, though the source text breaks off mid-sentence on the trigger [24].
Watch three things in your own stack. Whether any gateway or multi-tenant proxy you run sets a partition at all when one store serves many principals [21]. Whether your SDK version refuses to cache private entries in an unpartitioned store or silently accepts it, which is the behaviour the demonstration exercises [13]. And whether your invalidation path can beat the TTL you configured when an entitlement is withdrawn [1][23].
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
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.
The cacheHints declaration communicates the server's caching intent but does not tell a shared cache which caller made the request.
The MCP caching specification covers discovery, tool and prompt lists, resource lists, resource templates, and resource reads.
Public results may be reused across authorization contexts, even when the endpoint requires authentication.
Private results may be reused only within the same authorization context.
For a user-specific tool catalog, the example server sets cacheHints for 'tools/list' with ttlMs: 60_000 and cacheScope: 'private'.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
One reproducible walkthrough, no independent corroboration
The mechanism is unusually well specified for a single-source story: named API surface, pasteable code for both the unsafe and partitioned configurations, and a test whose outcome is stated in falsifiable terms (zero tools/list requests to Bob's endpoint unpartitioned; one request per endpoint when partitioned). It is nonetheless one dev.to post by the sample's own author, the spec text and SDK guide are only paraphrased, and the demonstration substitutes two in-process endpoints for a real authenticated server, so the authorization path itself is not exercised.
Primitives shipped; real-world uptake unmeasured
The only adoption signal is availability: the caching hints are described as part of the stable 2026-07-28 spec and the caching APIs are exercised against the official TypeScript SDK v2. The cluster contains no deployment counts, gateway or host disclosures, incident reports, or any evidence that production MCP clients share a response cache across principals, so uptake of the caching feature — and exposure to the leak — cannot be sized.
Mildly overstated inevitability, well-grounded mechanism
The headline framing ('will hand Alice's tool list to Bob') reads as an ambient property of MCP caching, whereas the source is explicit that the leak requires a specific misconfiguration — one shared response cache serving multiple principals with no cachePartition — and that a single-principal process with a private store gains little from partitioning. The article itself is disciplined: it flags that cacheScope is not authorization, that TTL is not revocation, and that the fix is available in-SDK. The gap is small and mostly presentational, not substantive.
Author-promoted sample on a developer platform; no vendor stake shown
The piece is a self-published dev.to tutorial that ends by pointing at the author's own runnable TypeScript regression sample and with an engagement prompt, giving a mild attention and credibility incentive to frame the failure mode starkly. Offsetting this, the author is not shown to represent the spec maintainers or a caching vendor, no product or paid service is sold, and the article volunteers limits (single-principal cases, cacheScope's non-authorization role, TTL's revocation gap) that cut against maximal alarm.
Mechanism credible, breadth unknown
Confidence in the mechanism is fairly high because it is code-level, internally consistent and echoed by the cited SDK guide. Confidence in the story's scale is low: one publisher, one author, no independent verification of spec or SDK details, and no adoption or incident data to indicate how many real clients are exposed. The ledger also contains a truncation observation that the supplied body contradicts, a small reminder that source handling here is imperfect.
build
A dropped date filter does not throw, it answers: the MCP bridge is a contract boundary1 distinct publisher
build
Thirty MCP tools become three: the reliability bug is in your schema, not the model1 distinct publisher
build
A build step instead of a backend: 1,025 records, 8 locales, no runtime API1 distinct publisher
build
Rate limit your MCP servers, because a retrying agent turns one error into a billing incident1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 14, 2026