nextjs-agent-mcp
Allows AI agents to inspect and interact with a running Next.js application, including reading routes, capturing errors, and controlling the browser via DOM events.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@nextjs-agent-mcpList all App Router routes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
nextjs-agent-mcp
An MCP server that lets an AI agent drive and inspect a running Next.js app — no Playwright, no headless Chromium. It works two ways:
Mode A — Headless introspection. Reads the App Router route map and captures structured dev-server errors straight from the framework. No browser required.
Mode B — In-page bridge. A tiny dev-only
<AgentBridge/>component you mount in your app connects to a small broker and the agent drives the page via real DOM events — click, fill, navigate, snapshot the page as a structured model, walk the React component tree, capture network calls, read/write storage, run JS, screenshot. A floating HUD lets you watch it work (status bar with typed narration, traveling cursor, spotlight).
Connection model (broker + claim)
Multiple agents and multiple tabs coexist cleanly:
A single broker owns the WS port (default
7333). The first MCP to start spawns it; others connect to it. (Fixes the old "two MCPs on different ports, agent drives the wrong tab" problem.)Each MCP registers as an agent with a unique id + name.
Each browser tab connects but stays inert ("unclaimed") — no agent controls it until one claims it. On claim, the tab's HUD shows the controlling agent's name + intent.
An agent controls one tab at a time (the one it claimed) and cannot touch a tab owned by another agent.
claim_tabbinds a free tab — or opens a new one if none are free.
Agent loop: claim_tab({intent}) → snapshot/find → fill/fill_form/click →
wait_for → network_calls → release_tab.
Install
git clone git@github.com:zohaib3249/nextjs-agent-mcp.git
cd nextjs-agent-mcp
npm install # builds the bridge automatically (prepare script)Run the MCP server
node src/index.js --project /path/to/your-next-app --ws-port 7333--project— path to the Next.js app you want to test (used by Mode A to read routes/errors).--ws-port— port the in-page bridge connects to (default7333). Must matchNEXT_PUBLIC_AGENT_BRIDGE_PORTin your app if you change it.
Wire it into your agent (mcp.json)
{
"mcpServers": {
"nextjs-agent": {
"command": "node",
"args": [
"/abs/path/to/nextjs-agent-mcp/src/index.js",
"--project", "/path/to/your-next-app",
"--ws-port", "7333"
],
"cwd": "/abs/path/to/nextjs-agent-mcp",
"transport": "stdio"
}
}
}Use absolute paths for
command's script arg andcwd— the launcher may not share your shell's working directory orPATH. Don't define the same server twice (two instances collide on the WS port; the second now exits with a clear message).
Mode B: mount the bridge in your app
Add one import + one dev-gated line to your root layout:
// app/layout.tsx (or src/app/layout.tsx)
import { AgentBridge } from 'nextjs-agent-mcp/bridge';
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
{process.env.NODE_ENV === 'development' && <AgentBridge />}
</body>
</html>
);
}In dev you'll see a floating nextjs-agent HUD (bottom-right): connection status, the tab's id,
and a live feed of agent actions; the targeted element pulses when clicked/filled. It renders
nothing in production. The bridge port defaults to 7333; override via
NEXT_PUBLIC_AGENT_BRIDGE_PORT (must match the MCP's --ws-port).
Installing the package into your app
The bridge ships as pre-built JS (bridge/index.js, React kept external, 'use client'
preserved). Install from npm and add it to transpilePackages:
npm install nextjs-agent-mcp// next.config.js
const nextConfig = { transpilePackages: ['nextjs-agent-mcp'] };Local development of the package itself: a
file:install symlinks intonode_modules, and Turbopack refuses to follow a symlink that points outside your app's filesystem root ("Symlink … points out of the filesystem root"). If you hit that, keep the package inside your app's repo root, or install a packed tarball (npm pack→npm install ./that.tgz) so it lands as real files. Published npm installs are unaffected.
Tools
Mode A — headless (no browser)
Tool | What it does |
| All App Router routes from the filesystem; flags locale-prefixed routes. |
| Spawns |
| Parse an existing dev-server log file instead of spawning. |
| Structured compile / module-not-found / runtime / hydration errors ( |
| Stop the spawned dev server. |
Mode B — in-page bridge (claim a tab first)
Tool | What it does |
| This agent's id/name, broker connection, and the tab it controls ( |
| All connected tabs: |
| Call first. Bind a free tab (or open one if none free). |
| Unbind the tab → back to "unclaimed" for a human/other agent. |
| This agent's broker status: connected?, port, bound tab, all tabs + owners. |
| Open a new browser tab (then claim it). |
| Navigate the bound tab to a URL / reload it. |
| Real pointer+click / type-aware input: text, |
| Fill MANY fields in one call ( |
| Structured page model: |
| Lightweight "where am I": url, pathname, locale, title, page heading. |
| Page landmark map: header / nav / sidebars / sections / tabs / footer / open dialogs. |
| Search the page for fields/actions/components matching a query. |
| Walk the React fiber tree: component names, nesting, hook shape. |
| Which components render a given element (owner chain). |
| Force the component owning a selector to re-render. |
| Poll until a selector appears or text is present. |
| Captured fetch/XHR (method, status, type, timing, capped bodies) + resources; filter by |
| Read/modify |
| Inspect or clear Cache Storage (PWA/Service Worker). |
| All console output + uncaught errors / unhandled rejections ( |
| Narrate intent in the on-page status bar (kinds: 💭 thinking · ⌘ code · ⇅ net · ✦ action). |
| Run arbitrary JS in the page and return the serialized result (dev-only). |
| In-page PNG capture via html2canvas (best-effort; needs network to load html2canvas). |
Every Mode-B tool acts on the agent's bound tab and accepts an optional message (typed into
the on-page status bar). Claim a tab with claim_tab before using them.
A typical agent loop
claim_tab({intent}) → page_context/overview → snapshot (or find) → fill_form /
fill / click → wait_for → network_calls / console_messages to verify → release_tab.
Errors without letting the MCP own your server
If you run your own dev server, redirect its output and attach:
npm run dev > /tmp/dev.log 2>&1
# then, via the agent: attach_log { "path": "/tmp/dev.log" }Test
npm test # broker isolation test (agents+tabs, no browser)
node test/smoke.mjs /path/to/your-next-app # boots over stdio, prints route_map (Mode A)Notes & caveats
The bridge needs a real browser tab open — "no headless browser", not "no browser".
React 19: fibers expose component names + hook shape, but not source file/line or hook names (
_debugSourcewas removed);screenshothas no headless fallback.evaland the bridge are dev-only — gate the<AgentBridge/>mount onNODE_ENV.
License
MIT
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/zohaib3249/nextjs-agent-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server