← AI Agents Open Protocols
🤖 AI Agents

MCP Caching: Optimizing ttlMs and cacheScope for Agents

Source: mortalapps.com
TL;DR
  • MCP caching allows servers to define the validity and visibility of tool responses using ttlMs and cacheScope metadata.
  • It solves the problem of redundant backend API calls during iterative agent reasoning loops.
  • Production significance lies in reducing P99 latency and preventing thundering herd scenarios on legacy systems.
  • Correct implementation ensures agents operate on fresh data while minimizing operational costs and token consumption.

Why This Matters

In production agentic systems, the 'reasoning loop' often involves an agent calling the same tool multiple times to verify state or retrieve incremental updates. Without a standardized caching mechanism, every tool execution triggers a full round-trip to the backend infrastructure. This approach was invented to decouple the high-frequency polling behavior of LLMs from the underlying data sources, which are often rate-limited or computationally expensive to query. If you ignore MCP caching, your system will likely suffer from 'agentic thundering herds' where a single user request results in dozens of identical, expensive API calls, leading to unnecessary latency and potential service outages. From an enterprise perspective, caching is not just a performance optimization but a cost-control and reliability requirement. It allows developers to specify exactly how long a piece of information remains valid (ttlMs) and who is allowed to see it (cacheScope). This is superior to client-side caching because the server - the domain authority - dictates the freshness policy based on the volatility of the underlying data. Use this when your tools wrap external APIs, databases, or long-running computations where the result is idempotent for a known duration. Avoid this for tools that perform state-changing actions, such as 'delete_record' or 'send_email', where caching would lead to incorrect agent perceptions of the system state.

Core Concepts

