MCP · tools/list · caching · 13 September 2026
MCP tools disappeared: fix ttlMs, cacheScope and stale tools/list caches
If an MCP host connects successfully but exposes zero tools, or newly deployed tools remain invisible until the application restarts, debug the tools/list cache boundary before rewriting tool code. On MCP 2026-07-28, cache hints are part of the modern result contract and clients can also keep a valid tool catalog fresh for a declared TTL. Those are two different failure classes with different fixes.
Published 13 September 2026 · sources checked 13 September 2026
ttlMs
How long a cacheable MCP result may be treated as fresh
cacheScope
Whether a cached result may cross authorization contexts
0 / private
Conservative modern defaults while debugging
tools/list
The first payload to inspect when an entire server's tools disappear
Why this is showing up now
Modern MCP clients are finally exercising the cache contract
The 2026-07-28 MCP revision added cacheable results for operations including tools/list, prompts/list, resources/list, resources/templates/list, resources/read and server/discover. Current official TypeScript migration guidance says modern cacheable results carry ttlMs and cacheScope, with conservative SDK defaults of 0 and private. The current Python SDK likewise documents those defaults and a client cache that honors them.
That creates two practical operator problems. First, a strict host can reject a malformed or era-mismatched list response before it ever registers the server's tools. Second, a valid positive TTL can make a previously fetched catalog remain visible after the server has added or removed tools if the host does not refresh or invalidate it correctly.
Fresh Claude Code reports demonstrate both failure shapes: one report shows a 2026-07-28 tools/list result being rejected around missing cache fields, while another shows a remote server returning a changed catalog with a five-minute cache hint but the running application continuing to expose the old tool set until restart. Treat those reports as client/interop evidence, not as a reason to weaken the MCP contract globally.
Fast diagnosis
Separate schema rejection from stale caching
| Signal | Likely boundary | First action |
|---|---|---|
| tools/list says invalid ttlMs or cacheScope | Modern result-shape validation | Return the current 2026-07-28 cache fields and resultType at the top level of the result. |
| Connection succeeds but every tool vanishes | List result rejected before registration | Inspect the MCP client logs and raw tools/list payload rather than debugging each tool implementation. |
| New tools appear only after an app restart | Stale client-side tool-list cache | Prove the origin returns the new catalog, then use an explicit refresh/restart workaround instead of changing the server contract blindly. |
| One user sees another user's tool surface | Unsafe shared caching | Use cacheScope: private and partition caches by authorization context. |
| Server is hammered with repeated tools/list calls | TTL is zero or hints are not being honored | Only raise ttlMs after the catalog's freshness and invalidation behavior are understood. |
Known-good baseline
Start with an immediately stale, authorization-private result
When interoperability is the priority, make the modern result unambiguous before tuning performance. A minimal tools/list result should include the modern result type and explicit cache policy alongside the tools array:
{
"resultType": "complete",
"tools": [
{
"name": "example",
"inputSchema": { "type": "object" }
}
],
"ttlMs": 0,
"cacheScope": "private"
}This is deliberately conservative. It does not promise that the catalog stays fresh for any period, and it does not authorize a shared cache to reuse one caller's result for another caller. Once every target client accepts the result, performance tuning can happen separately.
Cache semantics
A TTL is performance policy; cacheScope is a trust boundary
| Setting | Meaning | Operator rule |
|---|---|---|
ttlMs: 0 | Immediately stale | Safest compatibility default while debugging; clients may re-fetch every time. |
ttlMs > 0 | Fresh for the advertised number of milliseconds | Use for genuinely stable catalogs; test how changes become visible before choosing a long TTL. |
cacheScope: private | Reusable only inside the same authorization context | Default for token-, user-, tenant- or scope-dependent tool/resource visibility. |
cacheScope: public | May be shared across authorization contexts | Use only when the result is identical for every caller and contains no user-specific visibility or data. |
Do not use cacheScope: public merely because the endpoint itself is public or because the tool names look harmless. If OAuth scopes, tenant membership, account plan or any other authorization input changes which tools or resources a caller can see, the result is authorization-specific and should stay private.
A mistaken public cache can become more than a freshness bug: it can expose the existence of privileged tools to the wrong authorization context or cause a client to believe it has capabilities that the origin will later reject.
Repair sequence
Seven checks before changing tool implementations
- 1. Capture the negotiated protocol revision. A 2025-era and 2026-07-28 response do not have the same wire expectations. Do not infer the revision from the package version alone.
- 2. Fetch
tools/listoutside the failing host. Confirm whether the origin actually returns the missing tools. This separates server truth from client cache state. - 3. Validate the modern result envelope. On the 2026-07-28 path, inspect
resultType,ttlMsandcacheScopeas top-level result fields before debugging individual tool schemas. - 4. Fall back to
0/private. Remove performance assumptions temporarily. If the host now sees the tools, reintroduce a positive TTL only after the refresh path is proven. - 5. Test authorization separation. If two tokens or tenants can receive different catalogs, verify they never share a cached private result.
- 6. Exercise the client refresh path. Use a supported cache refresh mode when available. If only a full app restart makes a server-confirmed new catalog appear, capture that as client behavior instead of changing the server's identity or lying about protocol version.
- 7. Test removal as well as addition. A stale cache that hides a new tool is annoying; a stale cache that keeps presenting a revoked or removed tool can be operationally worse.
What not to do
Do not turn cache busting into protocol drift
Avoid changing server names, inventing protocol versions, rotating endpoints or adding random query strings merely to force a host to forget its cache. Those workarounds make identity and discovery harder to reason about and can conceal the original interoperability defect.
Likewise, do not choose public to improve cache hit rate until you have proven the catalog is authorization-invariant. Cache performance is not worth collapsing a trust boundary.
Sources
Current protocol and implementation evidence
Protocol behavior above is based on current official MCP SDK documentation and schema plus fresh client interoperability reports checked on 13 September 2026. Client bugs and SDK behavior can change quickly, so use the protocol revision and raw wire response as the durable diagnostic anchors.
Continue
Follow the failure to the next MCP boundary
Protocol era
Migrate MCP 2026-07-28 safely
Debug server/discover, removed sessions and dual-era client/server behavior.
HTTP headers
Fix MCP HeaderMismatch and x-mcp-header failures
Check modern routing headers when requests fail before tool execution.
Tool output
Fix structuredContent and outputSchema failures
Use this when the tool is visible and runs, but the result disappears before the model sees it.
Machine API
Inspect zFinia's machine services
See machine-readable service contracts, API entry points and current paid capabilities.