diff --git a/CLAUDE.md b/CLAUDE.md
index 8040cda..3a2497b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -4,7 +4,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
-This is an MCP (Model Context Protocol) server that provides Next.js development tools for AI coding assistants. The server exposes tools, prompts, and resources to help with Next.js upgrades, Cache Components setup, documentation search, browser testing, and runtime diagnostics.
+This is an MCP (Model Context Protocol) server that acts as a thin connector between AI coding assistants and a running Next.js dev server. It discovers running Next.js 16+ dev servers and proxies their built-in MCP endpoint (`/_next/mcp`) for runtime diagnostics, and provides Playwright-based browser automation.
+
+It exposes **tools only** — no prompts or resources. Documentation ships with Next.js itself (`node_modules/next/dist/docs/`), and upgrade/Cache Components workflows are distributed as agent skills, so they are intentionally not part of this server. See the "Migrating from 0.3.x" section of `README.md` for the removal history.
The server is built using the standard `@modelcontextprotocol/sdk` package with TypeScript and ES modules.
@@ -32,47 +34,35 @@ pnpm clean
## Testing
-The test suite uses vitest with Claude Agent SDK for E2E testing:
+The test suite uses vitest:
```bash
-# Run all tests
+# Run unit tests (default; excludes test/e2e/)
pnpm build && pnpm test
-# Note: Tests require ANTHROPIC_API_KEY environment variable
-# Get your key from: https://console.anthropic.com/
+# Run e2e tests (spawns the built server over stdio)
+pnpm build && pnpm test:e2e
```
-Test files are located in `test/e2e/` and use test fixtures from `test/fixtures/`.
+Unit tests live in `test/unit/`; e2e tests in `test/e2e/` spawn `dist/index.js` and exercise it over the MCP protocol. Fixtures are in `test/fixtures/`.
## Architecture
### MCP Server Structure
-The main server entry point is `src/index.ts` which uses the standard MCP SDK with stdio transport. The server manually registers:
-- **Tools** (`src/tools/`): Callable functions for automation - each exports `inputSchema`, `metadata`, and `handler`
-- **Prompts** (`src/prompts/`): Pre-configured prompts for common tasks - each exports `inputSchema`, `metadata`, and `handler`
-- **Resources** (`src/resources/`): Knowledge base articles and documentation - each exports `metadata` and `handler`
-
-All tools, prompts, and resources are explicitly imported and registered in `src/index.ts`.
+The main server entry point is `src/index.ts` which uses the standard MCP SDK with stdio transport. The server declares only the `tools` capability and registers tools from `src/tools/`, each exporting `inputSchema`, `metadata`, and `handler`. There are no prompt or resource handlers.
### Key Components
**MCP Tools** (`src/tools/`):
- Each tool exports: `inputSchema` (Zod schemas), `metadata` (name, description), `handler` (async function)
- Tools are manually imported and registered in `src/index.ts`
-- `nextjs_docs`: Search Next.js documentation and knowledge base
-- `browser_eval`: Playwright browser automation (via `playwright-mcp` server)
+- `nextjs_docs`: Version-aware docs gateway — points agents at the bundled docs in `node_modules/next/dist/docs/` (Next.js 16+) or recommends the upgrade codemod. Does NOT fetch docs.
- `nextjs_index`: Discover all running Next.js dev servers and list their available MCP tools
- `nextjs_call`: Execute specific MCP tools on a running Next.js dev server
-- `upgrade_nextjs_16`: Automated Next.js 16 upgrade guidance
-- `enable_cache_components`: Complete Cache Components setup with error detection
-
-**MCP Client Library** (`src/_internal/mcp-client.ts`):
-- Connects to external MCP servers via stdio transport
-- Used by `browser_eval` to communicate with `playwright-mcp`
+- `browser_eval`: Gateway to the [`agent-browser`](https://github.com/vercel-labs/agent-browser) CLI — detects whether it's installed and returns install/usage guidance. Does NOT drive the browser itself.
**Runtime Managers** (`src/_internal/`):
-- `browser-eval-manager.ts`: Manages Playwright MCP server lifecycle
- `nextjs-runtime-manager.ts`: Discovers and connects to Next.js dev servers with MCP enabled
**Telemetry System** (`src/telemetry/`):
@@ -85,12 +75,6 @@ All tools, prompts, and resources are explicitly imported and registered in `src
- Telemetry can be disabled via `NEXT_TELEMETRY_DISABLED=1` environment variable
- Data stored in `~/.next-devtools-mcp/` (telemetry-id, telemetry-salt, mcp.log)
-**Resources Architecture**:
-- Knowledge base split into focused sections (12 sections for Cache Components, 2 for Next.js 16, 1 for fundamentals)
-- Each resource exports: `metadata` (uri, name, description, mimeType) and `handler` (function returning content)
-- Resources use URI-based addressing (e.g., `cache-components://overview`)
-- Markdown files in `src/resources/` and `src/prompts/` are copied during build via `scripts/copy-resources.js` (to `dist/resources/` and `dist/resources/prompts/` respectively)
-
### TypeScript Configuration
- Target: ES2022, ES modules (NodeNext module resolution)
@@ -101,31 +85,25 @@ All tools, prompts, and resources are explicitly imported and registered in `src
## Build Process
-1. TypeScript compilation: `tsc` compiles all TypeScript files from `src/` to `dist/`
-2. Resource copying: `scripts/copy-resources.js` copies markdown files from `src/resources/` and `src/prompts/` (to `dist/resources/` and `dist/resources/prompts/` respectively)
-
-The `dist/index.js` file is the entry point for the MCP server and includes a shebang for CLI execution.
+`pnpm build` runs `tsc`, compiling all TypeScript files from `src/` to `dist/`. The `dist/index.js` file is the entry point for the MCP server and includes a shebang for CLI execution.
## MCP Protocol Integration
This server can:
1. Act as a standalone MCP server (stdio transport using `@modelcontextprotocol/sdk`)
-2. Connect to other MCP servers as a client (e.g., playwright-mcp, Next.js runtime MCP)
+2. Connect to a running Next.js dev server's MCP endpoint as a client (`nextjs_index` / `nextjs_call`)
**Key MCP Patterns**:
- Server uses standard MCP SDK `Server` class with `StdioServerTransport`
- Tools use Zod schemas for input validation, converted to JSON Schema for MCP
- Tool handlers are called with validated arguments
-- Resources use URI-based addressing (e.g., `cache-components://overview`)
-- Prompts return structured messages with markdown content
-## External MCP Server Dependencies
+## External Dependencies
-**Playwright MCP** (`browser_eval` tool):
-- Automatically installed globally via npm when needed
-- Package: `@playwright/mcp`
-- Command: `npx @playwright/mcp@latest` (with optional `--browser` and `--headless` flags)
-- Used for browser automation and testing
+**agent-browser CLI** (`browser_eval` tool):
+- The [`agent-browser`](https://github.com/vercel-labs/agent-browser) npm package (native browser-automation CLI)
+- `browser_eval` does not spawn or proxy it; it detects whether it's installed (`command -v agent-browser`) and returns install/usage guidance so the agent runs the CLI directly
+- Install: `npm install -g agent-browser` then `agent-browser install`
**Next.js Runtime MCP** (`nextjs_index` and `nextjs_call` tools):
- Built into Next.js 16+ (enabled by default)
@@ -143,26 +121,10 @@ This server can:
2. Import and add to the `tools` array in `src/index.ts`
3. Build and test
-**Adding a new MCP resource**:
-1. Create markdown file(s) in `src/resources/`
-2. Create resource handler TypeScript file in `src/resources/` with:
- - `export const metadata = { uri, name, description, mimeType }`
- - `export function handler() { return readResourceFile(...) }` - Returns content
-3. Import and add to the `resources` array in `src/index.ts`
-4. The `scripts/copy-resources.js` script automatically copies `.md` files to `dist/resources/`
-
-**Adding a new MCP prompt**:
-1. Create prompt file in `src/prompts/` with:
- - `export const inputSchema = { ... }` - Optional Zod schemas for parameters
- - `export const metadata = { name, description, role }`
- - `export function handler(args) { ... }` - Returns prompt text
-2. Import and add to the `prompts` array in `src/index.ts`
-3. Build and test
+> This server intentionally ships tools only. Do not re-add prompt or resource handlers — documentation lives in Next.js's bundled docs (`node_modules/next/dist/docs/`) and workflows are distributed as agent skills.
-**Working with external MCP servers**:
-- Use `src/_internal/mcp-client.ts` for stdio-based communication
-- Create manager module in `src/_internal/` for lifecycle management
-- Handle server installation, connection, and cleanup
+**Connecting to the Next.js dev server**:
+- `src/_internal/nextjs-runtime-manager.ts` discovers running dev servers and forwards JSON-RPC to their `/_next/mcp` endpoint over HTTP (used by `nextjs_index` / `nextjs_call`)
## Package Publishing
diff --git a/README.md b/README.md
index d0c9f77..39c24d3 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,10 @@
[](https://npmjs.org/package/next-devtools-mcp)
-`next-devtools-mcp` is a Model Context Protocol (MCP) server that provides Next.js development tools and utilities for coding agents like Claude and Cursor.
+`next-devtools-mcp` is a Model Context Protocol (MCP) server that connects coding agents like Claude and Cursor to your running Next.js dev server. It discovers running servers and proxies their built-in MCP endpoint (`/_next/mcp`), giving agents live access to runtime errors, routes, and logs — plus a gateway to [`agent-browser`](https://github.com/vercel-labs/agent-browser) for browser testing.
+
+> [!NOTE]
+> This server no longer bundles documentation or migration prompts. Next.js ships its own docs in `node_modules/next/dist/docs/` (surfaced via `AGENTS.md`); the `nextjs_docs` tool now points agents there instead of fetching. Upgrade/Cache Components workflows are moving to agent skills. See [Migrating from 0.3.x](#migrating-from-03x).
## Getting Started
@@ -185,25 +188,15 @@ Navigate to `Settings | AI | Manage MCP Servers` and select `+ Add` to register
## Quick Start
-### For Next.js 16+ Projects (Recommended)
-
-To unlock the full power of runtime diagnostics, start your Next.js dev server:
+Start your Next.js dev server:
```bash
npm run dev
```
-Next.js 16+ has MCP enabled by default at `http://localhost:3000/_next/mcp` (or whichever port your dev server uses). The `next-devtools-mcp` server will automatically discover and connect to it.
+Next.js 16+ has its MCP endpoint enabled by default at `http://localhost:3000/_next/mcp` (or whichever port your dev server uses). `next-devtools-mcp` automatically discovers and connects to it — no configuration needed.
-**⚠️ IMPORTANT: Start every Next.js session by calling the `init` tool to set up proper context:**
-
-```
-Use the init tool to set up Next.js DevTools context
-```
-
-This initializes the MCP context and ensures the AI assistant uses official Next.js documentation for all queries.
-
-**After initialization, try these prompts to explore runtime diagnostics:**
+Then ask your coding agent about your running application:
```
Next Devtools, what errors are in my Next.js application?
@@ -217,203 +210,45 @@ Next Devtools, show me the structure of my routes
Next Devtools, what's in the development server logs?
```
-Your coding agent will use the `nextjs_index` and `nextjs_call` tools to query your running application's actual state.
-
-### For All Next.js Projects
-
-You can use the development automation and documentation tools regardless of Next.js version:
-
-```
-Next Devtools, help me upgrade my Next.js app to version 16
-```
-
-```
-Next Devtools, enable Cache Components in my Next.js app
-```
-
-```
-Next Devtools, search Next.js docs for generateMetadata
-```
-
-### 💡 Pro Tip: Auto-Initialize on Every Session
-
-To make your AI assistant **automatically call the `init` tool** at the start of every Next.js session without being asked, add this instruction to your agent's configuration file:
-
-
-Claude Code / Claude Desktop
-
-Add to `~/.claude/CLAUDE.md` (global) or `./.claude/CLAUDE.md` (project-specific):
-
-```markdown
-**When starting work on a Next.js project, ALWAYS call the `init` tool from
-next-devtools-mcp FIRST to set up proper context and establish documentation
-requirements. Do this automatically without being asked.**
-```
-
-
-
-
-Cursor
-
-Add to `.cursorrules` in your project root or global Cursor settings:
-
-```
-When working with Next.js, always call the init tool from next-devtools-mcp
-at the start of the session to establish proper context and documentation requirements.
-```
-
-
-
-
-Codex / Other AI Coding Assistants
-
-Add to your agent's configuration file (e.g., `.codex/instructions.md`, `agent.md`, or similar):
-
-```markdown
-**Next.js Initialization**: When starting work on a Next.js project, automatically
-call the `init` tool from the next-devtools-mcp server FIRST. This establishes
-proper context and ensures all Next.js queries use official documentation.
-```
-
-
-
-**Why this matters:**
-- ✅ Ensures consistent context across all Next.js work
-- ✅ Automatically establishes the documentation-first requirement
-- ✅ No need to manually call init every time
-- ✅ Works across all your Next.js projects
-
-## MCP Resources
-
-The knowledge base resources are automatically available to your coding agent and are split into focused sections for efficient context management. Current resource URIs:
-
-
-📚 Available Knowledge Base Resources (click to expand)
-
-- Cache Components (12 sections):
- - `cache-components://overview`
- - `cache-components://core-mechanics`
- - `cache-components://public-caches`
- - `cache-components://private-caches`
- - `cache-components://runtime-prefetching`
- - `cache-components://request-apis`
- - `cache-components://cache-invalidation`
- - `cache-components://advanced-patterns`
- - `cache-components://build-behavior`
- - `cache-components://error-patterns`
- - `cache-components://test-patterns`
- - `cache-components://reference`
-
-- Next.js 16 migration:
- - `nextjs16://migration/beta-to-stable`
- - `nextjs16://migration/examples`
-
-- Next.js fundamentals:
- - `nextjs-fundamentals://use-client`
-
-
-
-Resources are loaded on-demand by your coding agent, providing targeted knowledge without overwhelming the context window.
-
-## MCP Prompts
+Your agent uses the `nextjs_index` and `nextjs_call` tools to query your running application's actual state.
-Pre-configured prompts to help with common Next.js development tasks:
-
-
-💡 Available Prompts (click to expand)
-
-- **`upgrade-nextjs-16`** - Guide for upgrading to Next.js 16
-- **`enable-cache-components`** - Migrate and enable Cache Components mode for Next.js 16
-
-
+> **Looking for docs, upgrades, or Cache Components setup?** Those no longer live here — see [Migrating from 0.3.x](#migrating-from-03x).
## MCP Tools
-
-init
-
-Initialize Next.js DevTools MCP context and establish documentation requirements.
-
-**Capabilities:**
-- Sets up proper context for AI assistants working with Next.js
-- Establishes requirement to use `nextjs_docs` for ALL Next.js-related queries
-- Documents all available MCP tools and their use cases
-- Provides best practices for Next.js development with MCP
-- Includes example workflows and quick start checklist
-
-**When to use:**
-- At the beginning of a Next.js development session
-- To understand available tools and establish proper context
-- To ensure documentation-first approach for Next.js development
-
-**Input:**
-- `project_path` (optional) - Path to Next.js project (defaults to current directory)
-
-**Output:**
-- Comprehensive initialization context and guidance for Next.js development with MCP tools
-
-
-
nextjs_docs
-Search and retrieve Next.js official documentation and knowledge base.
+Points your agent at the version-accurate Next.js documentation for the current project. **It does not fetch docs** — Next.js 16+ ships its full documentation inside the installed package at `node_modules/next/dist/docs/` (markdown, matching your exact version), and this tool tells the agent where to read it.
-**Capabilities:**
-- Two-step process: 1) Search for docs by keyword to get paths, 2) Fetch full markdown content by path
-- Uses official Next.js documentation search API
-- Provides access to comprehensive Next.js guides, API references, and best practices
-- Supports filtering by router type (App Router, Pages Router, or both)
+**What it does:**
+- Detects the project's installed Next.js version
+- On Next.js 16+: returns the local docs path and how to read/grep it, so the agent uses version-accurate docs instead of training-data guesses
+- On older versions: recommends upgrading with `npx @next/codemod@latest upgrade latest`, which brings the bundled docs and an `AGENTS.md` that points agents to them
**Input:**
-- `action` (required) - Action to perform: `search` to find docs, `get` to fetch full content
-- `query` (optional) - Required for `search`. Keyword search query (e.g., 'metadata', 'generateStaticParams', 'middleware')
-- `path` (optional) - Required for `get`. Doc path from search results (e.g., '/docs/app/api-reference/functions/refresh')
-- `anchor` (optional) - Optional for `get`. Anchor/section from search results (e.g., 'usage')
-- `routerType` (optional) - For `search` only. Filter by: `app`, `pages`, or `all` (default: `all`)
+- `topic` (optional) - What you're looking for (e.g. `use cache`, `generateMetadata`); used only to suggest where to look
+- `project_path` (optional) - Path to the project (defaults to current directory)
**Output:**
-- Search results with doc titles, paths, content snippets, sections, and anchors
-- Full markdown content for specific documentation pages
+- JSON describing where to read the docs, or how to upgrade to get them
browser_eval
-Automate and test web applications using Playwright browser automation.
-
-**When to use:**
-- Verifying pages in Next.js projects (especially during upgrades or testing)
-- Testing user interactions and flows
-- Taking screenshots for visual verification
-- Detecting runtime errors, hydration issues, and client-side problems
-- Capturing browser console errors and warnings
-
-**Important:** For Next.js projects, prioritize using the `nextjs_index` and `nextjs_call` tools instead of browser console log forwarding. Only use browser_eval's `console_messages` action as a fallback when these tools are not available.
-
-**Available actions:**
-- `start` - Start browser automation (automatically installs if needed)
-- `navigate` - Navigate to a URL
-- `click` - Click on an element
-- `type` - Type text into an element
-- `fill_form` - Fill multiple form fields at once
-- `evaluate` - Execute JavaScript in browser context
-- `screenshot` - Take a screenshot of the page
-- `console_messages` - Get browser console messages
-- `close` - Close the browser
-- `drag` - Perform drag and drop
-- `upload_file` - Upload files
-- `list_tools` - List all available browser automation tools from the server
+A gateway to [`agent-browser`](https://github.com/vercel-labs/agent-browser), a fast native browser-automation CLI for agents. **It does not drive the browser itself** — it detects whether `agent-browser` is installed and tells the agent how to install it and where to start, so the agent runs the CLI directly (faster and more capable than proxying automation through MCP).
+
+**What it does:**
+- If `agent-browser` is installed: returns the entry point (`agent-browser skills get core --full`) and example commands
+- If not installed: returns the install steps (`npm install -g agent-browser`, then `agent-browser install`)
**Input:**
-- `action` (required) - The action to perform
-- `browser` (optional) - Browser to use: `chrome`, `firefox`, `webkit`, `msedge` (default: `chrome`)
-- `headless` (optional) - Run browser in headless mode (default: `true`)
-- Action-specific parameters (see tool description for details)
+- `task` (optional) - What you want to do in the browser; used only to tailor the guidance
**Output:**
-- JSON with action result, screenshots (base64), console messages, or error information
+- JSON describing how to install or use `agent-browser`
@@ -494,65 +329,19 @@ Calls a specific runtime diagnostic tool on a Next.js 16+ dev server's built-in
-
-upgrade_nextjs_16
+## Migrating from 0.3.x
-Guides through upgrading Next.js to version 16 with automated codemod execution.
+Starting in 0.4.0, `next-devtools-mcp` is a thin connector to the Next.js dev server.
-**Capabilities:**
-- Runs official Next.js codemod automatically (requires clean git state)
-- Handles async API changes (params, searchParams, cookies, headers)
-- Migrates configuration changes
-- Updates image defaults and optimization
-- Fixes parallel routes and dynamic segments
-- Handles deprecated API removals
-- Provides guidance for React 19 compatibility
+**Changed:**
+- **`nextjs_docs`** no longer fetches documentation over the network. It is now a gateway that points your agent at the version-accurate docs Next.js bundles at `node_modules/next/dist/docs/` (or recommends upgrading if the project is too old). The `nextjs-docs://llms-index` resource is removed.
-**Input:**
-- `project_path` (optional) - Path to Next.js project (defaults to current directory)
-
-**Output:**
-- Structured JSON with step-by-step upgrade guidance
+**Removed:**
+- **`init` tool** — it existed only to enforce the old docs-fetch workflow, which is no longer needed.
+- **`upgrade_nextjs_16` and `enable_cache_components` tools, and their prompts** — these workflows are moving to distributable agent skills.
+- **All `cache-components://`, `nextjs16://`, and `nextjs-fundamentals://` resources** — superseded by the bundled docs.
-
-
-
-enable_cache_components
-
-Complete Cache Components setup, enablement, and migration for Next.js 16 with automated error detection and fixing. This tool is used for migrating Next.js applications to Cache Components mode.
-
-**Capabilities:**
-- Pre-flight checks (package manager, Next.js version, configuration)
-- Enable Cache Components configuration
-- Start dev server with MCP enabled
-- Automated route verification and error detection
-- Automated error fixing with intelligent boundary setup (Suspense, caching directives, static params)
-- Final verification and build testing
-
-**Input:**
-- `project_path` (optional) - Path to Next.js project (defaults to current directory)
-
-**Output:**
-- Structured JSON with complete setup guidance and phase-by-phase instructions
-
-**Example Usage:**
-
-With Claude Code:
-```
-Next Devtools, help me enable Cache Components in my Next.js 16 app
-```
-
-With other agents or programmatically:
-```json
-{
- "tool": "enable_cache_components",
- "args": {
- "project_path": "/path/to/project"
- }
-}
-```
-
-
+What remains: the docs gateway (`nextjs_docs`), server discovery (`nextjs_index`), runtime proxying (`nextjs_call`), and browser automation (`browser_eval`).
## Privacy & Telemetry
@@ -560,7 +349,7 @@ With other agents or programmatically:
`next-devtools-mcp` collects anonymous usage telemetry to help improve the tool. The following data is collected:
-- **Tool usage**: Which MCP tools are invoked (e.g., `nextjs_index`, `nextjs_call`, `browser_eval`, `upgrade_nextjs_16`)
+- **Tool usage**: Which MCP tools are invoked (e.g., `nextjs_index`, `nextjs_call`, `browser_eval`)
- **Error events**: Anonymous error messages when tools fail
- **Session metadata**: Session ID, timestamps, and basic environment info (OS, Node.js version)
@@ -592,11 +381,7 @@ rm -rf ~/.next-devtools-mcp
### Module Not Found Error
-If you encounter an error like:
-
-```
-Error [ERR_MODULE_NOT_FOUND]: Cannot find module '...\next-devtools-mcp\dist\resources\(cache-components)\...'
-```
+If you encounter an `ERR_MODULE_NOT_FOUND` error referencing `next-devtools-mcp/dist`:
**Solution:** Clear your npx cache and restart your MCP client (Cursor, Claude Code, etc.). The server will be freshly installed.
@@ -606,10 +391,10 @@ If you see `[error] No server info found`:
**Solutions:**
1. Make sure your Next.js dev server is running: `npm run dev`
-2. If using Next.js 15 or earlier, use the `upgrade_nextjs_16` tool to upgrade to Next.js 16+
+2. Confirm you are on Next.js 16+ (the `/_next/mcp` endpoint is only available there)
3. Verify your dev server started successfully without errors
-**Note:** The `nextjs_index` and `nextjs_call` tools require Next.js 16+ with a running dev server. Other tools (`nextjs_docs`, `browser_eval`, `upgrade_nextjs_16`, `enable_cache_components`) work without a running server.
+**Note:** The `nextjs_index` and `nextjs_call` tools require Next.js 16+ with a running dev server. `browser_eval` works without one.
## Local Development
@@ -642,7 +427,7 @@ To run the MCP server locally for development:
## Features
-This MCP server provides coding agents with comprehensive Next.js development capabilities through three primary mechanisms:
+This MCP server gives coding agents two capabilities:
### **1. Runtime Diagnostics & Live State Access** (Next.js 16+)
Connect directly to your running Next.js dev server's built-in MCP endpoint to query:
@@ -651,22 +436,14 @@ Connect directly to your running Next.js dev server's built-in MCP endpoint to q
- Development server logs and diagnostics
- Server Actions and component hierarchies
-### **2. Development Automation**
-Tools for common Next.js workflows:
-- **Automated Next.js 16 upgrades** with official codemods
-- **Cache Components migration and setup** with error detection and automated fixes
-- **Browser testing integration** via Playwright for visual verification
-
-### **3. Knowledge Base & Documentation**
-- Curated Next.js 16 knowledge base (12 focused resources on Cache Components, async APIs, etc.)
-- Direct access to official Next.js documentation via search API
-- Pre-configured prompts for upgrade guidance and Cache Components enablement
+### **2. Browser Testing**
+A gateway to the [`agent-browser`](https://github.com/vercel-labs/agent-browser) CLI for visual verification, interaction testing, and capturing client-side errors. The agent runs the CLI directly; `browser_eval` just installs/points to it.
> **Learn more:** See the [Next.js MCP documentation](https://nextjs.org/docs/app/guides/mcp) for details on how MCP servers work with Next.js and coding agents.
## How It Works
-This package provides a **bridge MCP server** that connects your coding agent to Next.js development tools:
+This package is a **thin connector** between your coding agent and the tooling around your Next.js project:
```
Coding Agent
@@ -674,17 +451,10 @@ Coding Agent
next-devtools-mcp (this package)
↓
├─→ Next.js Dev Server MCP Endpoint (/_next/mcp) ← Runtime diagnostics
- ├─→ Playwright MCP Server ← Browser automation
- └─→ Knowledge Base & Tools ← Documentation, upgrades, setup automation
+ └─→ agent-browser CLI ← Browser automation (gateway)
```
-**Key Architecture Points:**
-
-1. **For Next.js 16+ projects**: This server automatically discovers and connects to your running Next.js dev server's built-in MCP endpoint at `http://localhost:PORT/_next/mcp`. This gives coding agents direct access to runtime errors, routes, logs, and application state.
-
-2. **For all Next.js projects**: Provides development automation tools (upgrades, Cache Components setup), documentation access, and browser testing capabilities that work independently of the runtime connection.
-
-3. **Simple workflow**: Call `nextjs_index` to see all servers and available tools, then call `nextjs_call` with the specific port and tool name you want to execute.
+It discovers running Next.js 16+ dev servers and proxies their built-in MCP endpoint at `http://localhost:PORT/_next/mcp`, giving agents direct access to runtime errors, routes, logs, and application state. The workflow: call `nextjs_index` to discover servers and their available tools, then `nextjs_call` with the port and tool name to execute one.
## License
diff --git a/package.json b/package.json
index b25dddb..a90ed1c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "next-devtools-mcp",
- "version": "0.3.10",
+ "version": "0.4.0",
"type": "module",
"description": "Next.js development tools MCP server with stdio transport",
"license": "MIT",
@@ -22,14 +22,12 @@
"mcpName": "io.github.vercel/next-devtools-mcp",
"scripts": {
"dev": "tsc --watch",
- "copy-resources": "node scripts/copy-resources.js",
- "build": "tsc && npm run copy-resources",
+ "build": "tsc",
"prepublishOnly": "pnpm run clean && pnpm run build",
"clean": "rm -rf dist",
"start": "node dist/index.js",
"test": "vitest run --exclude 'test/e2e/**'",
"test:e2e": "vitest run test/e2e",
- "eval": "vitest run test/e2e/upgrade.test.ts",
"typecheck": "tsc --noEmit"
},
"dependencies": {
diff --git a/scripts/copy-resources.js b/scripts/copy-resources.js
deleted file mode 100644
index 0ab8b16..0000000
--- a/scripts/copy-resources.js
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/env node
-/**
- * Copy resources script
- * Preserves directory structure from src/resources/ to dist/resources/
- * Copies .md files from src/prompts/ to dist/resources/prompts/
- *
- * Usage: node scripts/copy-resources.js
- */
-
-import fs from 'fs'
-import path from 'path'
-
-const SRC_RESOURCES_DIR = 'src/resources'
-const SRC_PROMPTS_DIR = 'src/prompts'
-const DEST_DIR = 'dist/resources'
-
-/**
- * Recursively copy .md files while preserving directory structure
- */
-function copyMarkdownFiles(srcDir, destDir, relativePath = '') {
- if (!fs.existsSync(srcDir)) {
- return []
- }
-
- const files = fs.readdirSync(srcDir)
- const copiedFiles = []
-
- files.forEach(file => {
- const srcPath = path.join(srcDir, file)
- const stat = fs.statSync(srcPath)
-
- if (stat.isDirectory()) {
- // Recursively copy subdirectories
- const newRelativePath = path.join(relativePath, file)
- const copied = copyMarkdownFiles(srcPath, destDir, newRelativePath)
- copiedFiles.push(...copied)
- } else if (file.endsWith('.md')) {
- // Copy .md file preserving structure
- const destPath = path.join(destDir, relativePath, file)
- const destDirPath = path.dirname(destPath)
-
- if (!fs.existsSync(destDirPath)) {
- fs.mkdirSync(destDirPath, { recursive: true })
- }
-
- fs.copyFileSync(srcPath, destPath)
- const relativeDestPath = path.relative(destDir, destPath)
- copiedFiles.push(relativeDestPath)
- }
- })
-
- return copiedFiles
-}
-
-/**
- * Main function
- */
-function main() {
- console.log('📦 Copying markdown files with preserved structure...\n')
-
- // Ensure destination directory exists
- if (!fs.existsSync(DEST_DIR)) {
- fs.mkdirSync(DEST_DIR, { recursive: true })
- }
-
- // Copy resources with preserved structure
- console.log('Copying from src/resources/...')
- const resourceFiles = copyMarkdownFiles(SRC_RESOURCES_DIR, DEST_DIR)
-
- // Copy prompt .md files to prompts/ subdirectory
- console.log('Copying from src/prompts/...')
- const promptFiles = copyMarkdownFiles(SRC_PROMPTS_DIR, DEST_DIR, 'prompts')
-
- const allFiles = [...resourceFiles, ...promptFiles]
-
- console.log(`\nCopied ${allFiles.length} files:\n`)
- allFiles.forEach(file => {
- console.log(` ✓ ${file}`)
- })
-
- console.log('\n✅ Resources copied successfully!')
-}
-
-main()
diff --git a/server.json b/server.json
index 2f59c1c..9750b3d 100644
--- a/server.json
+++ b/server.json
@@ -6,12 +6,12 @@
"url": "https://github.com/vercel/next-devtools-mcp",
"source": "github"
},
- "version": "0.3.10",
+ "version": "0.4.0",
"packages": [
{
"registryType": "npm",
"identifier": "next-devtools-mcp",
- "version": "0.3.10",
+ "version": "0.4.0",
"transport": {
"type": "stdio"
},
diff --git a/src/_internal/browser-eval-manager.ts b/src/_internal/browser-eval-manager.ts
deleted file mode 100644
index b00417b..0000000
--- a/src/_internal/browser-eval-manager.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import { exec } from "child_process"
-import { promisify } from "util"
-import { connectToMCPServer, MCPConnection } from "./mcp-client.js"
-
-const execAsync = promisify(exec)
-
-let browserEvalConnection: MCPConnection | null = null
-
-/**
- * Check if playwright-mcp is installed
- */
-async function isPlaywrightMCPInstalled(): Promise {
- try {
- const { stdout } = await execAsync("npm list -g @playwright/mcp --depth=0")
- return stdout.includes("@playwright/mcp")
- } catch (error) {
- // npm list returns error if package not found
- return false
- }
-}
-
-/**
- * Install playwright-mcp globally
- */
-async function installPlaywrightMCP(): Promise {
- console.error("[Browser Eval Manager] Installing @playwright/mcp globally...")
- try {
- await execAsync("npm install -g @playwright/mcp@latest")
- console.error("[Browser Eval Manager] Successfully installed @playwright/mcp")
- } catch (error) {
- throw new Error(`Failed to install @playwright/mcp: ${error}`)
- }
-}
-
-/**
- * Ensure playwright-mcp is installed and install if needed
- */
-export async function ensureBrowserEvalMCP(): Promise {
- const installed = await isPlaywrightMCPInstalled()
- if (!installed) {
- await installPlaywrightMCP()
- } else {
- console.error("[Browser Eval Manager] @playwright/mcp is already installed")
- }
-}
-
-/**
- * Start playwright-mcp server and connect to it
- */
-export async function startBrowserEvalMCP(options?: {
- browser?: "chrome" | "chromium" | "firefox" | "webkit" | "msedge"
- headless?: boolean
-}): Promise {
- // Ensure playwright-mcp is installed
- await ensureBrowserEvalMCP()
-
- // If already connected, return existing connection
- if (browserEvalConnection) {
- console.error("[Browser Eval Manager] Using existing connection")
- return browserEvalConnection
- }
-
- console.error("[Browser Eval Manager] Starting playwright-mcp server with verbose logging...")
-
- // Build args for playwright-mcp
- const args: string[] = ["@playwright/mcp@latest", "--image-responses", "omit"]
-
- if (options?.browser) {
- args.push("--browser", options.browser)
- }
-
- // --headless is a flag (no value needed)
- // Pass the flag only if headless is true
- if (options?.headless === true) {
- args.push("--headless")
- }
-
- // Always enable verbose logging via environment variables
- const env = {
- ...process.env,
- DEBUG: "pw:api,pw:browser*",
- VERBOSE: "1",
- }
-
- // Connect to playwright-mcp using npx
- const connection = await connectToMCPServer("npx", args, { env })
-
- browserEvalConnection = connection
- console.error("[Browser Eval Manager] Successfully connected to playwright-mcp (verbose mode enabled)")
- console.error("[Browser Eval Manager] Browser automation logs will be shown below:")
-
- return connection
-}
-
-/**
- * Get the current browser eval connection
- */
-export function getBrowserEvalConnection(): MCPConnection | null {
- return browserEvalConnection
-}
-
-/**
- * Stop playwright-mcp server and cleanup
- */
-export async function stopBrowserEvalMCP(): Promise {
- if (!browserEvalConnection) {
- return
- }
-
- console.error("[Browser Eval Manager] Stopping playwright-mcp server...")
-
- try {
- await browserEvalConnection.transport.close()
- await browserEvalConnection.client.close()
- browserEvalConnection = null
- console.error("[Browser Eval Manager] Successfully stopped playwright-mcp")
- } catch (error) {
- console.error("[Browser Eval Manager] Error stopping playwright-mcp:", error)
- browserEvalConnection = null
- throw error
- }
-}
-
-/**
- * Cleanup on process exit
- */
-process.on("SIGINT", async () => {
- await stopBrowserEvalMCP()
- process.exit(0)
-})
-
-process.on("SIGTERM", async () => {
- await stopBrowserEvalMCP()
- process.exit(0)
-})
-
diff --git a/src/_internal/global-state.ts b/src/_internal/global-state.ts
deleted file mode 100644
index 63773e5..0000000
--- a/src/_internal/global-state.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Global state for the MCP server
- * Tracks initialization status and other server-wide state
- */
-
-interface GlobalState {
- initCalled: boolean
- initTimestamp: number | null
-}
-
-const globalState: GlobalState = {
- initCalled: false,
- initTimestamp: null,
-}
-
-export function markInitCalled(): void {
- globalState.initCalled = true
- globalState.initTimestamp = Date.now()
-}
-
-export function isInitCalled(): boolean {
- return globalState.initCalled
-}
-
-export function getInitTimestamp(): number | null {
- return globalState.initTimestamp
-}
-
-export function resetGlobalState(): void {
- globalState.initCalled = false
- globalState.initTimestamp = null
-}
diff --git a/src/_internal/mcp-client.ts b/src/_internal/mcp-client.ts
deleted file mode 100644
index 40afb18..0000000
--- a/src/_internal/mcp-client.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { Client } from "@modelcontextprotocol/sdk/client/index.js"
-import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
-
-export interface MCPConnection {
- client: Client
- transport: StdioClientTransport
-}
-
-/**
- * Connect to an external MCP server via stdio
- */
-export async function connectToMCPServer(
- command: string,
- args: string[] = [],
- options?: {
- cwd?: string
- env?: Record
- }
-): Promise {
- // Create the client
- const client = new Client(
- {
- name: "next-devtools-mcp-client",
- version: "0.1.0",
- },
- {
- capabilities: {},
- }
- )
-
- // Create stdio transport with server parameters
- const transport = new StdioClientTransport({
- command,
- args,
- cwd: options?.cwd,
- env: options?.env,
- stderr: "pipe", // Pipe stderr so we can listen to it
- })
-
- // Listen to stderr for debugging
- const stderrStream = transport.stderr
- if (stderrStream) {
- stderrStream.on("data", (data) => {
- console.error(`[MCP Server stderr]: ${data}`)
- })
- }
-
- // Connect client to transport (this also starts the server process)
- await client.connect(transport)
-
- return {
- client,
- transport,
- }
-}
-
-/**
- * Check if a tool is available on the connected MCP server
- */
-export async function listServerTools(connection: MCPConnection): Promise {
- try {
- const result = await connection.client.listTools()
- return result.tools.map((tool) => tool.name)
- } catch (error) {
- console.error("Failed to list tools:", error)
- return []
- }
-}
-
-/**
- * Call a tool on the connected MCP server
- */
-export async function callServerTool(
- connection: MCPConnection,
- toolName: string,
- args: Record
-): Promise {
- try {
- const result = await connection.client.callTool({
- name: toolName,
- arguments: args,
- })
- return result
- } catch (error) {
- console.error(`Failed to call tool ${toolName}:`, error)
- throw error
- }
-}
-
-/**
- * Disconnect from MCP server and cleanup
- */
-export async function disconnectFromMCPServer(connection: MCPConnection): Promise {
- try {
- await connection.transport.close()
- await connection.client.close()
- } catch (error) {
- console.error("Error disconnecting from MCP server:", error)
- throw error
- }
-}
diff --git a/src/_internal/nextjs-channel-detector.ts b/src/_internal/nextjs-channel-detector.ts
deleted file mode 100644
index c3cd56a..0000000
--- a/src/_internal/nextjs-channel-detector.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { readFileSync, existsSync } from "fs"
-import { join } from "path"
-
-export interface ChannelDetectionResult {
- isBeta: boolean
- isCanary: boolean
- currentVersion: string | null
-}
-
-/**
- * Detects the Next.js channel (beta/canary/stable) from a project's package.json
- * @param projectPath - Path to the Next.js project directory
- * @returns Channel detection result with isBeta, isCanary, and currentVersion
- */
-export function detectProjectChannel(projectPath: string): ChannelDetectionResult {
- const packageJsonPath = join(projectPath, "package.json")
-
- if (!existsSync(packageJsonPath)) {
- return { isBeta: false, isCanary: false, currentVersion: null }
- }
-
- try {
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"))
- const nextVersion = packageJson.dependencies?.next || packageJson.devDependencies?.next
-
- if (!nextVersion) {
- return { isBeta: false, isCanary: false, currentVersion: null }
- }
-
- const isBeta = nextVersion.includes("beta") || nextVersion.includes("16.0.0-beta")
- const isCanary = nextVersion === "canary" || nextVersion.includes("canary")
-
- return { isBeta, isCanary, currentVersion: nextVersion }
- } catch (error) {
- console.warn("Failed to parse package.json, assuming not on beta/canary")
- return { isBeta: false, isCanary: false, currentVersion: null }
- }
-}
-
-/**
- * Processes conditional template blocks based on channel detection
- * Supports {{IF_BETA_CHANNEL}} blocks
- * @param template - Template string with conditional blocks
- * @param isBeta - Whether the project is on beta channel
- * @returns Processed template with conditional blocks resolved
- */
-export function processConditionalBlocks(template: string, isBeta: boolean): string {
- let result = template
-
- // Process IF_BETA_CHANNEL blocks
- if (isBeta) {
- // Keep content, remove markers
- result = result.replace(/\{\{IF_BETA_CHANNEL\}\}/g, "")
- result = result.replace(/\{\{\/IF_BETA_CHANNEL\}\}/g, "")
- } else {
- // Remove entire block including content
- result = result.replace(/\{\{IF_BETA_CHANNEL\}\}.*?\{\{\/IF_BETA_CHANNEL\}\}/gs, "")
- }
-
- return result
-}
-
diff --git a/src/_internal/nextjs-runtime-manager.ts b/src/_internal/nextjs-runtime-manager.ts
index 35d7124..d35a122 100644
--- a/src/_internal/nextjs-runtime-manager.ts
+++ b/src/_internal/nextjs-runtime-manager.ts
@@ -328,7 +328,7 @@ async function makeNextJsMCPRequest(
if (response.status === 404) {
throw new Error(
`MCP endpoint not found. Next.js MCP support requires Next.js 16+. ` +
- `If you're on an older version, upgrade using the 'upgrade-nextjs-16' MCP prompt. ` +
+ `If you're on an older version, upgrade by running 'npx @next/codemod@latest upgrade latest'. ` +
`If you're already on Next.js 16+: MCP is enabled by default - make sure the dev server is running.`
)
}
@@ -356,7 +356,7 @@ async function makeNextJsMCPRequest(
`Cannot connect to Next.js dev server on port ${port}. ` +
`Make sure the dev server is running. ` +
`Next.js MCP support requires Next.js 16+ where MCP is enabled by default. ` +
- `If you're on Next.js 15 or earlier, upgrade using the 'upgrade-nextjs-16' MCP prompt.`
+ `If you're on Next.js 15 or earlier, upgrade by running 'npx @next/codemod@latest upgrade latest'.`
)
}
diff --git a/src/_internal/resource-loader.ts b/src/_internal/resource-loader.ts
deleted file mode 100644
index d421b41..0000000
--- a/src/_internal/resource-loader.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { readdirSync, existsSync } from "node:fs"
-import { resolveResourcePath, readResourceFile } from "./resource-path.js"
-
-export function loadKnowledgeResources(): Record {
- const resources: Record = {}
- const resourcesDir = resolveResourcePath("")
-
- if (!existsSync(resourcesDir)) {
- console.warn(`Resources directory not found: ${resourcesDir}`)
- return resources
- }
-
- const files = readdirSync(resourcesDir)
- .filter((file) => file.endsWith(".md") && /^\d+-/.test(file))
- .sort()
-
- for (const file of files) {
- const content = readResourceFile(file)
- const key = file.replace(/^\d+-/, "").replace(".md", "")
- resources[key] = content
- }
-
- return resources
-}
-
-export function loadNumberedMarkdownFilesWithNames(): Array<{ filename: string; content: string }> {
- const results: Array<{ filename: string; content: string }> = []
- const resourcesDir = resolveResourcePath("")
-
- if (!existsSync(resourcesDir)) {
- console.warn(`Resources directory not found: ${resourcesDir}`)
- return results
- }
-
- const files = readdirSync(resourcesDir)
- .filter((file) => file.endsWith(".md") && /^\d+-/.test(file))
- .sort()
-
- for (const file of files) {
- const content = readResourceFile(file)
- results.push({ filename: file, content })
- }
-
- return results
-}
diff --git a/src/_internal/resource-path.ts b/src/_internal/resource-path.ts
deleted file mode 100644
index 6322904..0000000
--- a/src/_internal/resource-path.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { join, dirname } from "node:path"
-import { readFileSync, existsSync } from "node:fs"
-import { fileURLToPath } from "node:url"
-
-const DIST_RESOURCES_DIR = "resources"
-
-function findProjectRoot(startDir: string): string {
- let current = startDir
-
- while (current !== dirname(current)) {
- const distPath = join(current, "dist")
- const packageJsonPath = join(current, "package.json")
-
- if (existsSync(distPath) || existsSync(packageJsonPath)) {
- return current
- }
-
- current = dirname(current)
- }
-
- return startDir
-}
-
-function getResourcesRoot(): string {
- const currentDir = dirname(fileURLToPath(import.meta.url))
-
- if (currentDir.includes("/dist/")) {
- const distIndex = currentDir.lastIndexOf("/dist/")
- const projectRoot = currentDir.substring(0, distIndex)
- return join(projectRoot, "dist", DIST_RESOURCES_DIR)
- }
-
- const projectRoot = findProjectRoot(currentDir)
- return join(projectRoot, "dist", DIST_RESOURCES_DIR)
-}
-
-export function resolveResourcePath(filename: string): string {
- const resourcesRoot = getResourcesRoot()
- return join(resourcesRoot, filename)
-}
-
-export function readResourceFile(filename: string): string {
- const filePath = resolveResourcePath(filename)
- return readFileSync(filePath, "utf-8")
-}
diff --git a/src/index.ts b/src/index.ts
index ed9ff03..75f93d0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,10 +4,6 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import {
CallToolRequestSchema,
ListToolsRequestSchema,
- ListPromptsRequestSchema,
- GetPromptRequestSchema,
- ListResourcesRequestSchema,
- ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js"
import { z } from "zod"
import { spawn } from "child_process"
@@ -22,67 +18,19 @@ const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
import * as browserEval from "./tools/browser-eval.js"
-import * as enableCacheComponents from "./tools/enable-cache-components.js"
-import * as init from "./tools/init.js"
import * as nextjsDocs from "./tools/nextjs-docs.js"
import * as nextjsIndex from "./tools/nextjs_index.js"
import * as nextjsCall from "./tools/nextjs_call.js"
-import * as upgradeNextjs16 from "./tools/upgrade-nextjs-16.js"
-import * as upgradeNextjs16Prompt from "./prompts/upgrade-nextjs-16.js"
-import * as enableCacheComponentsPrompt from "./prompts/enable-cache-components.js"
-import * as cacheComponentsOverview from "./resources/(cache-components)/overview.js"
-import * as cacheComponentsCoreMechanics from "./resources/(cache-components)/core-mechanics.js"
-import * as cacheComponentsPublicCaches from "./resources/(cache-components)/public-caches.js"
-import * as cacheComponentsPrivateCaches from "./resources/(cache-components)/private-caches.js"
-import * as cacheComponentsRuntimePrefetching from "./resources/(cache-components)/runtime-prefetching.js"
-import * as cacheComponentsRequestApis from "./resources/(cache-components)/request-apis.js"
-import * as cacheComponentsCacheInvalidation from "./resources/(cache-components)/cache-invalidation.js"
-import * as cacheComponentsAdvancedPatterns from "./resources/(cache-components)/advanced-patterns.js"
-import * as cacheComponentsBuildBehavior from "./resources/(cache-components)/build-behavior.js"
-import * as cacheComponentsErrorPatterns from "./resources/(cache-components)/error-patterns.js"
-import * as cacheComponentsTestPatterns from "./resources/(cache-components)/test-patterns.js"
-import * as cacheComponentsReference from "./resources/(cache-components)/reference.js"
-import * as cacheComponentsRouteHandlers from "./resources/(cache-components)/route-handlers.js"
-import * as nextjsFundamentalsUseClient from "./resources/(nextjs-fundamentals)/use-client.js"
-import * as nextjs16BetaToStable from "./resources/(nextjs16)/migration/beta-to-stable.js"
-import * as nextjs16Examples from "./resources/(nextjs16)/migration/examples.js"
-import * as nextjsDocsLlmsIndex from "./resources/(nextjs-docs)/llms-index.js"
-
-const tools = [browserEval, enableCacheComponents, init, nextjsDocs, nextjsIndex, nextjsCall, upgradeNextjs16]
+const tools = [browserEval, nextjsDocs, nextjsIndex, nextjsCall]
const toolNameToTelemetryName: Record = {
browser_eval: "mcp/browser_eval",
- enable_cache_components: "mcp/enable_cache_components",
- init: "mcp/init",
nextjs_docs: "mcp/nextjs_docs",
nextjs_index: "mcp/nextjs_index",
nextjs_call: "mcp/nextjs_call",
- upgrade_nextjs_16: "mcp/upgrade_nextjs_16",
}
-const prompts = [upgradeNextjs16Prompt, enableCacheComponentsPrompt]
-
-const resources = [
- cacheComponentsOverview,
- cacheComponentsCoreMechanics,
- cacheComponentsPublicCaches,
- cacheComponentsPrivateCaches,
- cacheComponentsRuntimePrefetching,
- cacheComponentsRequestApis,
- cacheComponentsCacheInvalidation,
- cacheComponentsAdvancedPatterns,
- cacheComponentsBuildBehavior,
- cacheComponentsErrorPatterns,
- cacheComponentsTestPatterns,
- cacheComponentsReference,
- cacheComponentsRouteHandlers,
- nextjsFundamentalsUseClient,
- nextjs16BetaToStable,
- nextjs16Examples,
- nextjsDocsLlmsIndex,
-]
-
// Type definitions
interface JSONSchema {
type?: string
@@ -101,8 +49,6 @@ const server = new Server(
{
capabilities: {
tools: {},
- prompts: {},
- resources: {},
},
}
)
@@ -159,79 +105,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
}
})
-// Register prompt handlers
-server.setRequestHandler(ListPromptsRequestSchema, async () => {
- return {
- prompts: prompts.map((prompt) => ({
- name: prompt.metadata.name,
- description: prompt.metadata.description,
- })),
- }
-})
-
-server.setRequestHandler(GetPromptRequestSchema, async (request) => {
- const { name, arguments: args } = request.params
-
- const prompt = prompts.find((p) => p.metadata.name === name)
- if (!prompt) {
- throw new Error(`Prompt not found: ${name}`)
- }
-
- // Validate arguments if schema exists
- let parsedArgs: Record = args || {}
- if (prompt.inputSchema) {
- parsedArgs = parseToolArgs(prompt.inputSchema, args || {})
- }
-
- // Get the prompt content
- const content = await prompt.handler(parsedArgs as never)
-
- return {
- messages: [
- {
- role: prompt.metadata.role || "user",
- content: {
- type: "text",
- text: content,
- },
- },
- ],
- }
-})
-
-// Register resource handlers
-server.setRequestHandler(ListResourcesRequestSchema, async () => {
- return {
- resources: resources.map((resource) => ({
- uri: resource.metadata.uri,
- name: resource.metadata.name,
- description: resource.metadata.description,
- mimeType: resource.metadata.mimeType || "text/markdown",
- })),
- }
-})
-
-server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
- const { uri } = request.params
-
- const resource = resources.find((r) => r.metadata.uri === uri)
- if (!resource) {
- throw new Error(`Resource not found: ${uri}`)
- }
-
- const content = await resource.handler()
-
- return {
- contents: [
- {
- uri,
- mimeType: resource.metadata.mimeType || "text/markdown",
- text: content,
- },
- ],
- }
-})
-
function zodSchemaToJsonSchema(zodSchema: z.ZodTypeAny): JSONSchema {
const description = zodSchema._def?.description
diff --git a/src/prompts/enable-cache-components-prompt.md b/src/prompts/enable-cache-components-prompt.md
deleted file mode 100644
index 2607916..0000000
--- a/src/prompts/enable-cache-components-prompt.md
+++ /dev/null
@@ -1,1430 +0,0 @@
-You are a Next.js Cache Components setup assistant. Help enable and verify Cache Components in this Next.js 16 project.
-
-PROJECT: {{PROJECT_PATH}}
-
-# BASE KNOWLEDGE: Cache Components Technical Reference
-
-**✅ RESOURCES AVAILABLE ON-DEMAND - Load only what you need**
-
-**Available Resources (Load as Needed):**
-
-The following resources are available from the Next.js MCP server. Load them on-demand to reduce token usage:
-
-- `cache-components://overview` - Critical errors AI agents make, quick reference (START HERE)
-- `cache-components://core-mechanics` - Fundamental paradigm shift, cacheComponents
-- `cache-components://public-caches` - Public cache mechanics using 'use cache'
-- `cache-components://private-caches` - Private cache mechanics using 'use cache: private'
-- `cache-components://runtime-prefetching` - Prefetch configuration and stale time rules
-- `cache-components://request-apis` - Async params, searchParams, cookies(), headers()
-- `cache-components://cache-invalidation` - updateTag(), revalidateTag() patterns
-- `cache-components://advanced-patterns` - cacheLife(), cacheTag(), draft mode
-- `cache-components://build-behavior` - What gets prerendered, static shells
-- `cache-components://error-patterns` - Common errors and solutions
-- `cache-components://test-patterns` - Real test-driven patterns from 125+ fixtures
-- `cache-components://route-handlers` - Using 'use cache' in Route Handlers (API Routes)
-- `cache-components://reference` - Mental models, API reference, checklists
-
-**How to Access Resources (MANDATORY - ALWAYS LOAD):**
-
-Resources use the URI scheme `cache-components://...` and are served by this MCP server.
-
-**CRITICAL: You MUST load resources at each phase phase - this is not optional.**
-
-To load a resource, use the ReadMcpResourceTool with:
-- server: `"next-devtools"` (or whatever your server is configured as)
-- uri: `"cache-components://[resource-name]"` from the list above
-
-**MANDATORY Resource Loading Schedule:**
-
-You MUST load these resources at the specified phases:
-
-- **BEFORE Phase 1-2:** ALWAYS load `cache-components://overview` first
- - Provides critical context and error patterns AI agents make
- - Must be loaded before any configuration changes
-
-- **During Phase 5 (Error Fixing):** ALWAYS load error-specific resources as needed
- - When fixing blocking route errors → Load `cache-components://error-patterns`
- - When configuring caching → Load `cache-components://advanced-patterns`
- - When using dynamic params → Load `cache-components://core-mechanics`
- - Do NOT guess or use generic patterns - load the specific resource
-
-- **During Phase 6 (Verification):** ALWAYS load `cache-components://build-behavior`
- - Provides build verification strategies and troubleshooting
-
-**Why This Matters:**
-
-- Resources contain proven solutions from 125+ test fixtures
-- Generic patterns may not work with Cache Components specifics
-- Loading ensures you follow exact API semantics and error patterns
-- Token savings only work if resources are loaded when needed
-- Without loading, you may apply incorrect fixes
-
-**Token Efficiency:**
-
-This mandatory loading strategy keeps tokens low while being complete:
-- ✅ Loads ~5-15K tokens per phase (not 60K upfront)
-- ✅ Each resource addresses specific problem sets
-- ✅ No guessing or hallucination about patterns
-- ✅ Supports multiple phases in one session
-- ✅ Stays within conversation budget
-
----
-
-# ENABLE WORKFLOW: Complete Cache Components Setup & Verification Guide
-
-The section below contains the comprehensive step-by-step enablement workflow. This guide includes ALL steps needed to enable Cache Components: configuration updates, flag changes, boundary setup, error detection, and automated fixing. Load the knowledge base resources above for detailed technical behavior, API semantics, and best practices.
-
-## What Are Cache Components?
-
-Cache Components are a new set of features designed to make caching in Next.js both **more explicit and more flexible**. They fundamentally change how Next.js handles rendering:
-
-**The Paradigm Shift:**
-- **Before (implicit caching):** Routes were static by default, you opted into dynamic rendering
-- **After (Cache Components):** Routes are dynamic by default, you opt into caching with `"use cache"`
-- **Goal:** Better align with developer expectations while preserving static pre-rendering capabilities
-
-**What Cache Components Achieve:**
-
-1. **Explicit Opt-In Caching:**
- - All dynamic code executes at request time by default
- - Use `"use cache"` directive to cache pages, components, or functions
- - Compiler automatically generates cache keys
-
-2. **Complete PPR (Partial Prerendering) Story:**
- - Instead of using Suspense to opt-in to dynamic (old PPR)
- - Now use `"use cache"` to opt-in to static (new paradigm)
- - Mix cached and dynamic content in the same route
-
-3. **Flexible Caching Levels:**
- - `"use cache"` - Public cache for build-time prerendering
- - `"use cache: private"` - Private cache for runtime prefetching (can access cookies/params)
- - `"use cache: remote"` - Persistent cache for serverless environments
-
-4. **Runtime Prefetching:**
- - Prefetch routes with actual runtime values (cookies, params, searchParams)
- - Instant client navigations without loading states
- - Cache snapshots of components in static shells
-
-**The Core Concept: Push Down Dynamic Boundaries**
-
-The key strategy with Cache Components is to **push dynamic boundaries as far down the component tree as possible**, making as much of your UI static as you can:
-
-```
-Static Shell (instant load)
-├─ Cached Header ("use cache")
-├─ Cached Sidebar ("use cache")
-└─
- └─ Dynamic Content (per-request)
-```
-
-This gives you:
-- ✅ Fast initial page load (static shell)
-- ✅ Reduced server load (cached components)
-- ✅ Fresh data where needed (dynamic content)
-
-## Overview: What This Process Covers
-
-This prompt automates the complete Cache Components enablement workflow:
-
-**Configuration & Flags (Phase 1-2):**
-- ✅ Detect package manager (npm/pnpm/yarn/bun)
-- ✅ Verify Next.js version (16.0.0 stable or canary only - beta NOT supported)
-- ✅ Enable cacheComponents (experimental in 16.0.0, stable in canary)
-- ✅ Migrate from `experimental.dynamicIO`, `experimental.ppr`, or `useCache` if needed (remove old flags)
-- ✅ Document existing Route Segment Config for migration
-
-**Dev Server & MCP Setup (Phase 3):**
-- ✅ Start dev server (MCP is enabled by default in Next.js 16+)
-- ✅ Verify MCP server is active and responding
-- ✅ Capture base URL and MCP endpoint for error detection
-
-**Error Detection (Phase 4 - Optional):**
-- ✅ Start browser and load every route using browser_eval tool
-- ✅ Collect errors from browser session using Next.js MCP `get_errors` tool
-- ✅ Categorize all Cache Components errors by type
-- ✅ Build comprehensive error list before fixing
-- ℹ️ Phase 4 can be skipped if proceeding directly to Phase 5 build-first approach
-
-**Automated Fixing (Phase 5 - Build-First Strategy):**
-- ✅ Run ` run build` to identify all failing routes at once
-- ✅ Get explicit error messages for every issue in build output
-- ✅ Fix errors directly based on clear error messages from build
-- ✅ Or verify in dev server with `next dev` for interactive fixing with Fast Refresh
-- ✅ Fix blocking route errors (add Suspense boundaries or "use cache")
-- ✅ Fix dynamic value errors (add `await connection()`)
-- ✅ Fix route params errors (add `generateStaticParams`)
-- ✅ Fix unavailable API errors (move outside cache or use "use cache: private")
-- ✅ Migrate Route Segment Config to "use cache" + cacheLife
-- ✅ Add cache tags with cacheTag() for on-demand revalidation
-- ✅ Configure cacheLife profiles for revalidation control
-- ✅ Verify each fix with Fast Refresh (no restart needed)
-
-**Final Verification (Phase 6):**
-- ✅ Verify all routes return 200 OK
-- ✅ Confirm zero errors with final `get_errors` check
-- ✅ Stop dev server after verification
-- ✅ Run production build and test
-
-**Key Features:**
-- One-time dev server start (no restarts needed)
-- Automated error detection using Next.js MCP tools
-- Browser-based testing with browser automation
-- Fast Refresh applies fixes instantly
-- Comprehensive fix strategies for all error types
-
-## Decision Guide: Static vs Dynamic - A Question-Driven Approach
-
-**📖 For complete decision-making guidance with detailed examples, load:**
-```
-Read resource "nextjs16://migration/examples"
-```
-
-Then navigate to **"Cache Components Examples"** → **"Decision Guide: Static vs Dynamic"** for:
-- Complete 4-question framework
-- Decision approaches with full code examples (A, B, C, D)
-- Decision summary table
-- When to ask human for ambiguous cases
-
-**Quick Reference - 4 Key Questions:**
-
-1. **Is this content the same for all users?**
- - YES → `"use cache"` | NO → Suspense or `"use cache: private"`
-
-2. **How often does this content change?**
- - Rarely (days/weeks) → `"use cache"` + long `cacheLife`
- - Occasionally (hours) → `"use cache"` + medium `cacheLife`
- - Frequently (minutes) → `"use cache"` + short `cacheLife`
- - Constantly (per-request) → ``
-
-3. **Does this content use user-specific data?**
- - YES, from cookies/session → Suspense OR `"use cache: private"`
- - YES, from route params → `"use cache"` + `generateStaticParams`
- - NO → `"use cache"`
-
-4. **Can this content be revalidated on-demand?**
- - YES (CMS updates, admin actions) → `"use cache"` + `cacheTag()`
- - NO (no clear trigger) → time-based `cacheLife` or Suspense
-
-**Load the MCP resource for complete decision approaches and code examples.**
-
-## PHASE 1: Pre-Flight Checks
-────────────────────────────────────────
-
-**⚠️ MANDATORY FIRST STEP: Load the overview resource**
-
-BEFORE doing anything, you MUST load:
-```
-ReadMcpResourceTool(server="next-devtools", uri="cache-components://overview")
-```
-
-This provides critical context about Cache Components and common mistakes.
-
-Before enabling Cache Components:
-
-1. **Detect Package Manager**
- Check: package.json "packageManager" field or lock files
-
- **Template Variables:**
- ```
- npm: = npm = npx
- pnpm: = pnpm = pnpx
- yarn: = yarn = yarn dlx
- bun: = bun = bunx
- ```
-
-2. **Next.js Version Check**
- Required: 16.0.0 stable or 16.x-canary.x (beta NOT supported)
- Check: package.json → dependencies.next
- Action: If < 16.0.0, run upgrade-nextjs-16 prompt first
-
-3. **Existing Configuration Check**
- **Find the config file first:**
- Check for these files in order (use the first one found):
- - `next.config.ts`
- - `next.config.mjs`
- - `next.config.js`
- - `next.config.cjs`
-
- If no config file exists, you'll create `next.config.js` in Phase 2.
-
- **Read the config file and look for:**
- - `cacheComponents` or `experimental.cacheComponents` (current)
- - `experimental.dynamicIO` (old name - migrate to cacheComponents)
- - `experimental.ppr` (removed - migrate to cacheComponents)
- - `useCache` or `experimental.useCache` (old name - migrate to cacheComponents and REMOVE)
-
-4. **Route Structure Analysis**
- Scan: app directory structure
- Identify: All routes (page.tsx/page.js files)
-
- ```bash
- # Count total routes
- find app -name "page.tsx" -o -name "page.js" | wc -l
- ```
-
- **Recommended:** Use build-first verification (Phase 5) for all projects - it's always reliable and doesn't require additional tools.
-
- Note: List all routes for reference
-
-5. **Existing Route Segment Config Check**
- Search for all Route Segment Config exports using:
- - Pattern: `"export const (dynamic|revalidate|fetchCache|runtime|preferredRegion|dynamicParams)"`
- - Path: `"app"`
-
- ⚠️ WARNING: Route Segment Config options are DISABLED with Cache Components
- Action: Document all locations - will migrate to `"use cache"` + `cacheLife` in Phase 5
-
-6. **unstable_noStore Usage Check**
- Search for all `unstable_noStore()` calls:
- - Pattern: `"unstable_noStore"`
- - Path: `"app"`
-
- ⚠️ WARNING: `unstable_noStore()` is INCOMPATIBLE with Cache Components
-
- **Why:** With Cache Components, everything is dynamic by default. `unstable_noStore()` was used to opt-out of static rendering in the old model, but this is now the default behavior.
-
- **📖 For detailed migration examples, load:**
- ```
- Read resource "nextjs16://migration/examples" (see unstable_noStore Examples section)
- ```
-
- Action: Document all locations - will remove in Phase 5
-
-## PHASE 2: Enable Cache Components Configuration
-────────────────────────────────────────
-Update the Next.js configuration to enable Cache Components. This phase handles ALL configuration and flag changes needed.
-
-**Step 1: Identify config file format**
-From Phase 1, you should know which config file exists:
-- `next.config.ts` (TypeScript)
-- `next.config.mjs` (ESM)
-- `next.config.js` (CommonJS)
-- `next.config.cjs` (CommonJS explicit)
-- Or no config file (will create `next.config.js`)
-
-Use the same format/extension when making changes.
-
-**Step 2: Backup existing config**
-If config file exists, copy it before making changes.
-If no config exists, you'll create a new one in the next step.
-
-**Step 3: Update cacheComponents flag**
-
-Enable the `cacheComponents` flag in your Next.js config. The flag location differs by version:
-
-**Version-Aware Configuration:**
-
-Check your Next.js version: `grep '"next":' package.json`
-
-- **16.0.0 stable**: `experimental.cacheComponents = true`
-- **Canary (16.x-canary.x)**: `cacheComponents = true` (no longer experimental)
-
-**Starting Fresh:**
-```typescript
-// next.config.ts (or .js)
-// For 16.0.0: Put cacheComponents inside experimental: {}
-// For canary: Put cacheComponents at root level
-const nextConfig = {
- cacheComponents: true, // canary only
- experimental: {
- cacheComponents: true, // 16.0.0 only - use ONE of these
- },
-}
-```
-
-**Migrating from experimental.dynamicIO, experimental.ppr, or useCache:**
-```diff
- const nextConfig = {
- experimental: {
-- dynamicIO: true, // REMOVE - replaced by cacheComponents
-- ppr: true, // REMOVE - replaced by cacheComponents
-- useCache: true, // REMOVE - replaced by cacheComponents
-+ cacheComponents: true, // 16.0.0 - for canary, move to root level
- },
- }
-```
-
-⚠️ **CRITICAL**: When migrating to `cacheComponents`, you MUST remove ALL old flags (`ppr`, `dynamicIO`, `useCache`). Do not leave any of these flags present alongside `cacheComponents`. They are all replaced by `cacheComponents` with enhanced features (cacheLife, cacheTag, "use cache: private").
-
-**Step 3: Remove incompatible flags**
-
-If present, REMOVE these flags (they conflict with Cache Components):
-```diff
- const nextConfig = {
- experimental: {
- cacheComponents: true,
-- ppr: true, // Remove - replaced by cacheComponents
-- useCache: true, // Remove - replaced by cacheComponents
-- dynamicIO: true, // Remove - replaced by cacheComponents
- },
- }
-```
-
-⚠️ **CRITICAL**: When migrating to `cacheComponents`, ensure ALL old flags (`ppr`, `dynamicIO`, `useCache`) are completely removed. Do not leave any of these flags present alongside `cacheComponents`.
-
-**Step 4: Preserve compatible flags**
-
-These flags CAN coexist with cacheComponents:
-- `turbo`, `serverActions`, `mdxRs` - All compatible
-
-Example:
-```typescript
-const nextConfig = {
- cacheComponents: true, // canary - or inside experimental: {} for 16.0.0
- experimental: {
- turbo: { rules: {} },
- serverActions: { bodySizeLimit: '2mb' },
- },
-}
-```
-
-**Step 5: Document Route Segment Config usage**
-
-Search for Route Segment Config exports (these are DISABLED with Cache Components):
-
-**Search Pattern:**
-- Use search with pattern: `"export const (dynamic|revalidate|fetchCache|runtime|preferredRegion|dynamicParams)"`
-- In path: `"app"`
-- This will find ALL Route Segment Config exports that need migration
-
-⚠️ **CRITICAL: All Route Segment Config options are DISABLED with Cache Components**
-
-**Migration Map:**
-- `export const dynamic = 'force-static'` → Add `"use cache"` + cacheLife
-- `export const dynamic = 'force-dynamic'` → Add `` boundary (or nothing - dynamic is default)
-- `export const revalidate = X` → Use matching `cacheLife()` profile (see table below)
-- `export const fetchCache = 'force-cache'` → Add `"use cache"`
-- `export const runtime = 'edge'` → Keep (still supported)
-- `export const runtime = 'nodejs'` → Remove (this is the default, no need to specify)
-- `export const dynamicParams = true` → Use `generateStaticParams` instead
-
-**Revalidate → cacheLife Mapping:**
-| revalidate value | cacheLife equivalent |
-|------------------|---------------------|
-| `0` or `false` | Dynamic (no "use cache" needed) |
-| `60` | `cacheLife('minutes')` |
-| `3600` | `cacheLife('hours')` |
-| `86400` | `cacheLife('days')` |
-| `604800` | `cacheLife('weeks')` |
-| Other values | `cacheLife({ revalidate: X })` |
-
-**When removing exports, add migration comments with the original value:**
-```typescript
-// MIGRATED from: export const revalidate = 60
-// → Add "use cache" + cacheLife('minutes') to maintain ~60s revalidation
-```
-
-Document all locations now - you'll migrate them in Phase 3.
-
-**Step 6: Verify configuration changes**
-
-Verify by reading the config file:
-- ✅ cacheComponents enabled (location depends on version)
-- ✅ Incompatible flags removed (ppr, dynamicIO, useCache)
-- ✅ Compatible flags preserved
-- ✅ Valid syntax, correct file format
-
-**What's Next:**
-- **Recommended:** Proceed to Phase 3 (build-first approach)
- - Phase 3 removes breaking changes, then runs build to see all errors
- - Fix all errors from build output
- - Then proceed to Phase 4 for final verification
-
-## PHASE 3: Build-First Error Fixing & Boundary Setup (RECOMMENDED)
-────────────────────────────────────────
-
-**This is the recommended workflow for ALL projects.**
-
-Build verification is always reliable and doesn't require dev server or browser tools upfront.
-
-**Prerequisites:**
-- ✅ Configuration enabled in Phase 2
-- ✅ Fast Refresh will apply changes automatically (no restart needed for fixes)
-
-**⚠️ MANDATORY: Load error-specific resources BEFORE making any changes**
-
-You MUST load these resources to understand errors and fix them correctly:
-```
-ReadMcpResourceTool(server="next-devtools", uri="cache-components://error-patterns")
-ReadMcpResourceTool(server="next-devtools", uri="cache-components://advanced-patterns")
-```
-
-Do NOT guess or apply generic patterns. Use the exact code examples and strategies from these resources.
-
-**OPTIMIZED STRATEGY: Fix Obvious Breaking Changes First, Then Build**
-
-This phase uses a three-step workflow to minimize iteration cycles:
-
-### Step 1: Remove Obvious Breaking Changes (Before First Build)
-
-Make these changes FIRST, before running any build or dev server:
-
-**A. Remove All Route Segment Config Exports**
-```bash
-# Find all Route Segment Config exports
-grep -r "export const dynamic\|export const revalidate\|export const fetchCache" app/
-```
-
-For each file found, remove these exports and add migration comments with suggested cacheLife:
-
-**For `export const revalidate = X`:**
-
-Use this mapping to suggest the appropriate cacheLife based on the original value:
-
-| Original revalidate | Suggested cacheLife | Notes |
-|---------------------|---------------------|-------|
-| `revalidate = 0` (or `false`) | Dynamic (no cache) | Was already dynamic, no "use cache" needed |
-| `revalidate = 1-59` | `cacheLife('seconds')` or custom | Very short cache, consider if caching helps |
-| `revalidate = 60` | `cacheLife('minutes')` | revalidate: 60s |
-| `revalidate = 61-3599` | `cacheLife({ revalidate: X })` | Custom value needed |
-| `revalidate = 3600` | `cacheLife('hours')` | revalidate: 3600s (1 hour) |
-| `revalidate = 3601-86399` | `cacheLife({ revalidate: X })` | Custom value needed |
-| `revalidate = 86400` | `cacheLife('days')` | revalidate: 86400s (1 day) |
-| `revalidate = 604800` | `cacheLife('weeks')` | revalidate: 604800s (1 week) |
-
-**Migration comment format - include the original value and suggestion:**
-```typescript
-// MIGRATED from: export const revalidate = 60
-// → Add "use cache" + cacheLife('minutes') to maintain ~60s revalidation
-import { cacheLife } from 'next/cache'
-
-export default async function Page() {
- "use cache"
- cacheLife('minutes') // Replaces: export const revalidate = 60
- // ...
-}
-```
-
-**For custom revalidate values (not matching a preset):**
-```typescript
-// MIGRATED from: export const revalidate = 1800 (30 minutes)
-// → Add "use cache" + cacheLife({ revalidate: 1800 }) to maintain existing behavior
-import { cacheLife } from 'next/cache'
-
-export default async function Page() {
- "use cache"
- cacheLife({ revalidate: 1800 }) // Replaces: export const revalidate = 1800
- // ...
-}
-```
-
-**For `export const dynamic`:**
-```typescript
-// MIGRATED from: export const dynamic = 'force-static'
-// → Add "use cache" to opt into caching (dynamic is now the default)
-```
-
-```typescript
-// MIGRATED from: export const dynamic = 'force-dynamic'
-// → No change needed (dynamic is now the default), or wrap in for loading states
-```
-
-**Keep these exports if found:**
-- `export const runtime = 'edge'` - Still supported
-- Remove `export const runtime = 'nodejs'` - Default, not needed
-
-**B. Remove All unstable_noStore() Calls**
-```bash
-# Find all unstable_noStore usage
-grep -r "unstable_noStore" app/ src/
-```
-
-For each file found, remove the calls and imports:
-```typescript
-// Remove: import { unstable_noStore } from 'next/cache'
-// Remove: unstable_noStore()
-
-// MIGRATED: Removed unstable_noStore() - dynamic by default with Cache Components
-// TODO: Will add "use cache" or Suspense boundary after analyzing build errors
-```
-
-**Why do this first?**
-- These changes are guaranteed to be needed
-- Removes noise from build output
-- Makes subsequent error messages clearer
-- Build will show what actually needs Suspense/"use cache" directives
-
-### Step 2: Run Build with Debug Prerender (Capture All Issues)
-
-After removing obvious breaking changes, run the build to see ALL errors:
-
-```bash
-# First attempt with debug-prerender flag (best output)
- run build -- --debug-prerender
-```
-
-If `--debug-prerender` is not supported:
-```bash
-# Fallback to standard build
- run build
-```
-
-**What to capture from build output:**
-- ✅ All failing routes listed
-- ✅ Explicit error messages for each route
-- ✅ Error types (blocking route, dynamic value, unavailable API, etc.)
-- ✅ Stack traces showing exact file and line numbers
-- ✅ Which routes succeeded vs failed
-
-**Build output will show errors like:**
-```
-Route "/dashboard": A component accessed data, headers, params, searchParams,
-or a short-lived cache without a Suspense boundary nor a "use cache" above it.
-
-Route "/blog/[slug]": Dynamic value detected during prerender
-
-Route "/api/users": Cannot use cookies() inside a cached function
-
-Route "/api/products": Cannot serialize Response object for caching
-```
-
-**Document all errors** - you'll fix them in Step 3.
-
-### Step 3: Fix Errors Based on Build Output
-
-Now fix errors iteratively, using the error messages from Step 2.
-
-**Sub-step A: Fix All Obvious Errors**
-
-Review the build output from Step 2 and fix all errors that have clear solutions:
-
-- **"A component accessed data... without a Suspense boundary"** → Add `` or `"use cache"`
-- **"Dynamic value detected during prerender"** → Add `await connection()`
-- **"Cannot use cookies() inside a cached function"** → Move outside cache or use `"use cache: private"`
-- **"Route params need generateStaticParams"** → Add `generateStaticParams`
-- **"Cannot serialize Response object for caching"** → Extract data fetching to helper function, add `use cache` to helper (see Route Handlers special case)
-- Any other error with an obvious fix from the error message
-
-**Special Case: 3rd Party Package Errors**
-
-If you see errors originating from packages in `node_modules/`:
-
-**📖 For complete 3rd party package workaround examples, load:**
-```
-Read resource "nextjs16://migration/examples"
-```
-
-Then navigate to the **"Cache Components Examples"** → **"3rd Party Package Workarounds"** section for:
-- Workaround 1: Wrap in Suspense Boundary
-- Workaround 2: Dynamic Import
-- Workaround 3: Move to Separate Dynamic Component
-- Complete code examples for each approach
-
-**Quick Reference:**
-
-1. **Document the issue** with standardized comment format
-2. **Try workarounds** (in order of preference):
- - Wrap component using the package in Suspense boundary
- - Use dynamic import to load package only when needed
- - Move package usage to separate dynamic component
- - Check for Cache Components-compatible version
-3. **If no workaround works:**
- - Document with comment
- - List in final report
- - Consider filing issue with package maintainer
-
-Fix ALL obvious errors (including documented 3rd party issues) before proceeding to Sub-step B.
-
-**Sub-step B: Verify Obvious Fixes with Build**
-
-After fixing all obvious errors, re-run the build to verify:
-
-```bash
- run build -- --debug-prerender
-```
-
-**Expected outcomes:**
-- ✅ **All routes pass** → Success! Proceed to final verification
-- ⚠️ **Some routes still fail with clear errors** → Return to Sub-step A, fix those errors
-- ❌ **Some routes fail with unclear errors** → Proceed to Sub-step C
-
-**Sub-step C: Final Build Verification**
-
-After fixing all obvious errors, run the build one more time:
-
-```bash
- run build -- --debug-prerender
-```
-
-**Expected outcomes:**
-- ✅ **All routes pass (0 errors)** → Success! Proceed to Phase 4 - Option A (final verification)
-- ⚠️ **Some routes still fail with clear errors** → Return to Sub-step A, fix those errors
-- ❌ **Some routes fail with unclear errors** → Proceed to Phase 4 - Option B (browser investigation)
-
-**Workflow Summary:**
-```
-Step 1: Remove obvious breaking changes (exports, unstable_noStore)
- ↓
-Step 2: Build to capture all errors
- ↓
-Step 3A: Fix ALL obvious errors from build output (NO dev server)
- ↓
-Step 3B: Re-run build to verify fixes
- ↓
-Step 3C: Final build verification
- ↓
- ├─ All pass (0 errors)? → Success! Go to Phase 4 - Option A
- ├─ Clear errors remain? → Back to Step 3A
- └─ Unclear errors remain? → Go to Phase 4 - Option B
-```
-
-**Key Point:** Phase 3 uses only build verification. Dev server is NOT started in Phase 3.
-
-**What This Phase Accomplishes:**
-
-This phase (Phase 3) handles ALL code changes needed for Cache Components:
-- ✅ Remove Route Segment Config exports (Step 1)
-- ✅ Remove unstable_noStore() calls (Step 1)
-- ✅ Add Suspense boundaries for dynamic content (Step 3)
-- ✅ Add "use cache" directives for cacheable content (Step 3)
-- ✅ Fix dynamic value errors with connection() (Step 3)
-- ✅ Add generateStaticParams for route params (Step 3)
-- ✅ Set up cache tags with cacheTag() for revalidation (Step 3)
-- ✅ Configure cacheLife profiles for fine-grained control (Step 3)
-- ✅ Move unavailable APIs outside cache scope (Step 3)
-
-**Critical: Apply the Decision Guide for Every Fix**
-
-For each error in Step 3, before applying a fix:
-
-1. **Analyze:** Use the Decision Guide questions
- - Is content the same for all users?
- - How often does it change?
- - Does it use user-specific data?
- - Can it be revalidated on-demand?
-
-2. **Ask Human for Ambiguous Cases**
- - **ALWAYS ask the human** when uncertain about caching decisions
- - Edge cases: Infrequently changing content (yearly, monthly)
- - Business logic: Unknown update frequency
- - Tradeoffs: Performance vs freshness unclear
-
-3. **Decide:** Choose the appropriate approach
- - Cache it (static) with `"use cache"`
- - Make it dynamic with ``
- - Mix both (hybrid)
- - Use `"use cache: private"` for prefetchable user content
-
-4. **Document:** Always add comments explaining your decision
- - Include human input if applicable ("HUMAN INPUT: ...")
- - Why you chose to cache or not cache
- - Expected update frequency
- - How content will be revalidated
-
-5. **Implement:** Apply the fix with proper configuration
- - Add `cacheLife()` based on content change frequency
- - Add `cacheTag()` if there's a clear revalidation trigger
- - Add descriptive comments with human decisions noted
-
-Fix errors systematically based on error type. For code examples and detailed patterns, refer to the loaded knowledge resources above.
-
-**Fixing Common Error Types:**
-
-For detailed code examples and patterns for each error type, refer to the knowledge resources loaded above:
-- Error-Patterns resource: Common Cache Components errors and their solutions
-- Advanced-Patterns resource: cacheLife(), cacheTag(), and optimization strategies
-
-**Key Fix Types:**
-- A. Blocking route errors → Add Suspense boundary or "use cache"
-- B. Dynamic values → Use connection() or extract to separate component
-- C. Route params → Add generateStaticParams
-- D. Unavailable APIs in cache → Move outside cache scope or use "use cache: private"
-- E. Route Segment Config → Remove ALL exports and migrate to Cache Components patterns
- - `export const dynamic` → Remove, add `"use cache"` or `` + migration comment
- - `export const revalidate` → Remove, use `cacheLife()` with appropriate profile
- - `export const fetchCache` → Remove, use `"use cache"` if needed
- - `export const runtime = 'edge'` → Keep if needed (edge runtime is still supported)
- - `export const runtime = 'nodejs'` → Remove (nodejs is the default, no need to specify)
- - `export const preferredRegion` → Keep value but remove the export const
- - `export const dynamicParams` → Remove, use `generateStaticParams` instead
- - **Always add migration comments** to document what was removed
-- F. unstable_noStore Removal → Remove all `unstable_noStore()` calls
- - `unstable_noStore()` → Remove completely (dynamic is now the default)
- - No replacement needed - Cache Components makes everything dynamic by default
- - If you want to cache specific content, use `"use cache"` instead
- - **Add migration comment** explaining the removal
-- G. Caching strategies → Configure cacheLife() and cacheTag()
-- H. Route Handlers with `use cache` → Extract data fetching to helper function (cannot use `use cache` directly in handler body)
- - **CRITICAL:** `use cache` **MUST** be extracted to a helper function - Response objects cannot be serialized for caching
- - See "Special Case: Using `use cache` in Route Handlers" section for complete examples and patterns
-
-### Removing unstable_noStore() Usage
-
-**CRITICAL: unstable_noStore() is incompatible with Cache Components**
-
-The `unstable_noStore()` API was used in the old caching model to opt-out of static rendering. With Cache Components, this API is no longer needed because:
-
-1. **Everything is dynamic by default** - No need to opt-out of caching
-2. **Use "use cache" to opt-in** - The paradigm is reversed
-3. **unstable_noStore() causes errors** - Will break Cache Components behavior
-
-**📖 For complete migration patterns and code examples, load:**
-```
-Read resource "nextjs16://migration/examples"
-```
-
-Then navigate to the **"unstable_noStore Examples"** section for:
-- Basic removal (keep dynamic)
-- Migration with Suspense boundary
-- Migration to cached content
-- Complete before/after examples
-- Hybrid approach patterns
-
-**Quick Migration Steps:**
-
-1. **Search for usage:**
- ```bash
- grep -r "unstable_noStore" app/ src/
- ```
-
-2. **Remove the import and calls:**
- ```typescript
- // Remove: import { unstable_noStore } from 'next/cache';
- // Remove: unstable_noStore();
- ```
-
-3. **Add migration comment:**
- ```typescript
- // MIGRATED: Removed unstable_noStore() - dynamic by default with Cache Components
- ```
-
-4. **Choose migration path:**
- - **Keep dynamic (most common):** No changes needed - already dynamic by default
- - **Add Suspense:** Wrap in `` for better UX with loading states
- - **Cache instead:** Add `"use cache"` if content should actually be cached
-
-5. **Load the resource for detailed examples** specific to your use case
-
-### Importing and Commenting cacheLife() and cacheTag() - Let Users Decide
-
-**IMPORTANT: Always Include Imports with Decision Comments**
-
-**📖 For complete caching strategy examples and comment templates, load:**
-```
-Read resource "nextjs16://migration/examples"
-```
-
-Then navigate to the **"Cache Components Examples"** section for:
-- **cacheLife() and cacheTag() Comment Templates** - Full template pattern
-- **Caching Strategy Examples** - All 5 strategies (A, B, C, D, E) with complete code
-- **Hybrid Caching Patterns** - Mix cached and dynamic content
-- **Private Cache Examples** - Using "use cache: private"
-
-**Quick Reference - Comment Template Pattern:**
-
-When adding `"use cache"` to any component, include commented import templates:
-
-```
-// ⚠️ CACHING STRATEGY DECISION NEEDED:
-// Uncomment ONE of the following based on your needs:
-// Option A: Time-based revalidation - cacheLife('hours')
-// Option B: Tag-based revalidation - cacheTag('resource-name')
-// Option C: Long-term caching - cacheLife('max')
-// Option D: Short-lived cache - cacheLife('minutes')
-// Option E: Custom profile - cacheLife({ stale, revalidate, expire })
-```
-
-**When to use each strategy:**
-- **Strategy A (Time-based):** Content changes on predictable schedules (most common)
-- **Strategy B (Tag-based):** Content updates unpredictably (admin actions, CMS events)
-- **Strategy C (Long-term):** Truly immutable content (historical data, archives)
-- **Strategy D (Short-lived):** Frequently updating content (dashboards, live data)
-- **Strategy E (Custom):** Advanced use cases with specific timing needs
-
-**Load the MCP resource for detailed examples and complete code for each strategy.**
-
-**Migration Checklist - cacheLife/cacheTag:**
-
-For EVERY component/function with `"use cache"`:
-
-- [ ] **Review imports:** Are `cacheLife` and/or `cacheTag` imports commented but visible?
-- [ ] **User decision:** Has someone decided which revalidation strategy to use?
-- [ ] **Configuration:** Is the chosen strategy uncommented and configured?
-- [ ] **Documentation:** Does the code comment explain WHY this strategy was chosen?
-- [ ] **Testing:** Have you verified the cache behavior matches expectations?
-
-**Red Flags - cacheLife/cacheTag Issues:**
-
-- ❌ **`"use cache"` without any cacheLife/cacheTag:** Will cache forever by default - decide intentionally
-- ❌ **cacheLife configured but no comment:** Future developers won't know why this value was chosen
-- ❌ **Multiple conflicting cacheTag calls:** May cause unexpected revalidation behavior
-- ❌ **cacheTag on non-revalidatable routes:** Tag-based revalidation won't work on static routes
-- ❌ **Very short revalidation times:** (< 30 seconds) - Consider if caching helps performance at all
-
-### Special Case: Handling `new Date()` and `Math.random()` in Cache Components
-
-**📖 For complete guidance on handling dynamic values in cached components, load:**
-```
-Read resource "nextjs16://migration/examples"
-```
-
-Then navigate to **"Cache Components Examples"** → **"Handling `new Date()` and `Math.random()`"** for:
-- Decision framework with 3 options
-- Complete code examples for each option
-- Common patterns table
-- Migration checklist
-
-**Quick Reference:**
-
-When you encounter `new Date()` or `Math.random()` in cached components, ask:
-**"Should this value be captured at cache time, or fresh per-request?"**
-
-**Three Options:**
-1. **Fresh Per-Request (Recommended):** Use `"use cache: private"` - always fresh
-2. **Captured at Cache Time:** Use `"use cache"` - frozen until revalidation (document tradeoff)
-3. **Extract to Separate Component:** Mix static (cached) + dynamic (Suspense)
-
-**Load the MCP resource for detailed examples and complete migration patterns.**
-
-### Special Case: Using `use cache` in Route Handlers (API Routes)
-
-**CRITICAL: `use cache` cannot be used directly inside Route Handler body**
-
-Route Handlers (`route.ts`/`route.js` files in `app/api/`) follow the same caching model as UI routes, but with an important restriction:
-
-**⚠️ Key Rule:** `use cache` **MUST** be extracted to a helper function - it cannot be used directly in the Route Handler function body.
-
-**Why:** Response objects (`Response.json()`, `NextResponse`, etc.) cannot be directly serialized for caching. The cached function must return serializable data (objects, arrays, primitives), not Response objects.
-
-**📖 For complete guidance on Route Handlers with Cache Components, load:**
-```
-Read resource "cache-components://route-handlers"
-```
-
-This resource provides:
-- Complete correct pattern (extract to helper function)
-- Incorrect pattern examples (direct use in handler)
-- Dynamic, static, and cached Route Handler examples
-- Migration checklist for Route Handlers
-- Common mistakes and how to avoid them
-- Best practices and patterns
-- Reference to [Next.js documentation](https://nextjs.org/docs/app/getting-started/cache-components#route-handlers-with-cache-components)
-
-**Quick Reference:**
-
-**Route Handler Behavior:**
-1. **Dynamic by default:** Route Handlers are dynamic by default (like all routes with Cache Components)
-2. **Pre-rendering:** Static handlers (no dynamic data) will be pre-rendered at build time
-3. **Caching:** Extract data fetching to a helper function with `use cache` to cache the data
-4. **Runtime APIs:** Using `cookies()`, `headers()`, or `connection()` defers to request time (no pre-rendering)
-
-**Key Pattern:**
-- ✅ **Correct:** Extract data fetching to helper function, add `use cache` to helper, return `Response.json()` in handler
-- ❌ **Incorrect:** Using `use cache` directly in Route Handler body (will cause serialization errors)
-
-**Load the MCP resource for detailed examples and complete migration patterns.**
-
-**Handling Unclear Cases That Can't Be Resolved:**
-
-If after multiple attempts a fix continues to fail or the issue is unclear, leave a comment documenting the problem:
-
-**For 3rd party packages (use the 3RD PARTY PACKAGE ISSUE format):**
-```typescript
-// ⚠️ 3RD PARTY PACKAGE ISSUE: payment-gateway-sdk@2.1.0
-// Error: Package uses internal async provider pattern that blocks routes
-// Source: node_modules/payment-gateway-sdk/dist/index.js
-// Workaround attempted: Suspense boundary, dynamic import, "use cache: private"
-// Status: Cannot fix - requires package update
-// Recommendation: Check for Cache Components-compatible version or alternative package
-// TODO: Monitor package updates or switch to alternative-payment-sdk
-```
-
-**For other unclear cases (custom code, complex patterns):**
-```typescript
-// ⚠️ UNRESOLVED: Unable to determine caching strategy for this component
-// Issue: [describe the unclear behavior]
-// Error: [specific error that persists]
-// Recommendation: [what should be investigated]
-// TODO: [action items or conditions for revisiting]
-```
-
-**Common Unclear Cases:**
-- **3rd party packages** with incompatible internal implementations (use 3RD PARTY PACKAGE ISSUE format above)
-- Third-party components with unknown/complex internal state management
-- Components using undocumented async patterns
-- External library integrations with unclear rendering behavior
-- Timing-dependent code that behaves differently in cache vs runtime
-
-**When to Leave These Comments:**
-1. You've tried multiple caching strategies (cache, Suspense, private cache)
-2. All attempts result in the same error or unexpected behavior
-3. The root cause is unclear (third-party code, complex state, etc.)
-4. You've verified the error isn't due to missing Suspense/cache directives
-5. The component works but you can't determine the appropriate caching mode
-
-**IMPORTANT: For 3rd party package issues:**
-- Always use the "3RD PARTY PACKAGE ISSUE" format
-- Include package name and version
-- Document attempted workarounds
-- List the issue in Phase 5 output section "G. 3rd Party Package Issues"
-- Include in final report table
-
-**Example Scenarios:**
-```typescript
-// ⚠️ 3RD PARTY PACKAGE ISSUE: analytics-dashboard@4.2.1
-// Error: Component works in dev but different behavior in build prerender
-// Source: node_modules/analytics-dashboard/dist/Dashboard.js
-// Workaround attempted: Suspense boundary - partially works
-// Status: Partially resolved - some features disabled
-// Recommendation: Contact package maintainer about Cache Components support
-// TODO: Upgrade when analytics-dashboard@5.0 releases with CC support
-
-// ⚠️ UNRESOLVED: Custom animation timing issue
-// Issue: Animation component behaves differently in cache vs runtime
-// Error: Cached value inconsistent between prerender and runtime
-// Recommendation: Investigate hydration mismatch, may need "use cache: private"
-// TODO: Profile in production build to understand timing behavior
-```
-
-This allows the codebase to be functional while clearly marking areas needing future investigation and tracking 3rd party compatibility issues separately.
-
-**Continue until:**
-- All routes return 200 OK
-- `get_errors` returns no errors
-- No console warnings related to Cache Components
-- All fixes have explanatory comments
-
-**Verification Strategy After All Fixes:**
-
-After completing Step 3 and fixing all errors, verify with a final build:
-
-```bash
-# Final verification build
- run build -- --debug-prerender
-```
-
-**Expected Result:**
-- ✅ Build succeeds without errors
-- ✅ All routes build successfully
-- ✅ Build output shows proper cache status for each route
-- ✅ No "blocking route" or "dynamic value" errors
-
-**If final build still has errors:**
-- Review build output for remaining issues
-- Fix any missed errors following Step 3 process
-- Re-run build until all errors are resolved
-
-**Optional: Dev Server Verification**
-
-If you want to verify routes interactively:
-```bash
-# Start dev server (MCP is enabled by default in Next.js 16+)
- dev
-```
-
-Then:
-- Navigate to key routes in browser
-- Verify dynamic content loads correctly
-- Test cached content behavior
-- Confirm Fast Refresh works with changes
-
-**Important:**
-- Build verification is the primary success criterion
-- Dev server verification is optional but helpful for testing dynamic behavior
-- Every fix should include comments explaining the decision
-
-## PHASE 4: Final Verification & Optional Browser Investigation
-────────────────────────────────────────
-
-**Prerequisites:**
-- ✅ Phase 3 completed with fixes applied
-- ✅ Build verification from Phase 3
-
-**⚠️ MANDATORY: Load verification resource**
-
-You MUST load:
-```
-ReadMcpResourceTool(server="next-devtools", uri="cache-components://build-behavior")
-```
-
-This provides build verification strategies and troubleshooting guidance.
-
-### Option A: Phase 3 Build Passed (Most Common)
-
-**If Phase 3 Step 3C build passed with 0 errors:**
-
-2. **Optional Dev Mode Test**
- ```bash
- dev
- ```
- - Test a few key routes in dev mode
- - Verify cached content behavior
- - Confirm Fast Refresh works
-
-**You're done! ✅**
-
-### Option B: Phase 3 Had Unclear Errors (Rare)
-
-**If Phase 3 Step 3C had unclear errors that couldn't be fixed from build output:**
-
-1. **Start Dev Server**
- ```bash
- # Start dev server (MCP is enabled by default in Next.js 16+)
- dev
- ```
-
- Wait for server to show ready message with URL.
-
-2. **Verify MCP Server Active**
- - Connect to `{dev-server-url}/_next/mcp`
- - Call `get_project_metadata` to verify
-
-3. **Use Browser to Investigate Unclear Errors** (requires Playwright)
-
- For each unclear error:
-
- a. **Start browser automation:**
- ```
- browser_eval({ action: "start", browser: "chrome", headless: true })
- ```
-
- b. **Navigate to failing route:**
- ```
- browser_eval({ action: "navigate", url: "{dev-server-url}/{route-path}" })
- ```
-
- c. **Collect detailed errors:**
- - Connect to Next.js MCP endpoint
- - Call `get_errors` to collect from browser session
-
- d. **Fix the error:**
- - Make code changes
- - Fast Refresh applies automatically
- - Re-navigate to verify
-
- e. **Repeat** for all unclear errors
-
-4. **Final Build Verification**
- ```bash
- run build -- --debug-prerender
- ```
-
- Expected: Build passes with 0 errors.
-
-**You're done! ✅**
-
-## Important Caching Behavior Notes
-────────────────────────────────────────
-
-### Memory Cache vs Persistent Cache
-
-**Self-Hosting (Long-Running Server):**
-- `"use cache"` entries saved in memory
-- Available for subsequent requests within same process
-- Lost when server restarts
-
-**Vercel / Serverless:**
-- NO memory cache between requests (lambda is ephemeral)
-- `"use cache"` only effective if included in prerendered fallback shell
-- If cached content is in same Suspense boundary as blocking content, it won't be in shell
-- For persistent cache between requests, use `"use cache: remote"` to store in Vercel Data Cache (VDC)
-
-**Key Implication:**
-If you see a cached component re-executing on every request:
-1. Check if there's blocking async IO in the same Suspense boundary
-2. Either: Wrap blocking content in its own Suspense boundary
-3. Or: Use `"use cache: remote"` for VDC storage
-
-### Prefetching Behavior
-
-**Production Only:**
-- Link prefetching ONLY works in production (`npm run build && npm start`)
-- In development, prefetching is disabled
-- Test prefetching in production build before deploying
-
-**What Gets Prefetched:**
-- Static shells for routes with `` components in viewport
-- Only NEW static content (not already in cache)
-- Full cached components (with `"use cache"`)
-- `"use cache: private"` content can be prefetched with runtime values (cookies, params, searchParams)
-
-### Static Shell Storage
-
-**Build Output:**
-- Saved in `.next` directory during build
-- Served as static assets (self-hosting)
-- Stored in ISR cache on Vercel (globally distributed to edge)
-
-**Partial Revalidation:**
-- Can be revalidated without full rebuilds
-- Using `revalidateTag` or `revalidatePath`
-- Based on `cacheLife` revalidate/expire times
-
-## OUTPUT FORMAT
-────────────────────────────────────────
-Report findings in this format:
-
-```
-# Cache Components Setup Report
-
-## Summary
-- Project: {{PROJECT_PATH}}
-- Next.js Version: [version]
-- Package Manager: [detected manager]
-
-## Phase 1: Pre-Flight Checks
-[x] Next.js version verified (16.0.0+ stable or canary - NOT beta)
-[x] Package manager detected: [manager]
-[x] Existing config checked
-[x] Routes identified: [count] routes
-[x] Verification strategy: Build-first (recommended for all projects)
-[x] Route Segment Config usage documented
-[x] unstable_noStore() usage documented
-
-## Phase 2: Configuration & Flags
-[x] cacheComponents enabled (version-aware: experimental for 16.0.0, root level for canary)
-[x] Configuration backed up
-[x] Incompatible flags removed (ppr, dynamicIO, useCache)
-[x] Compatible flags preserved
-[x] Route Segment Config documented
-[x] Config syntax validated
-
-## Phase 3: Build-First Error Fixing & Code Changes
-
-### Step 1: Obvious Breaking Changes Removed
-[x] Route Segment Config exports removed: [count]
- - [file path]: Removed `export const dynamic = 'force-static'` → Added "use cache"
- - [file path]: Removed `export const revalidate = 3600` → Added cacheLife('hours')
- - [file path]: Removed `export const revalidate = 60` → Added cacheLife('minutes')
- - [file path]: Removed `export const revalidate = 1800` → Added cacheLife({ revalidate: 1800 })
- - ...
-
-[x] unstable_noStore() calls removed: [count]
- - [file path]: Removed unstable_noStore() call and import
- - ...
-
-### Step 2: Initial Build Results
-[x] First build executed: ` run build -- --debug-prerender`
-[x] Total routes: [count]
-[x] Failing routes: [count]
-[x] Passing routes: [count]
-
-**Error Summary from Build:**
-- Blocking route errors: [count]
-- Dynamic value errors: [count]
-- Unavailable API errors: [count]
-- Route params errors: [count]
-- Other errors: [count]
-
-### Step 3A: Obvious Errors Fixed (From Build Output)
-[x] Reviewed build output from Step 2
-[x] Fixed all errors with clear solutions
-[x] Total obvious errors fixed: [count]
-
-**Errors Fixed:**
-- [file path]: [error type] - [fix applied]
-- ...
-
-### Step 3B: Build Verification After Obvious Fixes
-[x] Re-ran build: ` run build -- --debug-prerender`
-[x] Result: [X] routes passing, [Y] routes failing
-
-### Step 3C: Final Build Verification
-[x] Re-ran build: ` run build -- --debug-prerender`
-[x] Result: [X] routes passing, [Y] routes failing
- - If 0 failing: ✅ Success! Proceed to Phase 4 - Option A
- - If clear errors remain: Looped back to Step 3A
- - If unclear errors remain: Proceeded to Phase 4 - Option B
-
-## Phase 4: Final Verification
-[x] Phase 3 build passed with 0 errors (most common - Option A)
-[x] Optional dev mode testing completed
-
-**If Option B was needed (unclear errors):**
-[x] Started dev server with MCP
-[x] Used browser_eval to investigate unclear errors
-[x] Fixed unclear errors with Fast Refresh
-[x] Final build verification: ✅ 0 errors
-
-### Summary of Fixes by Type
-
-**A. Suspense Boundaries Added: [count]**
-- [file path]: Added Suspense boundary for dynamic content
-- ...
-
-**B. "use cache" Directives Added: [count]**
-- [file path]: Added "use cache" to page component
-- ...
-
-**C. Route Params Errors Fixed: [count]**
-- [file path]: Added generateStaticParams
-- ...
-
-**D. Unavailable API Errors Fixed: [count]**
-- [file path]: Moved cookies() outside cache scope
-- ...
-
-**E. Cache Tags Added: [count]**
-- [file path]: Added cacheTag('posts')
-- ...
-
-**F. cacheLife Profiles Configured: [count]**
-- [file path]: Added cacheLife('hours')
-- ...
-
-**G. 3rd Party Package Issues: [count]**
-- [package-name@version]: [error description]
- - File: [file path using the package]
- - Workaround: [Suspense boundary / Dynamic import / Alternative package / None]
- - Status: [Resolved / Partially resolved / Cannot fix]
- - Notes: [additional context]
-- ...
-
-### Build Iterations Summary
-- Step 2 - Initial build (after Step 1): [X] errors
-- Step 3B - After obvious fixes: [Y] errors
-- Step 3D - After unclear fixes: ✅ 0 errors
-- Total iterations: [count]
-
-### Summary of All Code Changes:
-- Total Route Segment Config exports removed: [count]
- - `revalidate` exports migrated to cacheLife: [count]
- - cacheLife('minutes'): [count] (was revalidate ≈ 60)
- - cacheLife('hours'): [count] (was revalidate ≈ 3600)
- - cacheLife('days'): [count] (was revalidate ≈ 86400)
- - cacheLife({ revalidate: X }): [count] (custom values)
- - `dynamic` exports removed: [count]
-- Total unstable_noStore() calls removed: [count]
-- Total Suspense boundaries added: [count]
-- Total "use cache" directives added: [count]
-- Total generateStaticParams functions added: [count]
-- Total cache tags added: [count]
-- Total cacheLife profiles configured: [count]
-- Total unavailable API errors fixed: [count]
-- Total 3rd party package issues encountered: [count]
- - Resolved with workarounds: [count]
- - Cannot fix (need package updates): [count]
-- Total build iterations: [count]
-
-
-## Migration Notes
-[Any special notes about the migration, especially if migrating from PPR]
-
-## Complete Changes Summary
-This enablement process made the following comprehensive changes:
-
-### Configuration Changes (Phase 2):
-- ✅ Enabled cacheComponents (location depends on version)
-- ✅ Removed incompatible flags (ppr, dynamicIO, useCache)
-- ✅ Preserved compatible flags
-- ✅ Documented Route Segment Config
-
-### Boundary & Cache Setup (Phase 3):
-- ✅ Added Suspense boundaries for dynamic content
-- ✅ Added "use cache" directives for cacheable content
-- ✅ Added "use cache: private" for prefetchable private content
-- ✅ Created loading.tsx files where appropriate
-- ✅ Added generateStaticParams for dynamic routes
-
-### API Migrations (Phase 3):
-- ✅ Moved cookies()/headers() calls outside cache scope
-- ✅ Handled dynamic values (connection(), "use cache" with cacheLife, or Suspense as appropriate)
-- ✅ Migrated Route Segment Config to "use cache" + cacheLife
-- ✅ Removed all export const dynamic/revalidate/fetchCache
-
-### Cache Optimization (Phase 3):
-- ✅ Added cacheTag() calls for granular revalidation
-- ✅ Configured cacheLife profiles for revalidation control
-- ✅ Set up cache invalidation strategies
-
-### Final Verification (Phase 4):
-- ✅ Build passed with 0 errors
-- ✅ Option B used if needed: Dev server + browser for unclear errors
-
-## Next Steps
-- Monitor application behavior in development
-- Test interactive features with Cache Components
-- Review cacheLife profile usage for optimization
-- Test prefetching in production build
-- Consider enabling Turbopack file system caching for faster dev
-- Monitor cache hit rates and adjust cacheLife profiles
-
-## Troubleshooting Tips
-- If cached components re-execute on every request: Check Suspense boundaries, consider "use cache: remote"
-- If prefetching doesn't work: Test in production build, not dev mode
-- If routes still show blocking errors: Look for parent Suspense or add "use cache"
-- If "use cache" with params fails: Add generateStaticParams
-- If dynamic APIs fail in cache: Move outside cache scope or use "use cache: private"
-- If Route Segment Config errors: Remove exports, use "use cache" + cacheLife instead
-
-## What Was Accomplished
-Cache Components is now fully enabled with:
-- ✅ Configuration flags properly set
-- ✅ All routes verified and working
-- ✅ All boundaries properly configured
-- ✅ All cache directives in place
-- ✅ All API migrations completed
-- ✅ Cache optimization strategies implemented
-- ✅ Zero errors in final verification
-- ✅ Production build tested and passing
-
-## 3rd Party Package Issues & Recommendations
-
-**Packages with Cache Components Compatibility Issues:**
-[If any 3rd party package issues were encountered, list them here]
-
-| Package | Version | Issue | Workaround | Status | Recommendation |
-|---------|---------|-------|------------|--------|----------------|
-| [package-name] | [version] | [error description] | [workaround applied] | [Resolved/Cannot fix] | [Upgrade/Replace/Report issue] |
-| ... | ... | ... | ... | ... | ... |
-
-**Actions Needed:**
-- [ ] Monitor package updates for Cache Components compatibility
-- [ ] Consider filing issues with package maintainers
-- [ ] Document workarounds for team reference
-- [ ] Plan to replace packages if no fix is available
-
-**If no 3rd party package issues:** ✅ All packages are compatible with Cache Components
-```
-
-# START HERE
-Begin Cache Components enablement:
-
-## Recommended Workflow (Build-First Approach)
-
-**Use this workflow for ALL projects.**
-
-Build verification is always reliable and doesn't require any additional tools like Playwright.
-
-**Workflow:**
-
-1. **Phase 1:** Pre-flight checks
-
-2. **Phase 2:** Enable Cache Components in config
-
-3. **Phase 3:** Build-first error fixing
- - Step 1: Remove breaking changes (exports, unstable_noStore)
- - Step 2: Build with --debug-prerender to see all errors
- - Step 3A: Fix all obvious errors from build output
- - Step 3B: Verify fixes with build
- - Step 3C: Final build verification
-
-4. **Phase 4:** Final verification
- - **Option A:** If Phase 3 passed (0 errors) - just verify with optional dev test
- - **Option B:** If Phase 3 had unclear errors - use dev server + browser to investigate, then final build
-
-**Why This Workflow Works Best:**
-
-✅ **No dependencies** - Works without Playwright or other tools
-✅ **Always reliable** - Build verification catches all errors
-✅ **Efficient for any project size** - Works for small and large projects
-✅ **Shows ALL errors at once** - Complete picture from build output
-✅ **Fixes in batches** - More efficient than one-by-one
-✅ **Clear error messages** - Build output is explicit
-✅ **Faster overall** - Fewer iteration cycles
-
-## Summary: There is NO alternative workflow
-
-**The workflow is now linear and simple:**
-
-1. **Phase 1:** Pre-flight checks
-2. **Phase 2:** Enable Cache Components in config
-3. **Phase 3:** Build-first error fixing (remove breaking changes → build → fix → verify)
-4. **Phase 4:** Final verification
- - Option A: Build passed (most common) - optional dev test
- - Option B: Unclear errors remain - dev server + browser investigation
-
-**Key points:**
-- Everyone uses Phase 3 (build-first with build verification)
-- Phase 4 has two paths based on Phase 3 outcome
-- No separate phases for browser vs final verification - merged into Phase 4
diff --git a/src/prompts/enable-cache-components.ts b/src/prompts/enable-cache-components.ts
deleted file mode 100644
index a1a28f7..0000000
--- a/src/prompts/enable-cache-components.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { z } from "zod"
-import { readResourceFile } from "../_internal/resource-path.js"
-
-export const inputSchema = {
- project_path: z
- .string()
- .optional()
- .describe("Path to the Next.js project (defaults to current directory)"),
-}
-
-type EnableCacheComponentsPromptArgs = {
- project_path?: string
-}
-
-export const metadata = {
- name: "enable-cache-components",
- title: "enable-cache-components",
- description:
- "Complete Cache Components setup for Next.js 16. Handles ALL steps: updates experimental.cacheComponents flag, removes incompatible flags, migrates Route Segment Config, starts dev server with MCP, detects all errors via chrome_devtools + get_errors, automatically fixes all issues by adding Suspense boundaries, 'use cache' directives, generateStaticParams, cacheLife profiles, cache tags, and validates everything with zero errors.",
- role: "user",
-}
-
-export function handler(args: EnableCacheComponentsPromptArgs): string {
- const projectPath = args.project_path || process.cwd()
-
- let promptTemplate = readResourceFile("prompts/enable-cache-components-prompt.md")
-
- // Replace template variables
- promptTemplate = promptTemplate.replace(/{{PROJECT_PATH}}/g, projectPath)
-
- return promptTemplate
-}
diff --git a/src/prompts/upgrade-nextjs-16-prompt.md b/src/prompts/upgrade-nextjs-16-prompt.md
deleted file mode 100644
index 5e45b8a..0000000
--- a/src/prompts/upgrade-nextjs-16-prompt.md
+++ /dev/null
@@ -1,618 +0,0 @@
-You are a Next.js upgrade assistant. Help upgrade this project from Next.js 15 (or earlier) to Next.js 16.
-
-PROJECT: {{PROJECT_PATH}}
-
-# REQUIRED: Load Migration Guide Resource
-
-**Before starting the upgrade, load the complete migration guide:**
-
-```
-Read resource "nextjs16://migration/examples"
-```
-
-This resource contains:
-- 🚨 Quick reference of all breaking changes
-- ✅ Complete checklist
-- 📖 All code examples with search commands
-- 🔧 Step-by-step implementation patterns
-
-**Additional Knowledge Resources (load as needed):**
-- `nextjs16://knowledge/overview` - Critical errors AI agents make
-- `nextjs16://knowledge/request-apis` - Detailed async API patterns
-- `nextjs16://knowledge/cache-invalidation` - Cache invalidation semantics
-- `nextjs16://knowledge/error-patterns` - Common errors and solutions
-- `nextjs16://knowledge/test-patterns` - Test-driven patterns
-- `nextjs16://knowledge/reference` - Complete API reference
-
-**Note:** Resource URIs use the `nextjs16://` scheme regardless of your MCP server name.
-
----
-
-# UPGRADE WORKFLOW: Next.js 15 → 16 Migration Guide
-
-The section below contains the step-by-step upgrade workflow. Load the knowledge base resources above for detailed technical behavior, API semantics, and best practices.
-
-## PHASE 1: Pre-Flight Checks (REQUIRED)
-────────────────────────────────────────
-Check these BEFORE running the codemod:
-
-0. **Detect Monorepo Structure (CRITICAL)**
- ⚠️ **If this is a monorepo, you MUST run the upgrade flow on each individual app, NOT at the monorepo root**
-
- Check for monorepo indicators:
- - Workspace configuration: `workspaces` field in root package.json
- - Monorepo tools: pnpm-workspace.yaml, lerna.json, nx.json, turbo.json
- - Multiple app directories: apps/, packages/, services/ folders
-
- **If monorepo detected:**
- ```bash
- # Find all Next.js apps in the monorepo
- find . -name "package.json" -not -path "*/node_modules/*" -exec grep -l "\"next\":" {} \;
- ```
-
- **For each Next.js app found:**
- - Navigate to that app's directory: `cd apps/web` (or wherever the app is)
- - Run the ENTIRE upgrade workflow from that directory
- - The codemod will fail if run from monorepo root
-
- Example for typical monorepo structure:
- ```bash
- # If you have: apps/web, apps/admin, apps/marketing
- cd apps/web && [run upgrade workflow here]
- cd ../admin && [run upgrade workflow here]
- cd ../marketing && [run upgrade workflow here]
- ```
-
-1. **Detect Package Manager**
- Check: package.json "packageManager" field or lock files
-
- **Template Variables:**
- ```
- npm: = npm = npx
- pnpm: = pnpm = pnpx
- yarn: = yarn = yarn dlx
- bun: = bun = bunx
- ```
-
- Use these template variables in ALL commands below for consistency
-
-2. **Node.js Version**
- Required: Node.js 20.9+
- Check: node --version
- Action: Upgrade if < 20.9.0
-
-3. **TypeScript Version**
- Required: TypeScript 5.1+
- Check: package.json → devDependencies.typescript
- Note: Document if upgrade needed (codemod won't upgrade this)
- Action: If < 5.1, plan to upgrade after codemod
-
-4. **Browser Support** (Informational)
- Next.js 16 requires these minimum browser versions:
- - Chrome 111+
- - Edge 111+
- - Firefox 111+
- - Safari 16.4+
- Note: No action needed, but verify your target audience supports these versions
-
-5. **Current Next.js Version**
- Check: package.json → dependencies.next
-
- ```bash
- # Check current version
- grep '"next":' package.json
- ```
-
- **If on beta channel:**
- - Current: `"next": "16.0.0-beta.X"` or `"next": "beta"`
- - Action: Will upgrade to latest stable
- - Note: Beta users should upgrade to stable now that it's released
-
- Note: Document current version for rollback
-
-6. **Git Status**
- Check: git status
- Action: Ensure working directory is clean (no uncommitted changes)
- Why: The codemod requires a clean git state to run
-
-## PHASE 2: Run Automated Codemod
-────────────────────────────────────────
-⚠️ **IMPORTANT: Run this BEFORE making any manual changes**
-
-The codemod requires a clean git working directory. It will fail with this error if you have uncommitted changes:
-> But before we continue, please stash or commit your git changes
-
-Run the official codemod to handle most changes automatically:
-
-{{CODEMOD_COMMAND}}
-
-```bash
-# This will:
-# - Upgrade Next.js, React, and React DOM to {{UPGRADE_CHANNEL}} versions
-# - Upgrade @types/react and @types/react-dom to {{UPGRADE_CHANNEL}}
-# - Convert async params/searchParams automatically
-# - Update experimental config locations
-# - Fix other breaking changes
- @next/codemod@canary upgrade {{UPGRADE_CHANNEL}}
-```
-
-**Note:** When prompted for options during codemod execution, select "yes" for all selections to apply all recommended changes.
-
-**What the codemod handles:**
-- ✅ Upgrades Next.js, React, and React DOM to latest versions
-- ✅ Upgrades React type definitions to latest
-- ✅ Converts sync params/searchParams to async (most cases)
-- ✅ Updates experimental config locations
-- ✅ Fixes metadata generation functions
-- ✅ Updates deprecated imports
-
-**What the codemod does NOT handle:**
-- ❌ TypeScript version upgrade (do this manually if needed)
-
-**After codemod completes:**
-1. **If you were on beta:** Load the beta-to-stable migration resource for additional config changes:
- ```
- Read resource "nextjs16://migration/beta-to-stable"
- ```
- Key changes: `experimental.cacheLife` → `cacheLife` (move to root level)
-
-2. Review the git diff to see what changed
-
-3. If TypeScript < 5.0, upgrade it now:
- ```bash
- add -D typescript@latest
- ```
-
-4. **Verify the upgrade by running a build:**
- ```bash
- run build
- # If this succeeds, the automated upgrade is complete
- # If it fails, proceed to Phase 3 to identify and fix remaining issues
- ```
-
-4. **Browser Verification with browser_eval (RECOMMENDED):**
- After the build succeeds, verify pages actually load correctly in a browser:
-
- a. Start the Next.js dev server:
- ```bash
- run dev
- ```
-
- b. Use the browser_eval MCP tool to verify pages load correctly:
- ```
- # Start browser automation
- Use browser_eval tool with action="start"
-
- # Navigate to key pages and verify they load
- Use browser_eval tool with action="navigate", url="http://localhost:3000"
- Use browser_eval tool with action="navigate", url="http://localhost:3000/users/1"
- # ... test other important routes
-
- # Check for console errors
- Use browser_eval tool with action="console_messages", errorsOnly=true
-
- # Close browser when done
- Use browser_eval tool with action="close"
- ```
-
- **Why browser_eval instead of curl:**
- - ✅ browser_eval actually renders the page and executes JavaScript
- - ✅ Detects runtime errors that curl/HTTP requests cannot catch
- - ✅ Verifies client-side hydration and React component mounting
- - ✅ Captures browser console errors and warnings
- - ✅ Tests the full user experience, not just HTTP status codes
-
- **Note:** If you only use curl or simple HTTP GET requests, you'll miss client-side errors, hydration issues, and JavaScript runtime problems.
-
-**Wait for codemod to complete and verify both build and browser tests before proceeding to Phase 3**
-
-## PHASE 3: Analyze Remaining Issues
-────────────────────────────────────────
-After the codemod runs, check for any remaining issues it might have missed:
-
-### Manual Check Checklist:
-
-**A. Completely Removed Features (NOT handled by codemod)**
- Check your codebase for these removed APIs and configs.
-
- **📖 For detailed code examples, see: `nextjs16://migration/examples` (Removed Features Examples)**
-
- **1. AMP Support Removed:**
- - Search: `grep -r "useAmp\|amp:" app/ src/ pages/`
- - Remove all AMP-related code: `useAmp` hook, `export const config = { amp: true }`
- - No replacement available - AMP support completely removed
-
- **2. Runtime Config Removed:**
- - Search: `grep -r "serverRuntimeConfig\|publicRuntimeConfig" next.config.*`
- - Remove `serverRuntimeConfig` and `publicRuntimeConfig` from next.config.js
- - Migrate to environment variables in `.env` files
-
- **3. PPR Flags Removed:**
- - Search: `grep -r "experimental.ppr\|experimental_ppr" next.config.* app/ src/`
- - Remove `experimental.ppr` flag and `experimental_ppr` route exports
- - Use `experimental.cacheComponents: true` instead
-
- **4. experimental.dynamicIO Renamed:**
- - Search: `grep -r "experimental.dynamicIO" next.config.*`
- - Rename to `experimental.cacheComponents`
-
- **5. unstable_rootParams() Removed:**
- - Search: `grep -r "unstable_rootParams" app/ src/`
- - Alternative API coming in upcoming minor release
- - Temporarily use params from props
-
- **6. Automatic scroll-behavior: smooth Removed:**
- - No longer automatic
- - Add `data-scroll-behavior="smooth"` to `` tag if needed
-
- **7. devIndicators Config Options Removed:**
- - Search: `grep -r "devIndicators" next.config.*`
- - Remove `appIsrStatus`, `buildActivity`, `buildActivityPosition` options
- - The dev indicator itself remains
-
-**B. Parallel Routes (NOT handled by codemod)**
- Files: Check for @ folders (except `@children`)
- Requirement: All parallel route slots must have `default.js` files
- Impact: Build fails without them
-
- **Note:** `@children` is a special implicit slot and does NOT require a `default.js` file.
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Parallel Routes Examples)**
-
- Quick fix: Create `app/@modal/default.js` (or `@auth`, etc.) that returns `null`
-
-**C. Image Security Config (NOT handled by codemod)**
- File: next.config.js
- Check: Are you using local images with query strings?
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Image Configuration Examples)**
-
- If yes, add `images.localPatterns` config
-
-**D. Image Default Changes (Behavior change)**
- Note: These defaults changed automatically in v16:
- - `minimumCacheTTL`: 60s -> 14400s (4 hours)
- - `qualities`: [1..100] -> [75]
- - `imageSizes`: removed 16
- - `dangerouslyAllowLocalIP`: now false by default
- - `maximumRedirects`: unlimited -> 3
-
- Action: Review if these affect your app, override in config if needed
-
-**E. Lint Command Migration (NOT handled by codemod)**
- Files: package.json scripts, CI workflows
- Check: Scripts using `next lint`
- Note: `next build` no longer runs linting automatically
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Lint Command Migration)**
-
- Options:
- 1. Use Biome: `biome check .`
- 2. Use ESLint directly: ` @next/codemod@canary next-lint-to-eslint-cli .`
-
- **Note:** `@next/eslint-plugin-next` now defaults to ESLint Flat Config format, aligning with ESLint v10
-
-**F. next.config.js Turbopack Config Updates (REQUIRED for canary users)**
- File: next.config.js
- Check: `turbopackPersistentCachingForDev` config option
- Action: Rename to `turbopackFileSystemCacheForDev`
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Config Migration Examples)**
-
- Note: This was a temporary change on canary - not everyone has this config
-
- **Additional Turbopack Enhancement:**
- - Turbopack now automatically enables Babel if a babel config is found
- - Previously exited with hard error
- - No action needed - automatic behavior
-
-**G. --turbopack Flags (No Longer Needed)**
- Files: package.json scripts
- Check: `next dev --turbopack`, `next build --turbopack`
- Action: Remove `--turbopack` flags (Turbopack is default in v16)
- Note: Use `--webpack` flag if you want webpack instead
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Config Migration Examples)**
-
-**H. ESLint Config Removal (REQUIRED)**
- File: next.config.js
- Check: `eslint` configuration object
- Action: Remove eslint config from next.config.js
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Config Migration Examples)**
-
- Note: ESLint configuration should now be in .eslintrc.json or eslint.config.js
- Migration: Use ` @next/codemod@canary next-lint-to-eslint-cli .` if needed
-
-**I. serverComponentsExternalPackages Deprecation (BREAKING)**
- File: next.config.js
- Check: `serverComponentsExternalPackages` in experimental config
- Action: Move out of experimental - this is now a top-level config option
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Config Migration Examples)**
-
- {{IF_BETA_CHANNEL}}**J. Beta to Stable Migration (REQUIRED for beta channel users)**
-
- You are currently upgrading to Next.js 16 **beta** channel. When Next.js 16 **stable** is released, you will need to apply additional config migrations:
-
- {{BETA_TO_STABLE_GUIDE}}
-
- **Key migration when stable is released**: `experimental.cacheLife` must be moved to top-level `cacheLife`{{/IF_BETA_CHANNEL}}
-
-
-**K. Edge Cases the Codemod May Miss**
- Review these manually:
-
- - Complex async destructuring patterns
- - Dynamic params in nested layouts
- - Route handlers with cookies()/headers() in conditionals
- - Custom metadata generation with complex logic
- - Metadata image routes (opengraph-image, twitter-image, icon, apple-icon)
-
- **📖 For detailed code examples, see: `nextjs16://migration/examples` (Async API Migration Examples)**
-
- **CRITICAL: Only change if function actually uses these 5 APIs:**
- 1. `params` from props
- 2. `searchParams` from props
- 3. `cookies()` in body
- 4. `headers()` in body
- 5. `draftMode()` in body
-
- **Do NOT change:**
- - `robots()`, `sitemap()`, `manifest()` without these APIs
- - `generateStaticParams()`
- - Any function that doesn't use the 5 APIs above
-
- **METADATA IMAGE ROUTES - Important Changes:**
- For metadata image route files (opengraph-image, twitter-image, icon, apple-icon):
- - The function signature remains `{ params, id }` but `params` becomes a Promise
- - `params` is now async: `await params`
- - The `id` parameter remains a string (not a Promise)
-
- See migration examples resource for complete before/after code
-
-**L. ViewTransition API Renamed (NOT handled by codemod)**
- Files: Search for imports of `unstable_ViewTransition` from React
- Action: Rename to `ViewTransition` (now stable in v16)
-
- **📖 For code examples, see: `nextjs16://migration/examples` (ViewTransition API Migration)**
-
- - Rename `unstable_ViewTransition` → `ViewTransition`
- - Remove `experimental.viewTransition` flag from next.config.js
-
-**M. revalidateTag API Changes (Deprecation - NOT handled by codemod)**
- Files: Search for `revalidateTag(` calls
- Check: All revalidateTag calls now require a profile parameter
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Cache Invalidation Examples)**
-
- Search: `grep -r "revalidateTag(" app/ src/`
-
- **When to use which:**
- - Use `updateTag('tag')` in Server Actions when you need immediate consistency (read-your-own-writes, no profile parameter)
- - Use `revalidateTag('tag', 'max')` in Route Handlers or when background invalidation is acceptable (requires profile parameter)
-
- Load `nextjs16://knowledge/cache-invalidation` for detailed API semantics and migration patterns.
-
-**N. Middleware to Proxy Migration (NOT handled by codemod)**
- Files: middleware.ts, next.config.js
- Check: Middleware-related files and config properties
-
- **📖 For code examples, see: `nextjs16://migration/examples` (Middleware to Proxy Examples)**
-
- The `middleware` concept is being renamed to `proxy` in Next.js 16:
-
- **File renames:**
- - Rename `middleware.ts` → `proxy.ts`
- - Rename named export `middleware` → `proxy` in the file
-
- **Config property renames:**
- - `experimental.middlewarePrefetch` → `experimental.proxyPrefetch`
- - `experimental.middlewareClientMaxBodySize` → `experimental.proxyClientMaxBodySize`
- - `experimental.externalMiddlewareRewritesResolve` → `experimental.externalProxyRewritesResolve`
- - `skipMiddlewareUrlNormalize` → `skipProxyUrlNormalize`
-
- Search: `grep -r "middlewarePrefetch\|middlewareClientMaxBodySize\|externalMiddlewareRewritesResolve\|skipMiddlewareUrlNormalize" .`
-
-**O. Build and Dev Improvements (Informational - No action needed)**
- These improvements are automatic in Next.js 16:
-
- - **Terminal Output Redesign:**
- - Clearer formatting
- - Better error messages
- - Improved performance metrics
-
- - **Separate Output Directories:**
- - `next dev` and `next build` now use separate output directories
- - Enables concurrent execution of both commands
-
- - **Lockfile Mechanism:**
- - Prevents multiple `next dev` or `next build` instances on same project
- - Prevents conflicts from concurrent builds
-
- - **Modern Sass Support:**
- - `sass-loader` bumped to v16
- - Supports modern Sass syntax and new features
- - Automatic - no action needed if using Sass
-
- - **Native TypeScript Config (Optional):**
- - Run with `--experimental-next-config-strip-types` flag to enable native TS for `next.config.ts`
- - Example: `next dev --experimental-next-config-strip-types`
-
-**P. unstable_noStore Migration (If using Cache Components)**
- - Search: `grep -r "unstable_noStore" app/ src/`
- - Context: If you plan to enable Cache Components (experimental.cacheComponents)
- - Action: Remove all `unstable_noStore()` calls - dynamic is the default with Cache Components
- - Migration: No replacement needed - everything is dynamic by default
- - Alternative: If content should be cached, use `"use cache"` instead
-
- **📖 For code examples, see: `nextjs16://migration/examples` (unstable_noStore Examples)**
-
- **Note:** `unstable_noStore()` is only incompatible when Cache Components are enabled. If you're not using Cache Components, you can keep using it.
-
-**Q. Other Deprecated Features (WARNINGS - Optional)**
- - `next/legacy/image` → use `next/image`
- - `images.domains` → use `images.remotePatterns`
- - `unstable_rootParams()` → being replaced
-
-## PHASE 4: Apply Manual Fixes
-────────────────────────────────────────
-Only fix issues the codemod missed:
-
-**📖 For all code examples, see: `nextjs16://migration/examples`**
-
-Based on Phase 3 analysis, apply only the necessary manual fixes:
-
-**1. Remove completely removed features (if found in Phase 3 section A)**
- - Remove AMP-related code
- - Migrate runtime configs to environment variables
- - Remove PPR flags
- - Rename experimental.dynamicIO to cacheComponents
- - Remove unstable_rootParams() usage
- - Add data-scroll-behavior attribute if needed
- - Remove devIndicators config options
-
- See: `nextjs16://migration/examples` → Removed Features Examples
-
-**2. Add missing default.js files (if you have @ folders)**
-
- See: `nextjs16://migration/examples` → Parallel Routes Examples
-
-**3. Add image security config (if using local images with query strings)**
-
- See: `nextjs16://migration/examples` → Image Configuration Examples
-
-**4. Update lint commands (if using next lint in scripts/CI)**
-
- See: `nextjs16://migration/examples` → Lint Command Migration
-
-**5. Fix revalidateTag calls (see section M in Phase 3)**
- - Update all `revalidateTag(tag)` calls to include profile parameter
- - Use `updateTag(tag)` for Server Actions (read-your-own-writes, no profile parameter)
- - Use `revalidateTag(tag, 'max')` for Route Handlers (background invalidation, requires profile parameter)
-
- See: `nextjs16://migration/examples` → Cache Invalidation Examples
-
-**6. Migrate middleware to proxy (see section N in Phase 3)**
- - Rename middleware.ts to proxy.ts
- - Update config properties
-
- See: `nextjs16://migration/examples` → Middleware to Proxy Examples
-
-**7. Remove unstable_noStore (see section P in Phase 3 - if using Cache Components)**
- - Remove all `unstable_noStore()` calls
- - Remove imports: `import { unstable_noStore } from 'next/cache'`
- - No replacement needed - dynamic by default with Cache Components
- - Add migration comments explaining removal
-
- See: `nextjs16://migration/examples` → unstable_noStore Examples
-
-**8. Fix edge cases the codemod missed (RARE - only if found in Phase 3 section K)**
-
- See: `nextjs16://migration/examples` → Async API Migration Examples
-
-## OUTPUT FORMAT
-────────────────────────────────────────
-Report findings in this format:
-
-```
-# Next.js 16 Upgrade Report
-
-## Summary
-- Current Version: [version]
-- On Beta: [Yes/No] - If yes, will upgrade to stable
-- Target Version: 16 (stable channel)
-- Package Manager: [npm/pnpm/yarn/bun]
-- Monorepo: [Yes/No]
-- If Monorepo, Apps to Upgrade: [list of app directories]
-
-## Phase 1: Pre-Flight Checks
-[ ] Monorepo structure detected (if applicable, list all Next.js apps)
-[ ] Working directory: [current app directory path]
-[ ] Node.js version (20.9+)
-[ ] TypeScript version checked (5.1+)
-[ ] Browser support requirements reviewed (Chrome 111+, Edge 111+, Firefox 111+, Safari 16.4+)
-[ ] Current Next.js version documented
-[ ] Git working directory is clean (no uncommitted changes)
-
-## Phase 2: Codemod Execution
-- [ ] Checked current version: On beta? [Yes/No]
-- [ ] If on beta: Noted to review beta-to-stable guide after upgrade
-- [ ] If already on stable: Skipped codemod (no reinstall needed)
-- [ ] If NOT on stable: Ran codemod: ` @next/codemod@canary upgrade {{UPGRADE_CHANNEL}}`
-- [ ] Selected "yes" for all codemod prompts
-- [ ] Codemod upgraded Next.js, React, and React DOM to stable
-- [ ] Codemod upgraded React type definitions to stable
-- [ ] Codemod applied automatic fixes
-- [ ] TypeScript upgraded if needed: ` add -D typescript@latest`
-- [ ] Reviewed git diff for codemod changes
-- [ ] **Verified build: ` run build` (if this passes, upgrade is complete!)**
-- [ ] **Browser verification with browser_eval (RECOMMENDED):**
- - [ ] Started dev server: ` run dev`
- - [ ] Started browser automation with action="start"
- - [ ] Navigated to key routes and verified pages load
- - [ ] Checked for console errors with action="console_messages"
- - [ ] Closed browser with action="close"
- - [ ] No client-side errors or hydration issues detected
-
-## Phase 3: Issues Requiring Manual Fixes
-Issues the codemod couldn't handle:
-[ ] A. Removed features check:
- [ ] AMP support removal
- [ ] Runtime config removal (serverRuntimeConfig, publicRuntimeConfig)
- [ ] PPR flags removal (experimental.ppr, experimental_ppr)
- [ ] experimental.dynamicIO → cacheComponents rename
- [ ] unstable_rootParams() removal
- [ ] Automatic scroll-behavior: smooth removal
- [ ] devIndicators config options removal
-[ ] B. Parallel routes missing default.js
-[ ] C. Image security config needed
-[ ] D. Image default changes reviewed
-[ ] E. Lint commands to update (ESLint flat config default noted)
-[ ] F. next.config.js: turbopackPersistentCachingForDev → turbopackFileSystemCacheForDev (Babel auto-enabled noted)
-[ ] G. Remove --turbopack flags from scripts
-[ ] H. next.config.js: Remove eslint config object
-[ ] I. next.config.js: Move serverComponentsExternalPackages out of experimental
-{{IF_BETA_CHANNEL}}[ ] J. next.config.js: Move cacheLife out of experimental (required when stable is released)
-{{/IF_BETA_CHANNEL}}[ ] K. Edge cases in async APIs
-[ ] L. ViewTransition API renamed (unstable_ViewTransition → ViewTransition, remove experimental.viewTransition flag)
-[ ] M. revalidateTag API changes
-[ ] N. Middleware to Proxy migration (rename middleware.ts → proxy.ts and config properties)
-[ ] O. Build and dev improvements reviewed (informational)
-[ ] P. unstable_noStore removal (if using Cache Components)
-[ ] Q. Deprecated features to update
-
-## Files Requiring Manual Changes
-- path/to/file1.ts (reason - not handled by codemod)
-- path/to/file2.tsx (reason - not handled by codemod)
-...
-
-## Phase 4: Manual Changes Applied
-- [List of manual fixes made]
-- [ ] **Final build verification: ` run build` (must succeed)**
-- [ ] **Final browser verification with browser_eval:**
- - [ ] All key routes load successfully in browser
- - [ ] No console errors or warnings
- - [ ] Client-side hydration works correctly
-
-## Completion Status
-- [ ] Upgrade complete - build succeeds without errors
-- [ ] Browser verification passed (using browser_eval, not curl)
-- [ ] All manual fixes applied (if any were needed)
-
-## Next Steps
-- [What to do next, e.g., commit changes, test in staging, etc.]
-```
-
-# START HERE
-Begin migration:
-1. **FIRST: Check if this is a monorepo** - If yes, navigate to each Next.js app directory and run the workflow there (NOT at monorepo root)
-2. Start with Phase 1 pre-flight checks (ensure clean git state)
-3. Run the codemod in Phase 2 (this handles most changes automatically)
-4. **Verify with build** - If ` run build` succeeds, continue to browser verification
-5. **Verify with browser_eval** - Use the browser_eval MCP tool to load pages in a real browser (NOT curl). This catches client-side errors that build verification misses
-6. Only if build or browser verification fails, proceed to Phase 3 and Phase 4 to fix remaining issues
-
-**⚠️ CRITICAL: Always use browser_eval for page verification, never curl or simple HTTP requests. browser_eval actually renders the page and detects runtime errors, hydration issues, and JavaScript problems that curl cannot catch.**
-
-**⚠️ MONOREPO USERS:** If you're in a monorepo, you MUST be in the specific Next.js app directory (e.g., `apps/web/`) before starting. The codemod will fail if run from the monorepo root.
diff --git a/src/prompts/upgrade-nextjs-16.ts b/src/prompts/upgrade-nextjs-16.ts
deleted file mode 100644
index 6f4a1f0..0000000
--- a/src/prompts/upgrade-nextjs-16.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-import { z } from "zod"
-import { readResourceFile } from "../_internal/resource-path.js"
-import { execSync } from "child_process"
-import {
- detectProjectChannel,
- processConditionalBlocks,
-} from "../_internal/nextjs-channel-detector.js"
-
-export const inputSchema = {
- project_path: z
- .string()
- .optional()
- .describe("Path to the Next.js project (defaults to current directory)"),
-}
-
-type UpgradeNextjs16PromptArgs = {
- project_path?: string
-}
-
-export const metadata = {
- name: "upgrade-nextjs-16",
- title: "upgrade-nextjs-16",
- description:
- "Guide through upgrading Next.js to version 16. CRITICAL: Runs the official codemod FIRST (requires clean git state) for automatic upgrades and fixes, then handles remaining issues manually. The codemod upgrades Next.js, React, and React DOM automatically. Covers async API changes, config moves, image defaults, parallel routes, and deprecations.",
- role: "user",
-}
-
-function checkNextjs16Availability(): { channel: "latest"; version: string } {
- try {
- const latestVersion = execSync("npm view next version", { encoding: "utf-8" }).trim()
- return { channel: "latest", version: latestVersion }
- } catch (error) {
- console.warn(
- "Failed to check Next.js version from npm registry, defaulting to latest channel assumption"
- )
- return { channel: "latest", version: "unknown" }
- }
-}
-
-export function handler(args: UpgradeNextjs16PromptArgs): string {
- const projectPath = args.project_path || process.cwd()
-
- const { version } = checkNextjs16Availability()
- const upgradeChannel = "latest"
- const codemodCommandNote = `**Note**: Next.js 16 stable (version ${version}) is now available.`
-
- // Detect if project is on beta/canary
- const { isBeta } = detectProjectChannel(projectPath)
-
- let promptTemplate = readResourceFile("prompts/upgrade-nextjs-16-prompt.md")
-
- // Replace basic template variables
- promptTemplate = promptTemplate.replace(/{{PROJECT_PATH}}/g, projectPath)
- promptTemplate = promptTemplate.replace(/{{UPGRADE_CHANNEL}}/g, upgradeChannel)
- promptTemplate = promptTemplate.replace(/{{CODEMOD_COMMAND}}/g, codemodCommandNote)
-
- // Process conditional blocks based on project channel
- promptTemplate = processConditionalBlocks(promptTemplate, isBeta)
-
- return promptTemplate
-}
diff --git a/src/resources/(cache-components)/00-overview.md b/src/resources/(cache-components)/00-overview.md
deleted file mode 100644
index 331e22a..0000000
--- a/src/resources/(cache-components)/00-overview.md
+++ /dev/null
@@ -1,112 +0,0 @@
-# Cache Components Mode: The Complete AI Agent Guide
-
-## Authoritative Reference Based on E2E Test Suite Patterns
-
-**Document Version**: 3.0 - E2E Test-Driven Edition
-**Target**: Next.js 15.6+ / 16.0.0-canary with `experimental.cacheComponents: true`
-**Source**: Derived from 125+ E2E test fixtures and behavioral assertions
-**Last Updated**: January 2025
-
-**⚠️ SCOPE**: This guide covers Cache Components mode (`experimental.cacheComponents: true`). These rules do NOT apply to standard Next.js 16 without Cache Components enabled.
-
----
-
-## 🎯 What AI Agents Get Wrong (And Why)
-
-Based on analyzing the complete E2E test suite, AI agents consistently make these mistakes **when Cache Components is enabled**:
-
-### ❌ **CRITICAL ERRORS AI AGENTS MAKE (with cacheComponents enabled):**
-
-1. **Using `loading.tsx` for loading states** (deprecated for PPR shell generation)
-2. **Using `export const dynamic = 'force-static'`** (completely incompatible with cacheComponents)
-3. **Using `export const fetchCache`** (raises build error with cacheComponents)
-4. **Using `export const revalidate`** (raises build error with cacheComponents)
-5. **Using `export const dynamicParams`** (raises build error with cacheComponents)
-6. **Using `export const runtime`** (raises build error when incompatible with cacheComponents)
-7. **Accessing `cookies()`/`headers()` in `'use cache'`** (throws runtime error)
-8. **Using `'use cache: private'` without ``** (build error)
-9. **Using `connection()` inside any cache scope** (throws error)
-10. **Not awaiting `params` and `searchParams`** (type error in Next.js 15)
-11. **Using `revalidateTag()` without the `profile` parameter** (deprecated)
-12. **Passing non-serializable props to cached components** (cache key issues)
-13. **Using `unstable_ViewTransition`** (renamed to `ViewTransition` in Next.js 16)
-14. **Using empty `await headers()` or `await cookies()` calls just to mark component as dynamic** (anti-pattern - use `await connection()` instead)
-
----
-
-## 📘 Table of Contents
-
-### Part 1: Core Mechanics
-
-1. [The Fundamental Paradigm Shift](#paradigm-shift)
-2. [How cacheComponents Changes Everything](#how-it-works)
-3. [The Three Types of Rendering](#three-types)
-
-### Part 2: Public Caches (`'use cache'`)
-
-4. [Public Cache Mechanics](#public-cache)
-5. [Cache Key Generation (Critical!)](#cache-keys)
-6. [Non-Serializable Props Pattern](#non-serializable)
-7. [Nested Public Caches](#nested-public)
-
-### Part 3: Private Caches (`'use cache: private'`)
-
-8. [Private Cache Mechanics](#private-cache)
-9. [When Private Cache is Included/Excluded](#private-inclusion)
-10. [Private Cache Patterns from Tests](#private-patterns)
-
-### Part 4: Runtime Prefetching
-
-11. [unstable_prefetch Configuration](#unstable-prefetch)
-12. [Runtime Prefetch Sample Patterns](#prefetch-samples)
-13. [What Gets Included in Runtime Prefetch](#prefetch-inclusion)
-14. [Stale Time Thresholds (30s Rule)](#stale-thresholds)
-
-### Part 5: Link Prefetching
-
-15. [Link prefetch Modes](#link-prefetch)
-16. [prefetch="unstable_forceStale" Deep Dive](#force-stale)
-17. [unstable_dynamicOnHover](#dynamic-on-hover)
-
-### Part 6: Request APIs
-
-18. [Async params Semantics](#params-semantics)
-19. [searchParams Behavior](#searchparams-behavior)
-20. [cookies() and headers() Patterns](#cookies-headers)
-21. [connection() Deep Dive](#connection-api)
-
-### Part 7: Cache Invalidation
-
-22. [updateTag() - Read-Your-Own-Writes](#update-tag)
-23. [revalidateTag(tag, profile) - New Signature](#revalidate-tag)
-24. [refresh() - Client Router Cache](#refresh-api)
-25. [Granular Invalidation Strategies](#granular-invalidation)
-
-### Part 8: Advanced Patterns
-
-26. [cacheLife() Profiles and Custom Config](#cache-life)
-27. [cacheTag() Multi-Tag Patterns](#cache-tag)
-28. [Draft Mode Behavior](#draft-mode)
-29. [generateStaticParams Integration](#generate-static-params)
-30. [Math.random() and Date.now() Patterns](#random-patterns)
-
-### Part 9: Build Behavior
-
-31. [What Gets Prerendered](#prerendering)
-32. [Resume Data Cache (RDC)](#resume-data-cache)
-33. [Static Shell vs Dynamic Holes](#shells-and-holes)
-34. [generateMetadata and generateViewport](#metadata-viewport)
-
-### Part 10: Error Patterns
-
-35. [Segment Config Errors](#segment-config-errors)
-36. [Dynamic Metadata Errors](#dynamic-metadata-errors)
-37. [Missing Suspense Errors](#missing-suspense)
-38. [Sync IO After Dynamic API Errors](#sync-io-errors)
-
-### Part 11: Real Test-Driven Patterns
-
-39. [Complete E2E Pattern Library](#pattern-library)
-40. [Decision Trees Based on Tests](#decision-trees)
-
----
diff --git a/src/resources/(cache-components)/01-core-mechanics.md b/src/resources/(cache-components)/01-core-mechanics.md
deleted file mode 100644
index c51cd7f..0000000
--- a/src/resources/(cache-components)/01-core-mechanics.md
+++ /dev/null
@@ -1,285 +0,0 @@
-## 0. The App Router Bundler Layer (Critical Context)
-
-### Understanding the Server Bundle Architecture
-
-**Important**: In Next.js App Router, many types of server-only code compile to the same server bundle:
-- Server components (pages, layouts)
-- Route handlers (`app/route.ts`)
-- Instrumentation (`instrumentation.ts`)
-- Proxy (`proxy.ts`, formerly `middleware.ts`)
-- Server Actions (in client/server components)
-
-**However, they execute in different contexts** - this is the KEY distinction:
-
-```
-┌──────────────────────────────────────────────────────────────┐
-│ SAME SERVER BUNDLE (all "server-only" code) │
-│ │
-│ ┌─────────────────────────────────────────────────────────┐ │
-│ │ REACT RENDERING LAYER │ │
-│ │ (Component tree execution during prerender/streaming) │ │
-│ │ │ │
-│ │ - Server Components (pages, layouts) │ │
-│ │ - Can use: 'use cache', cacheLife(), cacheTag() │ │
-│ │ - Participates in: PPR, static shell generation │ │
-│ │ - Can be prerendered at build time │ │
-│ │ - Streaming: Yes, with Suspense fallbacks │ │
-│ │ │ │
-│ └─────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌─────────────────────────────────────────────────────────┐ │
-│ │ HTTP REQUEST HANDLER LAYER │ │
-│ │ (Request-time only execution, outside React tree) │ │
-│ │ │ │
-│ │ - Route Handlers (GET, POST, PUT, DELETE) │ │
-│ │ - Cannot use: 'use cache' (not part of React tree) │ │
-│ │ - Uses: revalidateTag(), HTTP cache headers │ │
-│ │ - Participates in: HTTP caching only │ │
-│ │ - Cannot be prerendered (request-time only) │ │
-│ │ - Streaming: Native HTTP Response streaming │ │
-│ │ │ │
-│ └─────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌─────────────────────────────────────────────────────────┐ │
-│ │ INITIALIZATION LAYER │ │
-│ │ (Server startup, lifecycle hooks) │ │
-│ │ │ │
-│ │ - Instrumentation (one-time on server start) │ │
-│ │ - Cannot use: 'use cache' (not request-scoped) │ │
-│ │ - Participates in: Global state initialization │ │
-│ │ - Pre-request setup: Yes │ │
-│ │ - Streaming: N/A (not request-scoped) │ │
-│ │ │ │
-│ └─────────────────────────────────────────────────────────┘ │
-│ │
-│ ┌─────────────────────────────────────────────────────────┐ │
-│ │ EDGE PROXY LAYER │ │
-│ │ (Pre-request processing at edge/origin) │ │
-│ │ │ │
-│ │ - Proxy (optional, runs before route handlers) │ │
-│ │ - Cannot use: 'use cache' (request rewriting layer) │ │
-│ │ - Uses: Response modification, redirects │ │
-│ │ - Participates in: Request routing/transformation │ │
-│ │ - Prerender: N/A (edge layer) │ │
-│ │ - Streaming: Limited (request filter/transform layer) │ │
-│ │ │ │
-│ └─────────────────────────────────────────────────────────┘ │
-│ │
-└──────────────────────────────────────────────────────────────┘
-```
-
-### Why This Matters for Cache Components
-
-**`'use cache'` is a React-level caching directive**, designed specifically for:
-1. **Component tree execution** - renders JSX output
-2. **Build-time analysis** - Partial Prerendering discovers caches at build time
-3. **Streaming integration** - Works with Suspense boundaries and server streaming
-4. **Cache key generation** - Serializes component props to create deterministic cache keys
-
-**Route handlers operate at a different layer**:
-- Execute **only at request time** (not during prerender)
-- Return **Response objects** (not JSX/component output)
-- Cannot participate in **static shell generation**
-- Use **HTTP-level caching** (revalidateTag, cache headers, ISR)
-
-### The Critical Insight
-
-```typescript
-// ✅ SERVER COMPONENTS: Part of component tree, prerenderable
-export default async function Page() {
- 'use cache' // Belongs here - part of React rendering
- return
Content
-}
-
-// ❌ ROUTE HANDLERS: Not part of component tree, request-only
-export async function GET(request: Request) {
- // 'use cache' doesn't belong here - not React rendering
- // Use revalidateTag() instead
- return Response.json({ data: 'value' })
-}
-
-// ❌ INSTRUMENTATION: Not request-scoped, startup-only
-export async function register() {
- // 'use cache' doesn't belong here - not a request handler
- // Use global state / service initialization instead
-}
-```
-
-### Bundler Layer vs Execution Context
-
-| Code Type | Bundled To | Execution Context | Cache Model | Prerenderable |
-|-----------|-----------|-------------------|------------|---------------|
-| Server Component | Server | React tree (build + request) | `'use cache'` | ✅ Yes |
-| Route Handler | Server | HTTP request-time | `revalidateTag()` | ❌ No |
-| Server Action | Server | RPC call from client | `updateTag()` | ❌ No |
-| Instrumentation | Server | Startup hook | Global state | ❌ No |
-| Proxy/Middleware | Edge/Server | Request transform | Response headers | ❌ No |
-
-**Key Takeaway**: Being in the same "server bundle" doesn't mean they use the same caching model. The bundler layer is just where code lives; the execution context determines which caching APIs are available.
-
----
-
-## 1. The Fundamental Paradigm Shift
-
-### Test Evidence: Default Behavior Change
-
-**Test Source**: `test/e2e/app-dir/cache-components/cache-components.params.test.ts`
-
-```typescript
-// OBSERVED BEHAVIOR IN TESTS:
-
-// Route: /params/semantics/one/build/layout-access/server
-// With generateStaticParams returning { highcard: 'build' }
-
-// Development Mode:
-// - layout: 'at runtime'
-// - page: 'at runtime'
-// - ALL params: 'at runtime'
-
-// Production Mode (Build):
-// - layout: 'at buildtime' (from generateStaticParams)
-// - page: 'at buildtime' (from generateStaticParams)
-// - params.lowcard: 'one' (value present)
-// - params.highcard: 'build' (value present)
-
-// Route: /params/semantics/one/run/layout-access/server
-// With generateStaticParams NOT returning 'run' for highcard
-
-// Development Mode:
-// - layout: 'at runtime'
-// - page: 'at runtime'
-
-// Production Mode (Build):
-// - layout: 'at buildtime' (static shell)
-// - Suspense fallback: 'loading highcard children' (shown!)
-// - page: 'at runtime' (dynamic hole!)
-// - params.lowcard: 'one'
-// - params.highcard: 'run'
-```
-
-### Key Insight from Tests:
-
-**With cacheComponents enabled + generateStaticParams:**
-
-- Params from generateStaticParams → Component renders at buildtime
-- Params NOT in generateStaticParams → Component renders at runtime with PPR shell
-
-This is THE CORE DIFFERENCE that AI agents must understand.
-
----
-
-## 2. How cacheComponents Changes Everything
-
-### Test-Proven Behaviors
-
-**Test Source**: Multiple test files
-
-#### Behavior 1: Route Segment Configs Are Incompatible with Cache Components
-
-**⚠️ NOTE**: These configs work fine in Next.js 16 WITHOUT cacheComponents. They're only forbidden when `experimental.cacheComponents: true` is enabled.
-
-```typescript
-// ❌ BUILD ERROR (when cacheComponents is enabled):
-export const dynamic = "force-static"
-export const revalidate = 60
-export const fetchCache = "force-cache"
-export const dynamicParams = false
-export const runtime = "edge" // If incompatible
-
-// Error message from test:
-// "Route segment config "revalidate" is not compatible with
-// `nextConfig.experimental.cacheComponents`. Please remove it."
-
-// ✅ These work fine in Next.js 16 if cacheComponents is NOT enabled
-```
-
-**Test Source**: `test/e2e/app-dir/cache-components-segment-configs/`
-
-#### Behavior 2: Default is Fully Dynamic
-
-```typescript
-// Test shows: Without 'use cache', pages render fresh every request
-// Test Source: test/e2e/app-dir/use-cache/app/(dynamic)/page.tsx
-
-async function getCachedRandom(x: number, children: React.ReactNode) {
- 'use cache'
- return {
- x,
- y: Math.random(), // This value STAYS SAME across requests
- z: ,
- r: children,
- }
-}
-
-// Test assertion proves:
-// - Two navigations to ?n=1 return SAME random value
-// - Navigation to ?n=2 returns DIFFERENT random value
-// - Children prop (non-serializable) doesn't affect cache key
-```
-
-#### Behavior 3: Private Cache Can Access Cookies/Headers
-
-```typescript
-// Test Source: test/e2e/app-dir/use-cache-private/app/cookies/page.tsx
-
-async function Private() {
- 'use cache: private'
- cacheLife({ stale: 420 })
-
- const cookie = (await cookies()).get('test-cookie') // ✅ ALLOWED!
-
- const { headers } = await fetch('https://...', {
- headers: { 'x-test-cookie': cookie?.value ?? '' }
- }).then(res => res.json())
-
- return
test-cookie: {headers['x-test-cookie']}
-}
-
-// Test assertions:
-// - Cookie value 'testValue' → display shows 'testValue'
-// - Update cookie to 'foo' → display shows 'foo'
-// - Private cache MUST be wrapped in Suspense
-```
-
----
-
-## 3. The Three Types of Rendering
-
-### From Test Behavioral Patterns
-
-```
-┌─────────────────────────────────────────────────────┐
-│ Type 1: PUBLIC CACHE ('use cache') │
-│ ─────────────────────────────────────────────────── │
-│ Included in: ✅ Static prerender │
-│ Included in: ✅ Runtime prefetch │
-│ Can access: ❌ cookies/headers/searchParams │
-│ Must wrap in Suspense: ❌ No │
-│ Cache scope: Shared across ALL users │
-│ Test: test/e2e/app-dir/use-cache/app/*/cache-tag │
-└─────────────────────────────────────────────────────┘
-
-┌─────────────────────────────────────────────────────┐
-│ Type 2: PRIVATE CACHE ('use cache: private') │
-│ ─────────────────────────────────────────────────── │
-│ Included in: ❌ Static prerender (excluded!) │
-│ Included in: ✅ Runtime prefetch (if stale >= 30s) │
-│ Can access: ✅ cookies/headers/searchParams/params │
-│ Must wrap in Suspense: ✅ YES (build error if not) │
-│ Cache scope: Per-user │
-│ Test: test/e2e/app-dir/use-cache-private/ │
-└─────────────────────────────────────────────────────┘
-
-┌─────────────────────────────────────────────────────┐
-│ Type 3: FULLY DYNAMIC (no cache directive) │
-│ ─────────────────────────────────────────────────── │
-│ Included in: ❌ Static prerender (excluded!) │
-│ Included in: ❌ Runtime prefetch (excluded!) │
-│ Can access: ✅ All APIs │
-│ Must wrap in Suspense: Recommended for PPR │
-│ Cache scope: No caching │
-│ Test: test/e2e/app-dir/segment-cache/prefetch-* │
-└─────────────────────────────────────────────────────┘
-```
-
----
diff --git a/src/resources/(cache-components)/02-public-caches.md b/src/resources/(cache-components)/02-public-caches.md
deleted file mode 100644
index 6d12676..0000000
--- a/src/resources/(cache-components)/02-public-caches.md
+++ /dev/null
@@ -1,167 +0,0 @@
-## 4. Public Cache Mechanics
-
-### Pattern 1: Function-Level 'use cache'
-
-```typescript
-// Test Source: test/e2e/app-dir/use-cache/app/(partially-static)/cache-life/page.tsx
-
-import { cacheLife } from 'next/cache'
-
-async function getCachedRandom() {
- 'use cache'
- cacheLife('frequent')
- return Math.random()
-}
-
-export default async function Page() {
- const x = await getCachedRandom()
- return
{x}
-}
-
-// Test Behavior:
-// - Initial load: x = 0.12345
-// - Refresh: x = 0.12345 (SAME VALUE - cached!)
-// - Different arg: Different cache entry
-//
-// NOTE: 'use cache' is at FUNCTION level, not file level
-```
-
-### Pattern 2: Component-Level 'use cache'
-
-```typescript
-// Test Source: test/e2e/app-dir/use-cache/app/(partially-static)/cache-tag/page.tsx
-
-async function getCachedWithTag({ tag }: { tag: string }) {
- 'use cache'
- cacheTag(tag, 'c')
-
- const response = await fetch('https://...')
- return [Math.random(), await response.text()]
-}
-
-export default async function Page() {
- const a = await getCachedWithTag({ tag: 'a' })
- const b = await getCachedWithTag({ tag: 'b' })
-
- return (
-
- {values.r}
- >
- )
-}
-
-// TEST ASSERTIONS PROVE:
-// 1. ?n=1 first visit: y = 0.123
-// 2. ?n=2 visit: y = 0.456 (different cache key!)
-// 3. ?n=1 second visit: y = 0.123 (SAME! cached by 'x' param)
-// 4. values.r renders fresh random number each time
-// BUT doesn't invalidate the cache (non-serializable)
-```
-
-### Cache Key Formula (from tests):
-
-```
-Cache Key = hash(
- buildId +
- functionId +
- serializableArgs // Only these matter!
-)
-
-Non-serializable args (children, JSX, functions):
-- Treated as opaque references
-- NOT part of cache key
-- Re-evaluated each render
-- Can be different without invalidating cache
-```
-
----
-
-## 6. Non-Serializable Props Pattern
-
-### Test Pattern: Children Props
-
-**Test Source**: `test/e2e/app-dir/use-cache/app/(dynamic)/page.tsx`
-
-```typescript
-// THE PATTERN TESTS PROVE:
-
-async function getCachedRandom(x: number, children: React.ReactNode) {
- 'use cache'
- return {
- x,
- y: Math.random(),
- r: children, // Non-serializable
- }
-}
-
-// When called with:
-getCachedRandom(
- 1,
-
rnd{Math.random()}
// Different every time
-)
-
-// Behavior:
-// - Cache hits on x=1 even though children is different
-// - children re-renders with new random value
-// - y stays cached (same random value)
-```
-
-### Critical Rule from Tests:
-
-**Serializable props** (numbers, strings, plain objects):
-
-- Become part of cache key
-- Must match for cache hit
-
-**Non-serializable props** (JSX, functions, class instances):
-
-- Do NOT become part of cache key
-- Passed through as references
-- Re-evaluated on each render
-- Can change without cache miss
-
----
diff --git a/src/resources/(cache-components)/03-private-caches.md b/src/resources/(cache-components)/03-private-caches.md
deleted file mode 100644
index b50dba1..0000000
--- a/src/resources/(cache-components)/03-private-caches.md
+++ /dev/null
@@ -1,101 +0,0 @@
-## 8. Private Cache Mechanics
-
-### Pattern from Tests: Private Cache Structure
-
-**Test Source**: `test/e2e/app-dir/use-cache-private/app/cookies/page.tsx`
-
-```typescript
-// THE EXACT PATTERN FROM TESTS:
-
-export default function Page() {
- return (
- Loading...
-}
-
-// Pattern: Pass cookie Promise to public cache
-// This allows public cache while still accessing cookies!
-```
-
----
-
-## 13. What Gets Included in Runtime Prefetch
-
-### Test-Driven Inclusion Rules
-
-**Test Source**: `test/e2e/app-dir/segment-cache/prefetch-runtime/prefetch-runtime.test.ts`
-
-#### Rule 1: Includes All Public Caches
-
-```typescript
-// Always included in runtime prefetch:
-async function PublicCached() {
- 'use cache'
- cacheLife('hours') // Any duration
- return
Content
-}
-```
-
-#### Rule 2: Includes Private Caches (if stale >= 30s)
-
-```typescript
-// Test lines 752-829:
-
-// ✅ INCLUDED in runtime prefetch:
-async function IncludedPrivate() {
- 'use cache: private'
- cacheLife('seconds') // stale = 30s (exactly at threshold)
- return
- )
-}
-
-// TEST BEHAVIOR:
-// - ?q=foo → displays 'foo'
-// - Navigate to ?q=bar → displays 'bar'
-// - Each searchParams value gets its own cache entry
-```
-
----
-
-## 21. connection() Deep Dive
-
-### ⭐ When to Use connection()
-
-**Official Purpose** ([Next.js Docs](https://nextjs.org/docs/app/api-reference/functions/connection)):
-> The `connection()` function allows you to indicate rendering should wait for an incoming user request before continuing.
->
-> It's useful when a component doesn't use Dynamic APIs, but you want it to be dynamically rendered at runtime and not statically rendered at build time. This usually occurs when you access external information that you intentionally want to change the result of a render, such as `Math.random()` or `new Date()`.
-
-**Use connection() to explicitly mark a component as dynamic:**
-
-```typescript
-import { connection } from 'next/server'
-
-export default async function Page() {
- await connection() // ✅ Marks component as dynamic
-
- const currentYear = new Date().getFullYear()
- const random = Math.random()
-
- return
{currentYear} - {random}
-}
-```
-
-**Key Points:**
-- `connection()` replaces `unstable_noStore` (stabilized in Next.js 15)
-- Only necessary when dynamic rendering is required and common Dynamic APIs (`headers()`, `cookies()`, `draftMode()`) are NOT used
-- Perfect for `Math.random()`, `Date.now()`, or any time-based/random rendering
-
-### ❌ Anti-Pattern: Empty headers()/cookies() Calls
-
-**WRONG - Do NOT do this:**
-
-```typescript
-import { headers } from 'next/headers'
-
-export default async function Footer() {
- await headers() // ❌ BAD - Using as side effect just to mark dynamic
-
- const currentYear = new Date().getFullYear()
- return
-}
-```
-
-**Why it's bad:**
-- Not semantically correct (you're not using headers)
-- Misleading to other developers
-- Future readers think you need headers data
-
-**RIGHT - Use connection() instead:**
-
-```typescript
-import { connection } from 'next/server'
-
-export default async function Footer() {
- await connection() // ✅ GOOD - Clear intent to mark as dynamic
-
- const currentYear = new Date().getFullYear()
- return
-}
-```
-
-### Test Pattern: Math.random() and Date.now()
-
-**Test Source**: `test/development/app-dir/cache-components-warnings/` (disabled but shows pattern)
-
-```typescript
-// Pattern: Math.random() without connection()
-
-export default async function Page() {
- const random = Math.random() // ⚠️ Warning in dev
- return
{random}
-}
-
-// Dev warning:
-// 'Route "/path" used `Math.random()` outside of `"use cache"`
-// and without explicitly calling `await connection()` beforehand.'
-
-// ✅ CORRECT PATTERN:
-
-export default async function Page() {
- await connection()
- const random = Math.random() // ✅ No warning
- return
{random}
-}
-```
-
-### Test Pattern: connection() Creates Dynamic Hole
-
-**Test Source**: `test/e2e/app-dir/segment-cache/prefetch-runtime/` (Dynamic components)
-
-```typescript
-async function Dynamic() {
- await connection()
- // Everything after connection() is excluded from prefetch
- return
Dynamic content
-}
-
-export default function Page() {
- return (
- Loading...}>
-
-
- )
-}
-
-// Runtime prefetch test assertion:
-// - Prefetch response: block: 'reject' for "Dynamic content"
-// - Navigation: "Dynamic content" streams in
-```
-
----
diff --git a/src/resources/(cache-components)/07-cache-invalidation.md b/src/resources/(cache-components)/07-cache-invalidation.md
deleted file mode 100644
index 1549891..0000000
--- a/src/resources/(cache-components)/07-cache-invalidation.md
+++ /dev/null
@@ -1,110 +0,0 @@
-## 22. updateTag() - Read-Your-Own-Writes
-
-### Test Pattern: Immediate Cache Invalidation
-
-**Test Source**: `test/e2e/app-dir/use-cache/app/(partially-static)/cache-tag/buttons.tsx` + test assertions
-
-```typescript
-"use server"
-
-import { updateTag, revalidateTag } from "next/cache"
-
-export async function revalidateA() {
- revalidateTag("a")
-}
-
-export async function revalidateB() {
- revalidateTag("b")
-}
-
-export async function revalidateC() {
- revalidateTag("c")
-}
-
-// Page uses getCachedWithTag({ tag: 'a' }) and ({ tag: 'b' })
-// Both also use cacheTag(tag, 'c')
-
-// TEST BEHAVIOR (lines 223-315):
-// Initial: valueA = 0.123, valueB = 0.456
-//
-// Call revalidateA():
-// - valueA changes to 0.789
-// - valueB stays 0.456
-//
-// Call revalidateC():
-// - valueA changes (has tag 'c')
-// - valueB changes (has tag 'c')
-//
-// This proves: Tags work as expected for granular invalidation
-```
-
----
-
-## 23. revalidateTag(tag, profile) - New Signature
-
-### Test Pattern: Profile Parameter
-
-**Test Source**: Documentation and recent commits show new signature
-
-```typescript
-'use server'
-
-import { revalidateTag } from 'next/cache'
-
-// ✅ NEW RECOMMENDED PATTERN:
-export async function updateProductList() {
- await db.products.update(...)
- revalidateTag('products', 'max') // Stale-while-revalidate
-}
-
-// ❌ DEPRECATED (but still works):
-export async function oldPattern() {
- revalidateTag('products') // No profile = legacy behavior
-}
-
-// Test from use-cache.test.ts (lines 223-314):
-// - Revalidate specific tags
-// - Cache updates happen async
-// - Stale content served while revalidating (with 'max' profile)
-```
-
----
-
-## 24. refresh() - Client Router Cache
-
-### Test Pattern: In-Place Page Update
-
-**Test Source**: `test/e2e/app-dir/use-cache/app/(partially-static)/form/page.tsx`
-
-```typescript
-import { updateTag, cacheTag } from 'next/cache'
-
-async function refresh() {
- 'use server'
- updateTag('home')
-}
-
-export default async function Page() {
- 'use cache'
- cacheTag('home')
-
- return (
-
- )
-}
-
-// ACTUAL TEST BEHAVIOR:
-// 1. Initial load: timestamp = "2024-01-01T12:00:00.000Z"
-// 2. Click refresh button:
-// - updateTag('home') called (invalidates cache)
-// - Page re-renders with new timestamp
-// 3. New timestamp displayed (cache was invalidated)
-//
-// NOTE: This uses updateTag(), not revalidateTag()
-// refresh() here is the server action name, not the next/cache function
-```
-
----
diff --git a/src/resources/(cache-components)/08-advanced-patterns.md b/src/resources/(cache-components)/08-advanced-patterns.md
deleted file mode 100644
index e01380c..0000000
--- a/src/resources/(cache-components)/08-advanced-patterns.md
+++ /dev/null
@@ -1,156 +0,0 @@
-## 26. cacheLife() Profiles and Custom Config
-
-### Test Pattern: Custom Profile
-
-**Test Source**: `test/e2e/app-dir/use-cache/next.config.js` + test assertions
-
-```typescript
-// next.config.js
-const nextConfig = {
- experimental: {
- cacheComponents: true,
- cacheLife: {
- frequent: {
- stale: 19,
- revalidate: 100,
- expire: 300,
- },
- },
- },
-}
-
-// page.tsx
-'use cache'
-import { cacheLife } from 'next/cache'
-
-export default async function Page() {
- cacheLife('frequent') // Uses custom profile
- return