Server data from the Official MCP Registry
Inspect and operate Next.js Redis caches: health, tag state, SWR simulation, safe invalidation.
About
Inspect and operate Next.js Redis caches: health, tag state, SWR simulation, safe invalidation.
Security Report
This is a Next.js Redis cache handler MCP server with well-structured code and appropriate security patterns for its purpose. Redis connection details are properly sourced from environment variables, and dangerous operations like shell execution and data exfiltration are not present. However, several code quality issues around error handling, input validation, and logging practices create minor security concerns that prevent a higher score. Supply chain analysis found 12 known vulnerabilities in dependencies (2 critical, 5 high severity). Package verification found 1 issue (1 critical, 0 high severity).
4 files analyzed ยท 19 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
Unverified package source
We couldn't verify that the installable package matches the reviewed source code. Proceed with caution.
What You'll Need
Set these up before or after installing:
Environment variable: REDIS_URL
Environment variable: DEPLOYMENT_VERSION
Environment variable: HASH_TAG
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-leejpsd-nextjs-cache-handler-mcp": {
"env": {
"HASH_TAG": "your-hash-tag-here",
"REDIS_URL": "your-redis-url-here",
"DEPLOYMENT_VERSION": "your-deployment-version-here"
},
"args": [
"-y",
"@leejpsd/nextjs-cache-handler"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
@leejpsd/nextjs-cache-handler
v0.3.0โ install withnpm install @leejpsd/nextjs-cache-handler. Production-validated against AWS ECS Fargate with multi-instance Redis (v0.1: 24h live-traffic soak; v0.3: fresh multi-instance verification incl. a live Redis-reboot drill with zero 5xx โ seedocs/staging-verification-2026-08-01.md).v0.3 adds: Next.js 15 support (ISR handler), reconnect with exponential backoff (no more permanent memory-only latch), request-scoped GET deduplication, transparent gzip/brotli compression, Redis Sentinel support, a built-in OpenTelemetry emitter at
/otel, and an LRU-bounded memory fallback.
๐ค For AI agents
Working with Claude Code / Codex / Cursor? Give your agent this URL and it will install and wire everything (version detection, wrapper files, next.config patch, verification):
https://raw.githubusercontent.com/leejpsd/nextjs-cache-handler/main/setup-instructions/setup.md
An agent skill with decision tables, invalidation semantics, and a
troubleshooting playbook ships in the package (AGENTS.md,
skills/nextjs-redis-cache/SKILL.md) and via
npx skills add leejpsd/nextjs-cache-handler.
For cache operations from your agent (health, tag state, safe invalidation),
the companion MCP server is on the official registry as
io.github.leejpsd/nextjs-cache-handler-mcp โ or one command:
npx nextjs-cache-handler init --yes wires handlers, rules, and .mcp.json together.
The Redis cache handler for Next.js 15/16 that ships both cacheHandler
(ISR / Pages Router) and cacheHandlers ('use cache' directive,
cacheComponents: true) โ the area where
@fortedigital/nextjs-cache-handler
currently lists "Help needed".
// next.config.ts
const nextConfig = {
cacheComponents: true,
cacheHandler: require.resolve("./cache-incremental.cjs"),
cacheHandlers: { default: require.resolve("./cache-components.cjs") },
};
// cache-components.cjs
const { createCacheComponentsHandler } = require("@leejpsd/nextjs-cache-handler/cache-components");
module.exports = createCacheComponentsHandler({
client: { type: "redis", url: process.env.REDIS_URL },
buildNamespace: process.env.DEPLOYMENT_VERSION, // auto-isolates deploys
});
That's it. 'use cache', revalidateTag, updateTag, cacheLife all work.
Why this exists
Next.js 16 split caching into two handler interfaces:
| Option | Used by | Methods |
|---|---|---|
cacheHandler (singular) | Pages Router ISR, on-demand revalidation | get, set, revalidateTag, resetRequestCache |
cacheHandlers (plural) | 'use cache' directive, cacheComponents: true | get, set, refreshTags, getExpiration, updateTags |
As of 2026-05, the leading OSS Redis handler @fortedigital/nextjs-cache-handler@3.2.0
declares peerDependencies.next: ">=16.1.5" but the README marks the new
plural interface as โ "Not yet supported - Help needed":
๐ Compatibility matrix re-verified 2026-07-31 (from each project's published README/registry metadata). The OSS Next.js cache handler ecosystem moves quickly โ please verify
@fortedigitalandnextjs-turbo-redis-cachedirectly before relying on this comparison.
| Feature | this (0.3.0) | @fortedigital 3.2.1 | nextjs-turbo-redis-cache 1.15 |
|---|---|---|---|
cacheHandlers config (plural) | โ | โ Help needed | โ since 1.11 |
'use cache' directive | โ | โ Help needed | โ since 1.11 |
'use cache: remote' | โ default handler (dedicated multi-tier: roadmap) | โ Help needed | partial |
'use cache: private' | n/a (uncustomizable) | n/a | n/a |
cacheComponents: true | โ | โ Help needed | โ |
Build-phase skip (PHASE_PRODUCTION_BUILD) | โ | โ (singular only) | โ |
| Auto deploy isolation | โ
BUILD_NAMESPACE env-resolved | manual | โ
BUILD_ID since 1.13 |
| Lua-atomic SET+tag | โ Lua scripts | partial (MULTI) | partial |
| AbortSignal timeout | โ per-op | โ Proxy-wrapped | โ |
| Redis Cluster | โ (cluster adapter, see Production checklist) | โ | โ |
| ioredis support | โ | โ | โ |
| In-memory fallback (TTL-aware) | โ | partial | โ L1 + Redis L2 |
| Next 15 support (ISR handler) | โ
>=15.0.0 | โ (legacy 2.x line) | โ
>=15.0.3 |
| Request-scoped GET dedup | โ | โ | โ |
| Built-in value compression | โ gzip/brotli option | example only | example only |
| Redis Sentinel | โ (local failover drill) | โ | โ |
| OpenTelemetry | โ
built-in /otel emitter + onMetric hook | โ | โ |
| Reconnect strategy | โ exponential backoff | client-level | error-threshold restart |
| Live-traffic dogfood (24h+) | โ AWS ECS Fargate | not published | not published |
PR #207 on
@fortedigital (their cacheHandlers attempt) was held up in review over
PHASE_PRODUCTION_BUILD handling โ which this package has from the start.
Quick start
Install
npm install @leejpsd/nextjs-cache-handler redis
# or
npm install @leejpsd/nextjs-cache-handler ioredis
redis and ioredis are optional peer dependencies โ install whichever
client you use. Both can be present.
Wire up
Two CommonJS wrapper files in your project root (Next.js's
require.resolve pattern doesn't accept ESM directly):
// cache-components.cjs
const { createCacheComponentsHandler } = require("@leejpsd/nextjs-cache-handler/cache-components");
module.exports = createCacheComponentsHandler({
client: { type: "redis", url: process.env.REDIS_URL },
buildNamespace: process.env.DEPLOYMENT_VERSION,
abortTimeoutMs: 1500,
});
// cache-incremental.cjs
const { createIncrementalCacheHandler } = require("@leejpsd/nextjs-cache-handler/incremental");
module.exports = createIncrementalCacheHandler({
client: { type: "redis", url: process.env.REDIS_URL },
buildNamespace: process.env.DEPLOYMENT_VERSION,
abortTimeoutMs: 1500,
});
// next.config.ts
import path from "path";
import type { NextConfig } from "next";
const enabled = !!process.env.REDIS_URL && process.env.DISABLE_REDIS_CACHE_HANDLER !== "true";
const nextConfig: NextConfig = {
output: "standalone",
outputFileTracingRoot: path.join(__dirname),
cacheComponents: true,
deploymentId: process.env.DEPLOYMENT_VERSION,
generateBuildId: async () => process.env.DEPLOYMENT_VERSION ?? "dev-build",
cacheMaxMemorySize: 0, // delegate everything to the Redis handler
cacheHandler: enabled ? require.resolve("./cache-incremental.cjs") : undefined,
cacheHandlers: enabled ? { default: require.resolve("./cache-components.cjs") } : {},
};
export default nextConfig;
Use in your code
// app/blog/page.tsx
import { cacheLife, cacheTag, revalidateTag } from "next/cache";
async function getPosts() {
"use cache";
cacheLife("hours");
cacheTag("posts");
return await db.post.findMany();
}
// Server Action โ invalidate
async function publishPost(formData: FormData) {
"use server";
await db.post.create({ data: Object.fromEntries(formData) });
revalidateTag("posts", "max");
}
export default async function Page() {
const posts = await getPosts();
return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}
Configuration reference
interface CacheHandlerOptions {
client: RedisClientFactory | RedisClientConfig;
keyPrefix?: string; // default: "next-cache:" / "next-incremental:"
buildNamespace?: string | (() => string); // default: env DEPLOYMENT_VERSION || GIT_HASH || "unversioned"
abortTimeoutMs?: number; // default: 1500
fallback?: "auto" | "always" | "never"; // default: "auto"
staleWhileRevalidate?: boolean; // default: true (cache-components only)
singleFlight?: boolean; // default: false โ see "Single-flight refresh lock" below
singleFlightLockTtlSec?: number; // default: 10
isBuildPhase?: () => boolean; // override PHASE_PRODUCTION_BUILD detection
hashTag?: boolean; // default: false (set true on Redis Cluster)
memoryMaxEntries?: number; // default: 1000 โ LRU cap for the in-memory fallback
compression?: "gzip" | "brotli"; // default: off โ transparent value compression (node:zlib)
onMetric?: (event: MetricEvent) => void;
logger?: Logger;
}
type RedisClientConfig =
| { type: "redis"; url: string; password?: string; tls?: boolean; connectTimeout?: number }
| { type: "ioredis"; url: string; password?: string; tls?: boolean; connectTimeout?: number }
| { type: "cluster"; nodes: { host: string; port: number }[]; password?: string; tls?: boolean }
| { type: "sentinel"; sentinels: { host: string; port: number }[]; name: string;
password?: string; sentinelPassword?: string; tls?: boolean; connectTimeout?: number };
Full reference: docs/api.md.
Production checklist
-
DEPLOYMENT_VERSIONenv injected at runtime โ every entry key is prefixed with this so old prerender HTML can't bleed across deploys. For Docker, setENV DEPLOYMENT_VERSION=...in your runner stage, not just the builder. (Seedocs/build-phase.md.) -
cacheMaxMemorySize: 0โ turn off Next's local LRU so multi-instance reads always hit Redis (or the explicit memory fallback). -
outputFileTracingRootpinned โ required foroutput: "standalone"to avoid static-chunk-404 issues during a deploy. -
abortTimeoutMs: 1500(default) โ protects against a stuck Redis connection from hanging the request thread. - Redis Cluster: set
hashTag: trueโ multi-key Lua scripts (set-with-tags.lua,revalidate-hard.lua) require all KEYS to land on the same hash slot. WithouthashTag, cluster deployments will hitCROSSSLOT Keys in request don't hash to the same slot. The flag wraps the namespace in{}so every key for a given deploy hashes together. Cluster support is validated by a dedicated e2e suite against a real 3-master cluster (npm run test:cluster, also in CI) โ covering the multi-key Lua scripts, per-master SCAN propagation, and both handlers. Not yet load-tested at production scale. - Redis
maxmemory-policy: allkeys-lruornoevictionโ if you need bounded memory, chooseallkeys-lru. Otherwisenoevictionkeeps tag indices intact. - TLS โ use
rediss://URLs (e.g. ElastiCache in-transit encryption). The library auto-detects from the URL scheme. - Health check โ call your own
/api/healththat pings Redis (separate from the handler) so a Redis outage surfaces in your monitoring without inducing 5xx in user requests.
Compatibility with Redis-protocol services
| Service | How to use | Tested? |
|---|---|---|
| Self-hosted Redis 7+ | { type: "redis", url } or { type: "ioredis", url } | โ AWS ElastiCache 24h soak |
| Redis Cluster | { type: "cluster", nodes } + hashTag: true | โ e2e-tested against a real 3-master cluster (CI); not yet load-tested at scale |
| Upstash Redis | { type: "redis", url: "rediss://..." } (TLS auto-detected) | not yet validated, expected to work via the standard Redis protocol |
| AWS ElastiCache (replication group) | { type: "redis", url: "rediss://..." } | โ reference deployment (re-verified 2026-08-01, Seoul) |
| Redis Sentinel | { type: "sentinel", sentinels, name } | โ local master/replica failover drill |
| Vercel KV | not yet supported โ dedicated adapter on the roadmap | โ |
| DragonflyDB / KeyDB | Redis-protocol compatible โ { type: "redis", url } should work | not validated |
Single-flight refresh lock (v0.2)
The cacheHandlers (plural) interface returns stale entries inside the
SWR window so users get an instant response while the background
refresh completes. With many instances, the moment an entry crosses the
revalidate boundary, every instance independently triggers its own
refresh โ N parallel re-renders for the same key, each hitting your
origin once.
singleFlight: true adds an opt-in Redis lock (refresh-tag-lock.lua,
default TTL 10s) at the SWR boundary. The first instance to acquire it
becomes the leader and runs the refresh; the rest become
followers, keep serving the same stale entry, and wait for the
leader's write to land. The lock is observability-only at the handler
layer โ Next.js still drives the actual refresh; we just suppress the
stampede.
createCacheComponentsHandler({
client: { type: "redis", url: process.env.REDIS_URL },
singleFlight: true, // default false
singleFlightLockTtlSec: 10, // default 10
});
Two new MetricEvent types appear on onMetric:
| event type | meaning |
|---|---|
cache.stale.refresh.leader | this instance just acquired the lock and is the designated refresher |
cache.stale.refresh.follower | another instance holds the lock; we serve stale and skip the refresh |
If lock acquisition fails (Redis hiccup, TTL race), the handler defaults to the follower path โ the stale entry is always served, never dropped. This is intentional: the lock is an optimization, not a correctness-critical primitive.
When not to enable single-flight: small fleets (1โ2 instances) where Next's per-process serialization already covers the stampede risk. Adding a Redis round-trip per stale read isn't free.
See docs/architecture.md
for the full state machine and a reference to the Lua script body.
OpenTelemetry instrumentation (v0.2)
The handler doesn't bundle @opentelemetry/api (zero runtime
dependencies stays a goal). Instead, the onMetric(event) hook gives
strictly-typed events you can pipe into whatever observability stack
you already run.
examples/opentelemetry/ is a copy-paste
reference wrapper that:
- exposes a
nextjs_cache.events_totalcounter dimensioned ontype/freshness/backend/reason/op - exposes a
nextjs_cache.op_latency_mshistogram for events that carry anmsfield - keeps cardinality bounded โ cache keys and tag names are never emitted as attributes
See examples/opentelemetry/README.md
for setup and three suggested dashboards (hit rate, single-flight
leadership distribution, op latency tails).
How it differs from @fortedigital/nextjs-cache-handler
Three deliberate departures, all rooted in lessons from production
incidents (see docs/):
- Build-phase skip is the default, not an opt-in. Every Redis call
goes through a
shouldUseRedis()gate that short-circuits whenprocess.env.NEXT_PHASE === "phase-production-build". PR #207 on@fortedigitalwas rejected for missing exactly this. - Deployment isolation is automatic. Every entry key includes
BUILD_NAMESPACE(=process.env.DEPLOYMENT_VERSION) by default. New deploys can never read entries written by old ones โ fixes the "static chunk 404 after deploy" failure mode without manual cache flushes. - Lua-atomic tag updates.
setwrites the entry and updates tag indices in a single Lua transaction.updateTags(..., {expire: 0})removes matching entries with oneEVALSHA. No window for half-applied sets to leak dangling tag members.
When @fortedigital ships its cacheHandlers support (PR #207 / feature
branch feature/cache-components), this package will continue to differ
on (2) and (3). For (1), we consider it table-stakes; the upstream's
eventual implementation should converge on the same behavior.
Compatibility
- Next.js:
>=15.0.0 <17cacheHandler(singular, ISR): Next 15 and 16 โ the handler accepts both ctx shapes (Next 15'sctx.revalidate/kindHint, Next 16'sctx.cacheControl/kind)cacheHandlers(plural,'use cache'): Next >=16.1.5 only โ the interface does not exist before 16
- Node.js:
>=20 - redis:
>=5.0.0(peer, optional) - ioredis:
>=5.0.0(peer, optional) โ also powerstype: "cluster"andtype: "sentinel"(Sentinel master discovery with automatic failover)
ESM and CJS dual-published, full TypeScript types, validated via
arethetypeswrong and
publint.
Roadmap
- v0.1.0 (2026-05) โ Both handlers, SWR, Lua atomicity, build-phase
skip,
redis@5+ioredisadapters, AbortSignal, in-memory fallback with TTL, soft-tag freshness check โ - v0.2.0 (2026-05) โ Single-flight refresh lock with leader/follower
metrics, OpenTelemetry reference adapter, integration tests against
real Redis 7 (21 scenarios over
redis@5+ioredis), GitHub Actions OIDC publish path with provenance attestation โ - v0.3.0 (2026-08) โ Next.js 15 ISR support, reconnect backoff (no
permanent connect-failure latch), request-scoped GET deduplication,
gzip/brotli compression, Redis Sentinel, built-in OpenTelemetry emitter
(
/otel), LRU-bounded memory fallback, ESM peer-loading fixes โ - v0.4.0 โ Build-phase cache seeding (prepopulate prebuilt pages),
pub/sub-based tag propagation, Redis Cluster e2e validation, Vercel KV /
Upstash adapter,
'use cache: remote'multi-tier setup
License
MIT ยฉ 2026 Eddy Lee
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Git
Freeby Modelcontextprotocol ยท Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno ยท Developer Tools
Toleno Network MCP Server โ Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace ยท Developer Tools
Create, build, and publish Python MCP servers to PyPI โ conversationally.
MarkItDown
Freeby Microsoft ยท Content & Media
Convert files (PDF, Word, Excel, images, audio) to Markdown for LLM consumption
MCP Marketplace
Freeby mcp-marketplace ยท Developer Tools
Search and install MCP servers from inside your AI client.
FinAgent
Freeby mcp-marketplace ยท Finance
Free stock data and market news for any MCP-compatible AI assistant.
