Use case
We run a multi-tenant MCP gateway (Arcade) on v1.7.0-pre.2. Our tools/list responses are per-tenant (allow-lists, auth state), so the SDK's forced cacheable defaults — cacheScope: "public", ttlMs: 0 — are wrong for us twice over: public invites cross-tenant cache poisoning at intermediaries, and we want a real TTL (we use private/60s). We can override our own handler results, but not the SDK-generated ones (notably server/discover).
Current behavior (pre.2)
- Every SDK-generated list/discover result embeds
Cacheable{TTLMs, CacheScope} (protocol.go) and each internal handler calls unexported setDefaultCacheableValues() → CacheScope = "public", TTLMs = 0. Call sites include discover and the tools/prompts/resources/templates lists.
- No
ServerOptions/StreamableHTTPOptions field influences these defaults.
Why the workaround is fragile
Receiving middleware can type-assert each concrete result type after the fact and overwrite .Cacheable — that means enumerating every result type, keying off method strings, and depending on running after the SDK stamped its values. It works (we do it for our own tools/list), but there is no supported way to change what the SDK itself generates, and each new SDK result type silently reverts to public.
Proposal
type ServerOptions struct {
// DefaultCacheable, if non-nil, supplies the Cacheable values stamped on
// SDK-generated results (server/discover, *_list). Nil keeps today's
// public/0 defaults.
DefaultCacheable *Cacheable
}
One field, zero behavior change for existing users. For multi-tenant servers a public default is arguably unsafe, which is why a first-class knob (rather than post-hoc middleware) feels warranted.
🐕 Written by Kyoto, an AI agent, on Pascal's behalf —
Use case
We run a multi-tenant MCP gateway (Arcade) on
v1.7.0-pre.2. Ourtools/listresponses are per-tenant (allow-lists, auth state), so the SDK's forced cacheable defaults —cacheScope: "public",ttlMs: 0— are wrong for us twice over:publicinvites cross-tenant cache poisoning at intermediaries, and we want a real TTL (we useprivate/60s). We can override our own handler results, but not the SDK-generated ones (notablyserver/discover).Current behavior (pre.2)
Cacheable{TTLMs, CacheScope}(protocol.go) and each internal handler calls unexportedsetDefaultCacheableValues()→CacheScope = "public",TTLMs = 0. Call sites include discover and the tools/prompts/resources/templates lists.ServerOptions/StreamableHTTPOptionsfield influences these defaults.Why the workaround is fragile
Receiving middleware can type-assert each concrete result type after the fact and overwrite
.Cacheable— that means enumerating every result type, keying off method strings, and depending on running after the SDK stamped its values. It works (we do it for our owntools/list), but there is no supported way to change what the SDK itself generates, and each new SDK result type silently reverts topublic.Proposal
One field, zero behavior change for existing users. For multi-tenant servers a
publicdefault is arguably unsafe, which is why a first-class knob (rather than post-hoc middleware) feels warranted.🐕 Written by Kyoto, an AI agent, on Pascal's behalf —