Skip to main content
The Cache Plugin provides transparent response caching for tools based on their input payloads, reducing redundant computation and improving response time.

Why Use Caching?

Faster Responses

Return cached results instantly without re-executing expensive operations

Reduce Load

Minimize API calls, database queries, and computational overhead

Cost Savings

Lower infrastructure costs by reducing redundant processing

Better UX

Improve perceived performance with instant responses for repeated queries

Installation

How It Works

1

Before Execution

The plugin hashes the tool’s validated input and checks the cache store
2

Cache Hit

If a cached result exists, it’s returned immediately, bypassing tool execution entirely. The result has the same content and structuredContent as the call that filled the cache, and its own _meta.cache is 'hit'
3

Cache Miss

The tool executes normally, and the result is stored with the configured TTL
4

Sliding Window (Optional)

When enabled, each cache read refreshes the TTL to keep hot entries alive longer
A hit is marked on the result’s _meta, not merged into the cached data, so a tool without an outputSchema never finds _meta in its structuredContent or text. A plugin hook that adds result metadata of its own sets the tools:call-tool flow state’s resultMeta the same way: flowCtx.state.set('resultMeta', { ...flowCtx.state.resultMeta, traced: true }).
Cache entries are keyed using a deterministic hash of the tool name, the tool’s validated input, and — by default — the caller’s identity. Two calls share an entry only when all three match, so one user’s response is never served to another. See keyByIdentity below to opt out for data that is identical for every caller.

Quick Start

Basic Setup (In-Memory)

Enable Caching on Tools

Caching is opt-in per tool. Add the cache field to your tool metadata:

Storage Options

In-Memory (Default)

Best for: Single-instance deployments, development, non-critical caching
Memory cache resets when the process restarts. Not shared across multiple instances, and not shared between servers in one process either: every server gets its own memory store, even when several servers are built from the same app class or CachePlugin.init() result.
Best for: Multi-instance deployments, persistent caching, production environments
Redis enables cache sharing across multiple server instances and persists cache across restarts.

Configuration Options

Plugin-Level Configuration

Configure default behavior when registering the plugin:
'memory' | 'redis' | 'redis-client'
default:"'memory'"
Cache store backend to use
number
default:"86400"
Default time-to-live in seconds (applies to all cached tools unless overridden)
boolean
default:"true"
Include the caller’s identity in the cache key.On by default. Most cached tools return something that depends on who is asking — a profile, a balance, a tenant’s records, anything filtered by the caller’s own permissions — and a key built only from the tool and its arguments serves the first caller’s response to everyone else.The identity is the subject your auth layer puts in authInfo.extra (sub / userId), then the client id, then the session. For a request the SDK verified, the client id is the token’s sub, or anon:<id> for an anonymous session. Only a session the server verified (FrontMcpContext.verifiedSessionId) is an identity: the session id the stateless HTTP transport gives every request, the fresh per-request id of a request that sends no mcp-session-id, and an mcp-session-id the server did not accept are not. A call with no identity at all gets a key of its own rather than one shared with every other identity-less caller.
Set this to false only when every caller would get byte-identical output: public reference data, a currency table, a static document. Anything derived from the caller’s permissions, tenant or account must leave it on.
object
Redis connection configuration (required when type: 'redis')
Redis
Existing ioredis client instance (required when type: 'redis-client')
string[]
Tool names or glob patterns to cache. Tools matching these patterns use defaultTTL unless they have custom cache metadata.
string
default:"'x-frontmcp-disable-cache'"
HTTP header name that clients can send to bypass cache for a specific request. When present with value 'true' or '1', cache read/write is skipped.

Tool-Level Configuration

Configure caching behavior per tool in the @Tool or tool() metadata:
boolean | object
Enable caching for this tool
  • true - Use plugin defaults
  • object - Custom configuration
number
Time-to-live in seconds for this tool’s cache entries (overrides plugin default). 0 (or less) turns caching off for the tool, including when it matches toolPatterns. Up to 1.8.5, ttl: 0 cached the first result with no expiry.
boolean
default:"false"
When true, reading from cache refreshes the TTL, keeping frequently accessed entries alive longer

Caching Remote Tools

For remote MCP tools that you don’t control (connected via URL), use the toolPatterns option to enable caching by name or pattern:

Pattern Syntax

Priority Rules

  1. Tool metadata takes precedence - If a tool has cache: { ttl: 60 } metadata, that TTL is used
  2. Pattern list uses defaultTTL - Matched tools without metadata use the plugin’s default TTL
  3. Union behavior - A tool is cached if it matches toolPatterns OR has cache metadata
  4. ttl: 0 opts out - A tool with cache: { ttl: 0 } is never cached, even when it matches toolPatterns
The toolPatterns option is especially useful for remote MCP servers where you can’t add cache: true to tool metadata directly.

Bypassing Cache

Clients can bypass caching for specific requests by sending a header:
Configure a custom header name:
Cache bypass is useful for debugging, forcing fresh data, or when the client knows the cached data is stale.

Advanced Usage

Multi-Tenant Caching

Caller isolation is automatic — the key includes the caller’s identity, so one tenant is never served another’s entry. A tenantId in the input is an ordinary field that selects which data to fetch:
The cache key is a SHA-256 digest of the tool name, the validated input and the caller’s identity. Identifiers in the input select what is fetched; they are not what keeps callers apart. Set keyByIdentity: false only when every caller would get byte-identical output.

Session-Scoped Caching

User-specific data needs no special handling — the caller’s identity is part of the key, so each caller gets their own entry:

Time-Based Invalidation

Use short TTLs for frequently changing data:

Best Practices

Cache tools whose outputs depend solely on their inputs. Don’t cache tools that:
  • Return random data
  • Depend on external time-sensitive state
  • Have side effects (mutations, API calls that change state)
  • Short TTLs (5-60s): Real-time data, frequently changing content - Medium TTLs (5-30min): User dashboards, reports, analytics - Long TTLs (hours-days): Static content, configuration, reference data
Redis provides: - Cache persistence across restarts - Sharing across multiple server instances - Better memory management with eviction policies
Always include tenant IDs, user IDs, or other scoping fields in your tool inputs:
Enable slideWindow for frequently accessed data to keep it cached longer:

Cache Behavior Reference


Troubleshooting

Possible causes:
  • Tool missing cache: true in metadata
  • Cache store offline or misconfigured
  • Input varies slightly (whitespace, order of fields)
Solutions:
  • Verify cache field is set in tool metadata
  • Check Redis connection if using Redis backend
  • Ensure input structure is consistent
Possible causes:
  • TTL too long for data freshness requirements
  • Data changed but cache not invalidated
Solutions:
  • Reduce TTL for the tool
  • Consider input-based cache busting (include timestamp or version in input)
  • Restart server to clear memory cache (or flush Redis)
Possible cause:
  • Using memory cache with multiple server instances
Solution:
  • Switch to Redis backend for multi-instance deployments
Solution:
  • Currently, manual invalidation requires custom implementation
  • For memory: restart the server
  • For Redis: use Redis CLI to delete keys manually
  • Consider shorter TTLs or input-based versioning instead

Complete Example


Source Code

View the cache plugin source code

Demo Application

See caching in action with real examples

Plugin Guide

Learn more about FrontMCP plugins

Redis Documentation

Official Redis documentation