Connecting Puppeteer MCP to Cursor Agent Mode
Cursor is an AI-native IDE built on VS Code, with a built-in agent mode that supports MCP servers through a per-project .cursor/mcp.json configuration file. This setup is ideal for frontend debugging because the agent can navigate the browser, capture screenshots, and evaluate DOM state — all while you have the relevant source files open in the same editor. The feedback loop between browser observation and code editing becomes nearly instantaneous.
Create the MCP configuration file at .cursor/mcp.json in your project root:
{
"mcpServers": {
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"]
}
}
}
This configuration is project-scoped, meaning it applies only to the workspace where the file exists. For a global configuration that applies across all Cursor workspaces, place the file at ~/.cursor/mcp.json.
Commit .cursor/mcp.json to your repository so every team member automatically gets Puppeteer MCP available when they open the project in Cursor — no per-developer setup required.
For headful mode (watch the browser while the agent operates it, useful during initial setup and prompt development):
{
"mcpServers": {
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"],
"env": {
"PUPPETEER_HEADLESS": "false"
}
}
}
}
For CI runners or Docker dev containers:
{
"mcpServers": {
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"],
"env": {
"PUPPETEER_LAUNCH_ARGS": "--no-sandbox --disable-setuid-sandbox"
}
}
}
}
Enable Cursor's agent mode with the keyboard shortcut Cmd+Shift+I (macOS) or Ctrl+Shift+I (Windows/Linux), or click the Cursor icon in the sidebar and switch to "Agent" mode. The agent will have access to both your open files and the Puppeteer MCP tools.
Verify the connection by asking the agent in the Cursor chat:
What MCP tools do you have available? List them.
Tips
- Commit.cursor/mcp.jsonto version control so team members get Puppeteer MCP automatically when they clone the project and open it in Cursor — this eliminates "works on my machine" MCP setup differences.
- Cursor's agent mode and chat mode behave differently for MCP tool use — use agent mode (not chat mode) when you want the agent to autonomously execute multi-step Puppeteer automation sequences.
- If the MCP server fails to start, check Cursor's MCP logs via the Output panel (View > Output > Cursor MCP) to see the Puppeteer server's startup error messages.
- For monorepos, you can place.cursor/mcp.jsonin a subdirectory to scope Puppeteer MCP only to the frontend package, keeping the agent context focused on the relevant part of the codebase.
Debugging Frontend Bugs from Cursor: Agent Sees the Browser, You See the Code
The defining advantage of Puppeteer MCP in Cursor is co-location: the agent sees the live browser state while you look at the source code in the same IDE window. When the agent reports a DOM anomaly — a missing attribute, an unexpected CSS class, a failed event handler — you can immediately navigate to the relevant component file without switching applications.
A bug investigation workflow in Cursor agent mode:
I have @src/components/Dropdown.tsx open. There's a bug where the dropdown options list
doesn't close when the user clicks outside of it. The component is rendered at
http://localhost:3000/components/demo.
Please:
1. Navigate to that URL and screenshot the initial state
2. Click the dropdown toggle (selector: '[data-testid="dropdown-toggle"]')
3. Screenshot the open state
4. Click anywhere outside the dropdown (use the page body or header)
5. Screenshot the result — does the dropdown close?
6. If it doesn't close, evaluate: document.querySelector('[data-testid="dropdown-menu"]')
and report its display/visibility state after the outside click
7. Also check for any console errors related to 'click outside' or event listeners
Based on your findings, look at the @src/components/Dropdown.tsx file and identify
the likely bug location.
The @ file references let the Cursor agent read source files in the same prompt — so the agent can correlate the DOM behavior it observed in the browser with the actual component code, and point directly to the problematic event handler or useEffect.
For diagnosing a state management bug visible in the UI:
The cart badge count at the top of http://localhost:3000 doesn't update in real time
when items are added. Looking at @src/store/cartSlice.ts and @src/components/CartBadge.tsx,
please:
1. Navigate to http://localhost:3000/products
2. Screenshot the header (to capture the initial cart badge state)
3. Evaluate: document.querySelector('.cart-badge')?.textContent to get the current count
4. Click "Add to Cart" on the first product
5. Immediately evaluate the cart badge textContent again
6. Screenshot the header again
7. Wait 2 seconds and evaluate the cart badge textContent one more time
Based on the browser observations and the code in @src/store/cartSlice.ts and
@src/components/CartBadge.tsx, diagnose whether this is a Redux state update issue,
a component subscription issue, or a rendering delay.
Tips
- Use@filenamereferences in Cursor agent prompts to give the agent direct access to the relevant source files alongside the browser findings — this dramatically improves the quality of root cause analysis.
- For component-level bugs, open the component file in the editor before starting the agent session so it appears in the agent's context automatically.
- When the agent identifies a bug in a specific function, ask it to "show me the fix and apply it to the file" directly in the same Cursor agent session — the agent can edit the file, then re-run the browser verification in the same session.
- Use Cursor's diff view to review changes the agent proposes before accepting them, especially for fixes derived from browser behavior rather than pure code analysis.
Running UI Automation and QA Exploration Tests Directly from Cursor
Cursor's agent mode is well-suited for QA sessions where you want the test findings to directly feed into code changes. The agent can run a QA scenario, find a failure, point to the relevant source file, and propose a fix — without you leaving the IDE.
A QA-to-fix cycle in a single Cursor session:
Run QA checks on the form at http://localhost:3000/contact. Test these scenarios:
1. Submit the form with all fields empty — screenshot the validation errors
2. Enter an invalid email format and submit — screenshot
3. Enter valid data and submit — screenshot the success state
After each scenario, check for console errors. If any validation fails silently
(form submits without showing an error, or an error shows but then disappears),
look at @src/components/ContactForm.tsx and identify the issue in the validation
logic. Propose a fix inline.
For a full feature area QA run with code context:
I need a QA check on the authentication flow. The relevant files are
@src/pages/Login.tsx, @src/pages/Register.tsx, and @src/hooks/useAuth.ts.
Please test:
1. Login with valid credentials at http://localhost:3000/login
- Credentials: email "[email protected]", password "Test1234!"
- Verify redirect to /dashboard after login
- Verify the user's name appears in the header
2. Login with invalid credentials
- Verify an error message appears (not a console error — a visible UI error)
3. Register a new account at http://localhost:3000/register
- Use email "newuser+$(date)@example.com" pattern, password "NewPass123!"
- Verify success state
Cross-reference any failures with the relevant source files and report
both the browser behavior and the likely code location causing each issue.
For snapshot-style regression testing before a deploy:
Before I deploy the current build, run a visual smoke test on these 5 pages:
1. http://localhost:3000/ - homepage
2. http://localhost:3000/products - product listing
3. http://localhost:3000/about - about page
4. http://localhost:3000/contact - contact page
5. http://localhost:3000/login - login page
For each page:
- Screenshot at 1280x800 viewport
- Check for console errors
- Check that the page title is set (evaluate: document.title)
- Visually confirm the page is not blank or showing an error screen
Produce a smoke test report: each page with pass/fail status, screenshot,
and any console errors found.
Tips
- For QA flows that surface bugs, stay in the same Cursor agent session to fix them — the agent has the full context of what it observed in the browser and can apply a more targeted fix than starting a new session.
- Use Cursor's inline diff feature when the agent proposes code fixes: review the diff before accepting to ensure the fix addresses the browser-observed behavior rather than just modifying code speculatively.
- For large QA runs, save the agent's output as a markdown file in your project'sqa/directory — this gives you a versioned history of pre-deploy smoke tests.
- Combine Puppeteer MCP with Cursor's codebase search: if the agent finds an unexpected CSS class in the DOM, ask it to search the codebase for where that class is assigned to understand the full state logic.
Known Limitations and Workarounds for Puppeteer MCP in Cursor
Being aware of these limitations helps you structure your Cursor agent prompts to avoid hitting dead ends mid-session.
Cursor agent mode token limits. Long Puppeteer automation sessions that involve many screenshots and large DOM evaluations can approach Cursor's context window limits. The screenshots themselves are passed as base64-encoded images, which are token-heavy. Mitigation: request screenshots only at key checkpoints rather than after every action, and ask the agent to summarize DOM evaluation output rather than returning raw full-page HTML.
No direct browser network interception. Puppeteer MCP does not expose Puppeteer's page.on('request') or page.on('response') event listeners. You cannot intercept, mock, or inspect individual network requests at the protocol level through MCP. Workaround: use puppeteer_evaluate to call fetch() directly from the page context to test API endpoints, and use performance.getEntriesByType('resource') to inspect which resources loaded and their timing.
Evaluate: JSON.stringify(performance.getEntriesByType('resource').filter(r =>
r.name.includes('/api/')).map(r => ({ url: r.name, duration: r.duration,
status: r.responseStatus })))
Input handling on custom UI components. puppeteer_type works reliably on native <input> elements. Custom components built with contenteditable, virtual input libraries, or React-controlled inputs that intercept synthetic events may not respond to puppeteer_type as expected. Workaround: use puppeteer_evaluate to set the value programmatically and dispatch an input event:
const el = document.querySelector('[data-testid="rich-text-input"]');
el.value = 'test content';
el.dispatchEvent(new Event('input', { bubbles: true }));
Cursor agent restarts on MCP server crash. If the Puppeteer MCP server crashes mid-session (Chromium OOM, process signal), the Cursor agent loses access to the tools. The session must be restarted. Mitigation: for long QA runs, break them into shorter focused sessions. Check Chromium memory usage if crashes are frequent — reduce concurrent page operations.
File system access for screenshots. Puppeteer MCP returns screenshots in-session to the agent — it does not save them to disk automatically. If you need to archive screenshots, ask the agent to encode them to base64 and write them to files using Cursor's built-in file write capability, or use a custom MCP wrapper that saves screenshots to a directory.
Tips
- Break long QA sessions into focused 5–10 step prompts rather than one 30-step prompt — this keeps Cursor's context usage manageable and makes it easier to review results incrementally.
- For custom input components that don't respond topuppeteer_type, keep a library ofpuppeteer_evaluateworkarounds in a project.cursorrulesor agent instruction file that Cursor loads automatically.
- Monitor Chrome Task Manager (within the headful browser, if enabled) for memory usage during long automation sessions — Puppeteer keeps the Chromium process alive for the duration of the MCP session, and memory-heavy pages can accumulate.
- If you need screenshot archiving, write a small wrapper script that runs the Puppeteer MCP server and intercepts screenshot responses to save them to ascreenshots/directory — then reference this wrapper in your.cursor/mcp.jsoninstead of the rawnpxcommand.