Guide · Debugging
Debugging MCP servers in Cursor
Cursor is one of the most widely used MCP clients for developer tooling — it runs MCP servers as part of its AI assistant context and lets the model call tools directly from the chat. When an MCP server doesn't work correctly in Cursor, the failure can look like a missing tool, a silent tool call failure, or a confusing AI response. This guide covers the Cursor-specific debugging workflow: where to find the MCP logs, how to read them, and how to replay a failing scenario.
TL;DR
Open the Output panel (View → Output) and select the MCP channel from the dropdown to see raw protocol logs. Two distinct failures: "tool not appearing" = initialization failure (check the log before tools/list); "tool call failing" = handler error (check the tools/call response in the log). Configuration lives in .cursor/mcp.json for project-level settings or ~/.cursor/mcp.json for global settings.
Finding the MCP Output panel
Cursor logs all MCP protocol traffic to an Output channel. To access it:
- Open the Output panel: View → Output (or Ctrl+Shift+U / Cmd+Shift+U)
- Click the dropdown in the Output panel header (it shows the current channel name)
- Look for channels named MCP or MCP: <server-name>
The MCP channel shows every JSON-RPC message Cursor sent to and received from each server: the initialize handshake, tools/list response (the tool manifest), and each tools/call request/response pair. This is the primary debugging surface for MCP server issues in Cursor.
If you don't see an MCP channel in the dropdown, the server hasn't connected yet. Check that the server is configured correctly in mcp.json and restart Cursor. The MCP channel only appears after Cursor attempts to connect to at least one MCP server.
Configuring a local development server
Cursor reads MCP server configuration from two files:
.cursor/mcp.json— project-level (applies only in the current workspace)~/.cursor/mcp.json— global (applies in all workspaces)
For development, use the project-level file so your dev server config doesn't affect other projects:
// .cursor/mcp.json — local development server
{
"mcpServers": {
"my-server-dev": {
"command": "node",
"args": ["--inspect=9229", "/absolute/path/to/my-server/dist/index.js"],
"env": {
"DATABASE_URL": "postgres://localhost/mydb",
"NODE_ENV": "development",
"LOG_LEVEL": "debug",
"DEBUG": "mcp:*"
}
}
}
}
Key rules for Cursor's mcp.json:
- Use absolute paths in
args— Cursor doesn't inherit your shell's working directory - Add all required environment variables in
env— Cursor spawns the server in a clean environment - Restart Cursor (or reload the window with Cmd+Shift+P → "Reload Window") after changing
mcp.json - Adding
--inspect=9229to the node args enables the VS Code debugger to attach to the running server
Two failure modes: initialization vs. handler
When an MCP server doesn't work in Cursor, the failure always falls into one of two categories. Identifying which one immediately tells you where to look:
| Symptom | Failure type | Where to look |
|---|---|---|
| Tool doesn't appear in Cursor's tool list; model doesn't know the tool exists | Initialization failure — server failed before or during tools/list | MCP Output panel: look for errors before the tools/list response; check the server stderr log |
| Tool appears in Cursor's list but calls fail; model says "tool returned an error" | Handler failure — server registered the tool but the handler threw or returned isError: true | MCP Output panel: find the tools/call response and read the error message in content[0].text |
| Tool appears but model never uses it | Description quality issue — tool description doesn't signal when to use it | Improve the tool's description field to be more action-oriented |
| Tool call succeeds but model misinterprets the result | Result format issue — tool result text isn't parseable in context | Check that the result is valid JSON or clearly structured text; avoid binary or encoded output |
Reading the MCP Output panel log
The MCP Output panel shows JSON-RPC messages in chronological order. Here's how to read them to diagnose a failing tool:
// A successful tool call sequence in the MCP Output panel:
// 1. initialize handshake (on connection)
→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05",...}}
← {"jsonrpc":"2.0","id":1,"result":{"serverInfo":{"name":"my-server","version":"1.0.0"},...}}
→ {"jsonrpc":"2.0","method":"notifications/initialized"}
// 2. tools/list (Cursor fetches tool manifest)
→ {"jsonrpc":"2.0","id":2,"method":"tools/list"}
← {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"search_docs","description":"...","inputSchema":{...}}]}}
// 3. tools/call (model asks Cursor to call a tool)
→ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_docs","arguments":{"query":"MCP"}}}
← {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"[{\"id\":1,\"title\":\"...\""}]}}
// A failing tool call:
→ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"search_docs","arguments":{"query":""}}}
← {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"Error: query must not be empty"}],"isError":true}}
Key things to look for:
- If the
tools/listresponse has an emptytoolsarray → tool registration failed (not the handler) - If a
tools/callresponse has"isError":true→ handler returned an error; readcontent[0].textfor the message - If a
tools/callresponse is a JSON-RPC error ("error"key, not"result") → handler threw an uncaught exception - If there's no
tools/listmessage at all → server crashed during initialization; check stderr
Replaying a failing scenario
When you find a failing tools/call in the MCP Output log, the log shows the exact JSON arguments Cursor sent. Use these to reproduce the failure outside of Cursor:
- Copy the
"arguments"object from the failingtools/callrequest in the MCP Output panel - Open the MCP Inspector:
npx @modelcontextprotocol/inspector node dist/index.js - Navigate to the failing tool in the Inspector's tool list
- Switch to raw JSON input mode and paste the exact arguments
- Run the tool call — you'll see the same failure outside of Cursor
This isolates the problem from Cursor's AI behavior — you're now debugging the server directly with the exact inputs that caused the failure. Once you fix the handler, verify in the Inspector, then reload the Cursor window to reconnect the server and confirm the fix works end-to-end.
Common Cursor MCP debugging scenarios
Server connects in Claude Desktop but not in Cursor
Check that the mcp.json config uses the correct protocol version. Cursor and Claude Desktop may support slightly different subsets of the MCP spec. Check the MCP Output panel for the initialize response — if the protocolVersion fields don't match, Cursor may reject the connection. The MCP TypeScript SDK handles version negotiation automatically; if you're using a custom implementation, ensure you respond with the protocolVersion from the client's request.
Tool appears but is never called by the model
Cursor's model decides which tools to call based on the tool's description field. If the description is too generic ("performs document operations") or too technical ("queries the PostgreSQL FTS index"), the model won't recognize when to use it. Make descriptions action-oriented with concrete examples: "Search company documents by keyword. Use this when the user asks to find, look up, or search for specific information in the knowledge base."
Tool call fails with "Request timeout"
Cursor has a timeout on tool call responses (typically 30 seconds). If your handler takes longer than that — a slow database query, an external API call — Cursor times out the request. Fix options: (1) add a timeout to the handler that returns a partial result with an explanation; (2) optimize the slow operation; (3) if the operation is inherently long-running, return a job ID and provide a separate tool to poll for results.
Related pages
FAQ
Where does Cursor store MCP server logs?
Cursor writes each MCP server's stderr output to a log file on disk in addition to the Output panel. On macOS, log files are at ~/Library/Logs/Cursor/mcp-server-<name>.log. On Windows, they're at %APPDATA%\Cursor\logs\mcp-server-<name>.log. The log file name matches the key in your mcp.json mcpServers object. Tail this file to see server stderr output in real time: tail -f ~/Library/Logs/Cursor/mcp-server-my-server.log. The file is particularly useful for startup crashes that don't appear in the Output panel.
How do I reload the MCP server in Cursor without restarting Cursor?
Open the Command Palette (Cmd+Shift+P) and search for "MCP" — look for a "MCP: Restart Server" or "MCP: Reload" command. If that's not available, use "Reload Window" (Cmd+Shift+P → "Developer: Reload Window") which restarts all extensions and MCP servers without closing the Cursor window. After reloading, check the MCP Output panel to confirm the server reconnected and registered its tools correctly.
Can I test my MCP server against Cursor without involving the AI model?
Not directly through the Cursor UI — Cursor calls tools based on what the AI model decides to call. For isolated testing without the AI, use the MCP Inspector (npx @modelcontextprotocol/inspector) which connects directly to your server and lets you call tools with exact arguments. The Inspector gives you direct control over tool calls without the AI layer between you and the server. Then, once the tool works correctly in the Inspector, verify it in Cursor by asking the AI to use the tool in a context where you can predict what arguments it will send.
My Cursor MCP server works locally but fails for teammates. What's different?
Almost always an environment difference. Check: (1) the mcp.json is committed to the repo in .cursor/mcp.json so teammates use the same config; (2) all required environment variables are in the env block of mcp.json, not inherited from your shell; (3) the server binary path in args is either a relative path (like ./node_modules/.bin/my-server) or instructions to run npm install first; (4) the Node.js version matches — if the server uses Node 22 features and a teammate has Node 18, it will fail silently.
How does AliveMCP work with Cursor-connected MCP servers?
AliveMCP monitors the server independently from Cursor — it probes the server via its HTTP endpoint (for SSE or Streamable HTTP transport) regardless of whether Cursor is connected. If your server is stdio-only (spawned by Cursor as a child process), AliveMCP cannot probe it directly. For development servers, the MCP Inspector and Cursor's Output panel are the right debugging tools. For production servers accessed via HTTP, AliveMCP adds continuous uptime monitoring so you know if the server goes down between Cursor sessions — not just when a developer is actively using it.