---
title: "Send cache hints and a stable order with · Agent Wranglers"
url: https://agentwranglers.se/playbook/agents-as-users/mcp-servers/list-cache-hints
id: AGT-17
level: Recommended
updated: 2026-10-22
tokens: 942
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Send cache hints and a stable order with every list

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [MCP servers](https://agentwranglers.se/playbook/agents-as-users/mcp-servers.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-22.

- ID: AGT-17
- Level: Recommended
- Chapter: [MCP servers](https://agentwranglers.se/playbook/agents-as-users/mcp-servers.md)
- Rule: Return list results in the same order while the set is unchanged. Give each cacheable result a `ttlMs`, and a `cacheScope` of `private` whenever it depends on who asks.
- Check: Call `tools/list` twice with the same token. The two answers are identical byte for byte, and both carry `ttlMs` and `cacheScope`. A list that changes with the token's scopes says `"cacheScope": "private"`.
- How: The 2026-07-28 revision requires `ttlMs` and `cacheScope` on `tools/list`, `prompts/list`, `resources/list`, `resources/templates/list` and `resources/read`. A stable order lets clients cache the list and keeps the model provider's prompt cache warm. Sort by name rather than by registration order or a hash map. Use `public` only for answers that are the same for every caller. A tool list filtered by scope is `private`, or a shared proxy hands one user's tools to another. Set `ttlMs` from how often the list changes, such as 300,000 for a list that changes with deploys. Send `notifications/tools/list_changed` on `subscriptions/listen` when it changes sooner. The same idea for HTTP is [Keep private pages out of shared caches](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/private-responses-uncached.md).

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
const LIST_TTL_MS = 300_000;
export const toolListResult = (tools, variesByCaller) => ({
  resultType: 'complete',
  tools: [...tools].sort((a, b) => a.name.localeCompare(b.name)),
  ttlMs: LIST_TTL_MS,
  cacheScope: variesByCaller ? 'private' : 'public',
});
```

### Python example

```python
LIST_TTL_MS = 300_000

def tool_list_result(tools: list[dict], varies_by_caller: bool) -> dict:
    return {"resultType": "complete", "tools": sorted(tools, key=lambda tool: tool["name"]),
            "ttlMs": LIST_TTL_MS, "cacheScope": "private" if varies_by_caller else "public"}
```

## Sources

- [MCP specification on tools (listing tools)](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)
- [MCP specification 2026-07-28, key changes](https://modelcontextprotocol.io/specification/2026-07-28/changelog)

## Related rules

- SEC-53 [Keep private pages out of shared caches](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/private-responses-uncached.md) in Adversarial testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/agents-as-users/mcp-servers/list-cache-hints">Agent Wranglers agents as users playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Report bad arguments as tool results instead of protocol errors](https://agentwranglers.se/playbook/agents-as-users/mcp-servers/tool-errors-as-results.md)
- Next: [Sign the state you hand back for a second round](https://agentwranglers.se/playbook/agents-as-users/mcp-servers/signed-request-state.md)
- [MCP servers](https://agentwranglers.se/playbook/agents-as-users/mcp-servers.md)
