You added a new MCP server to Cursor — filesystem, GitHub, a custom one — and the dot stays red. Or it shows green but no tools appear in Composer. Or it connects on launch but drops after a few minutes. The Cursor MCP integration is thin: it spawns a process, speaks JSON-RPC over stdio (or hits an HTTP endpoint), and registers whatever tools the server advertises. When it breaks, the failure is almost always in the spawn (wrong path, missing runtime), in the handshake (bad JSON, wrong schema), or in the transport choice (stdio vs HTTP mismatch). Walk through the layers from bottom up and you will find it.
Common causes
Ordered by hit rate, highest first.
1. Command or path does not exist on PATH
You configured "command": "npx" or "command": "uvx" but Cursor inherits a stripped PATH on macOS and cannot find it. The server never starts.
How to judge: Open ~/.cursor/logs/ latest file and grep for spawn ENOENT or command not found. That is exactly this.
2. Stdio server printed to stdout instead of stderr
MCP stdio servers must reserve stdout for JSON-RPC frames only. If the server prints a banner, log line, or warning to stdout, Cursor sees malformed JSON and disconnects.
How to judge: Run the server command manually in a terminal. If you see anything non-JSON on stdout, that is the bug. All logs must go to stderr.
3. Wrong transport in mcp.json
You wrote an HTTP URL but used the stdio shape (or vice versa). Cursor 0.46+ supports both, but the JSON keys differ — command/args for stdio, url for HTTP.
How to judge: Open ~/.cursor/mcp.json. Stdio needs command + args. HTTP needs url (and usually type: "http"). Mixing them silently fails.
4. Server requires environment variables Cursor did not pass
GitHub MCP needs GITHUB_TOKEN, custom servers often need API keys. If you set them in your shell rc but did not add them under env in mcp.json, the spawned process does not see them.
How to judge: Run the server command outside Cursor with the same env. If it works there but not in Cursor, env is the gap.
5. Tools loaded but Composer has them disabled
Settings → MCP shows green and a tool count, but Composer never calls any tool. There is a per-conversation toggle hidden in Composer for each MCP server.
How to judge: In Composer, click the tools icon at the bottom. If your MCP server is listed but the toggle is off, that is it.
6. Cursor cached an old failed handshake
After fixing the underlying issue, Cursor sometimes keeps the server in a failed state until you toggle it off and on.
How to judge: Settings → MCP → toggle the server off, wait 3 seconds, toggle back on. If it works now, that was the symptom.
Before you start
- Know whether your server is stdio or HTTP — they are configured differently.
- Have the server’s README handy for the exact
command,args, and env vars. - Pick one server at a time; debugging two at once is twice as painful.
Information to collect
- Cursor version (Help → About). MCP support is solid only on 0.45+.
- OS and shell.
- Path to your
mcp.json(usually~/.cursor/mcp.jsonfor global,.cursor/mcp.jsonin a workspace for project-scoped). - Latest log in
~/.cursor/logs/(sort by mtime). - The server command and args, exactly as in
mcp.json. - Output of running that command manually in a fresh terminal.
- For HTTP servers: a
curlof the endpoint with the expected JSON-RPCinitializerequest.
Step-by-step fix
Step 1: Run the server outside Cursor first
Copy the command and args from mcp.json and run them in a terminal. The server should print nothing on stdout (or only JSON), and stay alive waiting for input. If it crashes immediately, the bug is in the server itself, not Cursor.
Step 2: Use absolute paths for the command
Replace "npx" with the full path: /Users/you/.nvm/versions/node/v20.11.0/bin/npx. Same for uvx, python, node. This bypasses the PATH issue.
{
"mcpServers": {
"github": {
"command": "/usr/local/bin/npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_TOKEN": "ghp_..." }
}
}
}
Step 3: Pass env explicitly
Anything the server needs must be under the env key in mcp.json. Cursor does not inherit your shell environment for spawned MCP processes.
Step 4: Check the log for the handshake
Open the latest file in ~/.cursor/logs/ and search for the server name. You should see initialize request and response. If you see Failed to parse JSON or Unexpected token, the server printed garbage on stdout — fix the server to log to stderr only.
Step 5: For HTTP servers, verify the endpoint
curl -X POST https://your-server.example.com/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
You should get a JSON response with serverInfo and capabilities. If you get HTML, a 404, or a CORS error, the URL or auth is wrong.
Step 6: Toggle and reload
Settings → MCP → toggle the server off, then on. If that does not register the change, fully quit Cursor (Cmd+Q, not just close the window) and relaunch. MCP servers re-spawn on launch only.
Step 7: Enable the tools in Composer
Open Composer, click the tools icon, scroll to your server, flip the per-server and per-tool toggles on. New tools default to off in some Cursor versions.
Verify
- Settings → MCP shows green dot and a tool count greater than zero.
- In Composer, ask a prompt that should trigger one of the tools, e.g. “List my GitHub repos.”
- Watch the chat for a tool call card. If you see it execute and return, the server is healthy.
- Restart Cursor and confirm the server reconnects automatically.
Long-term prevention
- Pin server versions in
args: use@modelcontextprotocol/server-github@1.0.4, not unpinned. - Keep
mcp.jsonunder version control (without secrets) so the team has the same servers. - Use a
.envfile plus a wrapper script that loads it and execs the server — easier than embedding secrets inmcp.json. - Add a CI check that JSON-parses
mcp.jsonso a typo never lands. - Document required env vars in your project README next to the MCP block.
Common pitfalls
- Editing
mcp.jsonwhile Cursor is open and expecting a hot reload. Cursor reads it on launch and on toggle. - Confusing user-level
~/.cursor/mcp.jsonwith workspace-level.cursor/mcp.json— both exist, both load. - Letting your server print “Server started on port 3000” to stdout. That single line breaks the protocol.
- Forgetting to set
type: "http"for HTTP transport; defaults to stdio and the URL key is ignored. - Assuming Cursor knows about new tools added after launch. It does not poll; restart.
FAQ
- Where is mcp.json?
~/.cursor/mcp.jsonfor global,.cursor/mcp.jsoninside a workspace for project-scoped. Both load if both exist. - Stdio vs HTTP — which should I pick? Stdio for local processes (filesystem, git). HTTP for hosted or shared servers (team-wide custom server).
- Why does the dot go red after a few minutes? Usually the server process exited silently. Check stderr or run it manually to see the crash.
- Can I share an MCP setup with my team? Yes — commit
.cursor/mcp.jsonin the repo, keep secrets out, document env vars in the README. - Does Cursor support the full MCP spec? Mostly. As of 0.46 it covers tools and resources well; prompts and sampling are less mature.
Related
- Cursor YOLO Mode Runs Without Confirm
- Cursor Agent Mode Tool Call Stuck
- Cursor Config Conflict
- Cursor IDE State Out of Sync
- Cursor Extension Marketplace Broken
Tags: #Cursor #mcp #Troubleshooting #Debug