Where does the bug appear (feature/product)?
Cursor IDE
Describe the Bug
All user-defined stdio MCP servers configured in ~/.cursor/mcp.json fail to stay connected on macOS Cursor 3.4.20. They reach the “connected” state after a successful initialize + tools/list handshake, then are killed by Cursor itself ~1.5–2 seconds later with SIGKILL (exit 137) — not a graceful stdin close. The state machine transitions are none → initializing → connected → error. Tools never become callable. The built-in cursor-ide-browser MCP is unaffected because it does not go through the stdio child-process path.
The same servers work perfectly when launched standalone in a terminal and fed JSON-RPC over stdin.
Steps to Reproduce
- Use this minimal
~/.cursor/mcp.json(or any other stdio MCP):
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "mcp-postgres@latest"],
"env": { "DATABASE_URL": "postgresql://USER:PASS@HOST:5432/postgres" }
}
}
}
- Launch Cursor with any workspace open.
- Open
~/Library/Application Support/Cursor/logs/<session>/window1/exthost/anysphere.cursor-mcp/MCP user-postgres.log. - Observe the server reaches
Successfully connected to stdio serverand thentransport_closed~1.5s later. - Verify the same
mcp-postgres@latestpackage works fine when started manually from a terminal with the same JSON-RPCinitialize+tools/listfed over stdin — it responds correctly and stays alive.
Expected Behavior
After a successful initialize + tools/list, the stdio MCP server should stay alive until the user (or Cursor on a clean shutdown) explicitly disables/removes it. Tools should remain callable for the entire session, just like the built-in MCPs.
Operating System
MacOS
Version Information
Cursor 3.4.20 (Electron 39.8.1)
Darwin 24.3.0 (macOS)
Node.js (system): v23.11.0 (/usr/local/bin/node)
Same behavior was observed on 3.3.27 before the auto-upgrade.
Additional Information
Direct evidence that Cursor sends SIGKILL
With a thin bash wrapper that records signals instead of execing immediately, Cursor’s kill -9 becomes visible:
/Users/me/.cursor/mcp-launcher-debug.sh: line 30:
12685 Killed: 9 /usr/local/bin/npx -y "$@"
Race condition in V2 FSM
workbench.mcp.allowlist.log + per-server log shows CreateClient followed by DeleteClient only 15 ms later, then another CreateClient. The earlier process is then killed mid-startup:
11:42:33.683 createClient: identifier="user-postgres"
11:42:33.688 [V2] Handling CreateClient action
11:42:33.703 [V2] Handling DeleteClient action, reason: config_server_modified ← 15ms later
11:42:35.372 [error] Connected to database: ... (still starting)
11:42:37.076 createClient: identifier="user-postgres"
11:42:37.079 [V2] Handling CreateClient action
11:42:38.806 [error] Connected to database: ...
11:42:39.683 [error] line 30: 12685 Killed: 9
11:42:39.691 Connection failed: MCP error -32000: Connection closed
The reason: config_server_modified fires even when mcp.json content is unchanged, suggesting an overly eager file-watcher trigger or duplicated CreateClient calls.
Separate but related: environment pollution leaks to MCP children
Cursor injects /Applications/Cursor.app/Contents/Resources/app/resources/helpers/ at the head of PATH when spawning the MCP child process. That directory contains a 226 MB binary named node, which is an Electron-as-Node helper, and Cursor also sets ELECTRON_RUN_AS_NODE=1 in the child environment.
which node inside the MCP wrapper returns:
/Applications/Cursor.app/Contents/Resources/app/resources/helpers/node
/usr/local/bin/npx
As a result, npx ends up executing the MCP server under the Electron-as-Node interpreter, which corrupts the stdio protocol enough that some servers cannot complete initialize at all (firebase-tools mcp, @mobilenext/mobile-mcp — both work standalone, both fail under Cursor unless ELECTRON_RUN_AS_NODE is unset and PATH is repaired).
Workaround for the env pollution (NOT for the SIGKILL bug):
#!/bin/bash
unset ELECTRON_RUN_AS_NODE
export PATH="/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin"
exec /usr/local/bin/npx -y "$@"
With this wrapper, initialize succeeds — but the SIGKILL bug still kills the server ~1.5 s later.
Impact
All user-defined stdio MCP servers (postgres / gitlab / apifox / firebase / mobile-mcp in my case) are unusable. Only the built-in cursor-ide-browser works.
Suggested investigation areas
- The duplicate
CreateClient→DeleteClientrace in the V2 FSM (reason: config_server_modifiedfiring without config changes). - Use
SIGTERM+ graceful stdin close beforeSIGKILL; respect a settle window afterinitialize. - Stop injecting
helpers/into childPATHand stop leakingELECTRON_RUN_AS_NODE=1to MCP child processes. - Make the
initializetimeout configurable per-server for heavy bootstraps likefirebase-tools mcp.
A log bundle (Cursor MCP logs + wrapper-captured stdio I/O + the SIGKILL stderr) is attached.
Does this stop you from using Cursor
No - Cursor works, but with this issue