The Model Context Protocol introduces specific metadata fields to manage the lifecycle of tool outputs. Understanding these terms is foundational to building efficient agentic connectors.

  • ttlMs (Time-to-Live in Milliseconds): A numeric value indicating the duration for which a client should consider a tool response valid. Once expired, the client must re-execute the tool.
  • cacheScope: A string enum defining the visibility boundary of the cached result. It determines if a result can be shared across different sessions or users.
  • Global Scope: Indicates the response is generic and can be served to any client requesting the tool with identical arguments (e.g., a public weather report).
  • Session Scope: Restricts the cache to the current connection or user session, preventing sensitive data leakage (e.g., a user's private inbox status).
  • Idempotency: The property where multiple identical requests return the same result. Caching is only safe for idempotent tool operations.
  • Argument Hashing: The process by which a client generates a unique key for a cache entry based on the tool name and the specific input parameters provided by the LLM.

How It Works

The MCP caching lifecycle is a coordinated effort between the server and the client host. It follows a structured sequence to ensure data integrity.

1. The Tool Request Phase

When an agent decides to use a tool, the client host first checks its local cache. It generates a hash of the tool name and the JSON-serialized arguments. If a match is found and the ttlMs has not elapsed, the client returns the cached result immediately, bypassing the server entirely.

2. Server Execution and Metadata Attachment

If a cache miss occurs, the request is forwarded to the MCP server. The server executes the underlying logic (e.g., querying a database). Along with the standard content array, the server attaches a _meta object containing the ttlMs and cacheScope. This metadata informs the client how to handle the result for future requests.

3. Client-Side Storage

Upon receiving the response, the client stores the result in its cache. The storage logic respects the cacheScope. If the scope is session, the entry is tagged with the session ID. If global, it is stored in a shared pool. The client also calculates the absolute expiration time by adding ttlMs to the current system clock.

4. Failure and Invalidation

If the backend API fails during execution, the server should not return a cacheable metadata object, or it should set ttlMs to 0. Clients must also implement a mechanism to invalidate the cache if an agent performs a related state-changing action that would render the cached data stale, though this is often handled by short TTLs in dynamic environments.

Architecture

The architecture consists of four primary layers. Execution begins at the Agent Runtime (Client).

  1. Agent Runtime: Hosts the LLM and the MCP Client. It contains the Cache Manager, which intercepts tool calls.
  2. Cache Store: A key-value store (in-memory for local agents, Redis for distributed systems) that maps argument hashes to tool outputs and metadata.
  3. MCP Server: The middleware that translates protocol requests into backend actions. It is responsible for calculating the appropriate TTL based on data volatility.
  4. Backend Systems: The source of truth (APIs, DBs). Data flows from the Backend to the Server, where metadata is appended, then to the Client, where it is stored in the Cache Store before being returned to the Agent.

The Decision Framework for ttlMs

Selecting the correct ttlMs requires balancing agent performance against data accuracy. For static or slow-changing data, such as organizational charts or documentation, a TTL of several hours (e.g., 3,600,000 ms) is appropriate. For highly dynamic data like system metrics or stock prices, a TTL should be measured in seconds (e.g., 5,000 ms to 30,000 ms). A common production pattern is 'Adaptive TTL,' where the server adjusts the duration based on the current load or the specific resource being accessed. If a backend is under heavy load, the server might temporarily increase ttlMs to protect the resource.

Scoping with cacheScope: Global vs. Session

The cacheScope parameter is the primary mechanism for ensuring multi-tenant security.

  • Global Scope: Use this for data that is invariant across all users. Examples include public API documentation, general knowledge bases, or shared configuration files. Global caching maximizes hit rates across the entire system.
  • Session Scope: Use this for any data that is user-specific or context-dependent. If a tool retrieves 'My Recent Tasks,' it must be scoped to the session. Failure to use session scoping for private data is a critical security vulnerability that can lead to cross-user data leakage.

Cache Key Generation and Collisions

Clients generate cache keys by hashing the tool name and its arguments. In production, it is vital to use a deterministic serialization method (like sorted JSON keys) before hashing to ensure that {"a": 1, "b": 2} and {"b": 2, "a": 1} produce the same key. Using a cryptographic hash like SHA-256 prevents collisions. Developers must also ensure that the cacheScope is part of the key derivation logic to prevent a global cache entry from being overwritten by a session-specific one.

Handling Non-Deterministic Tools

Some tools are inherently non-deterministic, such as those generating random numbers or timestamps. These tools should never return a ttlMs greater than 0. If a tool's output changes every time it is called regardless of input, caching will break the agent's logic. The server developer must explicitly omit caching metadata for these tools to ensure the client always fetches a fresh result.

Code Example

Python MCP server implementing a cached weather tool with global scope.
Python
from mcp.server.fastmcp import FastMCP
import time
import os

mcp_server = FastMCP("weather-service")

@mcp_server.tool()
async def get_current_weather(city: str) -> dict:
    # In a real scenario, this hits a paid API
    # We use ttlMs to reduce API costs
    weather_data = {"temp": 22, "condition": "Cloudy"}
    
    return {
        "content": [
            {"type": "text", "text": f"The weather in {city} is {weather_data['temp']}C and {weather_data['condition']}."}
        ],
        "_meta": {
            "ttlMs": 600000, # Cache for 10 minutes
            "cacheScope": "global"
        }
    }

if __name__ == "__main__":
    mcp_mcp_server.run()
Expected Output
A JSON response containing the weather text and a _meta object with ttlMs: 600000 and cacheScope: 'global'.
Implementing session-scoped caching for user-specific data.
Python
@mcp_server.tool()
async def get_user_preferences(user_id: str) -> dict:
    # Sensitive data must be session-scoped
    user_prefs = {"theme": "dark", "notifications": True}
    
    return {
        "content": [
            {"type": "text", "text": f"Preferences for {user_id}: {user_prefs}"}
        ],
        "_meta": {
            "ttlMs": 300000, # 5 minutes
            "cacheScope": "session"
        }
    }
Expected Output
A JSON response where the _meta object specifies 'session' scope, ensuring the data isn't shared with other users.

Key Takeaways

ttlMs defines the duration of validity for a tool response in milliseconds.
cacheScope (global vs. session) controls the visibility of cached data across users.
Caching is only safe for idempotent tool operations; state-changing tools must not be cached.
Server-side control of caching is superior to client-side logic because the server understands data volatility.
Implementing caching reduces P99 latency and protects backend infrastructure from thundering herd loads.
Deterministic argument serialization is required for effective cache key generation.
Security in multi-tenant systems relies on the correct application of session-scoped caching.

Frequently Asked Questions

What is the default behavior if ttlMs is not provided? +
If the server does not provide ttlMs, the client should assume the response is not cacheable and fetch fresh data for every request.
Can a client override the server's ttlMs? +
Technically yes, but it is discouraged. The server is the authority on data freshness; a client overriding this risks using stale or incorrect information.
How does cacheScope: 'session' work in a stateless HTTP transport? +
In stateless transports, the session is typically identified by a Bearer token or a specific session header. The client must use this identifier as part of the cache key.
Does caching work for tools with large binary outputs? +
Yes, but it requires significant client-side memory. For large outputs, consider caching a reference or using a persistent disk-based cache.
What happens if the system clock on the client and server are out of sync? +
Since ttlMs is a relative duration (milliseconds from now), clock skew between client and server does not affect the validity period.
Can I manually invalidate an MCP cache entry? +
The protocol does not currently define a standard 'invalidate' message. Invalidation is typically handled by the TTL expiring or the client clearing its local store.
Is it safe to cache error responses? +
Only if the error is deterministic (e.g., 'Invalid Argument'). Transient errors like '503 Service Unavailable' should have a ttlMs of 0.
How do I handle tools that have optional arguments? +
The cache key should be generated from the final resolved argument set, including any default values applied by the server.