When you configure a Model Context Protocol (MCP) server inside Cursor, you are not merely adding a plugin. You are instructing Cursor's Electron core to spawn a sub-process on your local operating system, wire its standard input and output streams (stdin and stdout) directly to an internal JSON-RPC 2.0 multiplexer, and exchange structured function-calling frames in real time.
┌────────────────────────────────────────────────────────────────────────┐
│ CURSOR MCP RUNTIME ARCHITECTURE │
└────────────────────────────────────────────────────────────────────────┘
Cursor IDE (Renderer Process)
│
▼ (IPC)
Cursor Node.js Main Host
│
├── 1. Spawns child process: child_process.spawn("node", ["server.js"])
│
├── 2. Reads standard I/O streams:
│ ├── STDIN <── Sends JSON-RPC Tool Requests (Ping / CallTool)
│ ├── STDOUT ──> Receives JSON-RPC Tool Responses
│ └── STDERR ──> Plaintext Debug Logging (Non-RPC)
│
▼
Target MCP Server Process (Local Python / Node.js Engine)
Because this pipeline relies on strict process I/O and JSON-RPC framing, the slightest anomaly—a console log statement on stdout, an unescaped Windows backslash, or a trailing comma in your configuration JSON—causes Cursor's parser to panic and silently mark the server as disconnected (red dot).
Over 90% of all Cursor MCP tickets filed on GitHub issues and Reddit boil down to five specific forensic issues:
| # | Error Symptom in Cursor | Underlying Technical Cause | Fix Time with aihub |
|---|
| 1 | Red dot, server never starts | Trailing comma in cursor_config.json / mcp.json | 1 second (aihub lint --fix) |
| 2 | "Connection closed" immediately after prompt | Server printed debug text to stdout instead of stderr | 2 seconds (redirect logs) |
| 3 | "Command not found: npx" on Windows | Shell PATH lookup failure in non-interactive subshell | 2 seconds (aihub doctor) |
| 4 | Tool calls timeout or return empty error | Missing API key in env object configuration | 2 seconds (aihub validate) |
| 5 | IDE freezes when opening chat | Zombie MCP process holding file lock on SQLite / database | 2 seconds (process kill) |
Let's examine how each failure occurs and how to fix it immediately.
Cursor stores server configurations in either:
- macOS / Linux:
~/.cursor/mcp.json or ~/.config/Cursor/User/globalStorage/cursor_config.json
- Windows:
%APPDATA%CursorUserglobalStoragecursor_config.json
Cursor's internal config loader uses standard JavaScript JSON.parse(), which strictly adheres to the RFC 8259 specification. Unlike JSON5 or modern TypeScript configs, trailing commas are illegal syntax in standard JSON:
// BROKEN: Notice the trailing comma after "dist/index.js"
{
"mcpServers": {
"git-sentinel": {
"command": "node",
"args": [
"dist/index.js",
]
}
}
}
When Cursor encounters this trailing comma, JSON.parse() throws an unhandled SyntaxError during startup, and Cursor completely drops the entire mcpServers dictionary without showing a visible banner in the UI!
Run AIStoreHub CLI's auto-fixer:
The CLI detects the syntax violation, strips the illegal trailing commas, validates the schema structure, and writes back the clean JSON while creating an automatic timestamped backup.
This is the most notorious bug in custom MCP server development.
In Model Context Protocol, stdout is strictly reserved for JSON-RPC messages (e.g., {"jsonrpc": "2.0", "result": ...}). If your Python or Node.js server executes a casual log statement like:
// DANGEROUS IN MCP SERVERS:
console.log("Connected to PostgreSQL successfully!");
or in Python:
# DANGEROUS IN MCP SERVERS:
print("Initializing embeddings cache...")
The text Connected to PostgreSQL successfully! is written directly into the stdout pipe. When Cursor attempts to parse this stream as JSON-RPC, it encounters Unexpected token 'C' and immediately drops the connection with the error:
Error: MCP connection closed unexpectedly. Exit code: null
Always route diagnostic and debug messages exclusively to stderr:
// CORRECT:
console.error("Connected to PostgreSQL successfully!");
# CORRECT:
import sys
sys.stderr.write("Initializing embeddings cache...
")
Cursor ignores stderr for protocol framing and displays it safely in the debug output panel.
On Windows systems, developers often copy file paths directly from File Explorer:
"args": ["C:\Users\abhinav\projects\mcp-server\index.js"]
In JSON strings, a single backslash U is parsed as a Unicode escape sequence, corrupting the path. Furthermore, Windows requires explicit shell extensions when invoking batch files or global npm packages (e.g., npx.cmd instead of raw npx when shell execution is disabled).
Use forward slashes / in JSON configuration paths (which both Windows and Node.js handle natively), or run:
The doctor command evaluates all registered paths in your Cursor configuration, verifies that target files exist on disk, and flags invalid Windows path delimiters.
Many popular MCP servers (such as Brave Search, GitHub MCP, and Neon Postgres) require API tokens to function. A common misconception is that Cursor's MCP runner automatically inherits all environment variables from your interactive .bashrc or .zshrc.
It does not. When Cursor launches on macOS or Windows from the desktop dock or taskbar, it executes in a clean system GUI environment without your terminal's shell profile loaded.
// INCOMPLETE CONFIGURATION:
{
"mcpServers": {
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"]
// Missing env block!
}
}
}
Without the token, the server exits immediately on startup with exit code 1.
Explicitly define the env dictionary inside the server block:
{
"mcpServers": {
"brave-search": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-brave-search"],
"env": {
"BRAVE_API_KEY": "BSA_xxxxxxxxxxxx"
}
}
}
}
If Cursor crashes or is force-quit while an MCP server is reading a SQLite file or binding to a local port (e.g., port 3000 for SSE transport), the child process often remains orphaned in the background as a zombie process.
When Cursor reopens, it tries to launch a fresh instance of the server, which immediately fails with EADDRINUSE: address already in use or SQLITE_BUSY: database is locked.
Instead of manually digging through obscure log directories, use the built-in diagnostic tools of AIStoreHub CLI:
# 1. Scan and auto-repair JSON syntax & schema errors
aihub lint --fix
# 2. Perform deep forensic health check on all installed MCP servers
aihub doctor
The command output gives you instant clarity:
AIStoreHub Doctor & Diagnostic Suite
✔ Found Cursor MCP configuration: C:\Users\abhinav\...\cursor_config.json
✔ JSON Syntax valid (strict RFC 8259)
✔ Found 4 registered MCP servers:
[1] postgres-mcp : ONLINE (latency: 18ms, exit code: 0)
[2] github-mcp : ONLINE (latency: 34ms, exit code: 0)
[3] brave-search : WARNING (missing BRAVE_API_KEY in env)
[4] local-fs : ONLINE (read-only permissions OK)
If you are developing or selling MCP servers on AIStoreHub, apply these hardening rules to ensure zero client-side crashes:
[ ] 1. NEVER write to stdout for debugging. Direct 100% of telemetry to stderr.
[ ] 2. Catch all unhandled promise rejections and exit gracefully with informative stderr messages.
[ ] 3. Support forward slash (/) paths uniformly across Windows, Linux, and macOS.
[ ] 4. Include clear schema descriptions and type constraints for all registered tools.
[ ] 5. Test your server with `aihub lint` prior to publishing to the open registry.