Introduction
The Model Context Protocol (MCP) is how an AI agent reaches your tools, files, and APIs — and when a server won't connect, the agent silently loses that capability with no obvious reason why. "Could not connect to MCP server" is one of the most common failures developers hit across Claude Code, Cursor, and Claude Desktop, and the error message rarely tells you which of several unrelated causes is to blame.
This is a diagnostic checklist, ordered so the cheapest and most common fixes come first. Work through it top to bottom. Nearly every failed MCP connection comes down to one of four root causes: the binary can't be found, an environment variable is missing, an auth token is wrong or expired, or the transport never completes its handshake. The steps below isolate which one you have before you start changing configuration blindly.
Step 1: Check the server's status first
Before editing anything, find out what state the server is actually in. In Claude Code, run /mcp inside a session, or from your shell:
claude mcp list
claude mcp get <server-name>
The status tells you which branch of this checklist to follow:
- failed — a launch problem. The process could not start. Continue to Step 2.
- needs authentication — the server launched but requires an auth flow you haven't completed. Skip to Step 5.
- pending — the server is waiting on your approval. Approve it and re-check.
In Cursor, open the Output panel and select MCP from the dropdown; in Claude Desktop, hover the connectors menu to see which servers are connected. Knowing the status first saves you from "fixing" a JSON file that was never the problem.
Step 2: Validate your configuration JSON
The single most common cause of "no servers loaded at all" is a JSON syntax error. A trailing comma, a missing quote, or an unescaped backslash will silently prevent the entire config from parsing, and most clients give no visible error. Validate the file before anything else:
# macOS/Linux
python3 -m json.tool < ~/.cursor/mcp.json
# or pipe any config through a validator
cat claude_desktop_config.json | python3 -m json.tool
If the command prints your config back formatted, the JSON is valid. If it prints an error with a line and column, fix that first — a single malformed server entry can take down every other server in the file.
On Windows, one specific trap is worth calling out: backslashes in paths must be escaped (C:\\Users\\me\\server) or written with forward slashes (C:/Users/me/server). A raw single backslash is an invalid escape sequence and breaks the parse.
Step 3: Read the actual error in the logs
The logs almost always contain the real cause — a permission-denied, a module-not-found, a port already in use. Don't guess; read them.
For Claude Desktop, the log files live at:
# macOS
tail -n 20 -F ~/Library/Logs/Claude/mcp*.log
# Windows (PowerShell)
type "$env:AppData\Claude\logs\mcp*.log"
For Claude Code, get the server's raw stderr with the debug flag:
claude --debug mcp
For Cursor, open the MCP output channel or the developer console (Help → Toggle Developer Tools → Console). A spawn error in the log almost always means the command path is wrong or the binary doesn't exist — which takes you to Step 4.
Step 4: Fix the binary path and environment
When a stdio server launches, the client's working directory may be undefined — often / on macOS, because the client can be started from anywhere. That single fact causes most path-related failures. Two rules follow from it:
Use absolute paths everywhere. The command, any script arguments, and any file paths inside your env block must be absolute. A relative path like ./data resolves against that unknown working directory and drifts:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/data"]
}
}
}
Pass environment variables explicitly. MCP servers launched over stdio inherit only a limited, platform-dependent subset of your shell environment. If your server needs an API key, don't assume it will pick it up from your shell — declare it in the config:
{
"mcpServers": {
"myserver": {
"command": "mcp-server-myapp",
"env": { "MYAPP_API_KEY": "some_key" }
}
}
}
This is also why a server can work when you run it by hand but fail when the IDE launches it: the IDE process never inherited your shell's exported variables.
On Windows, npx-based servers often need to be wrapped so the shell resolves the command correctly:
{ "command": "cmd", "args": ["/c", "npx", "-y", "@modelcontextprotocol/server-filesystem", "C:/data"] }
Step 5: Isolate the problem — test the server standalone
If the config looks right and the logs are ambiguous, remove the client from the equation. The MCP Inspector is a transport-agnostic testing UI — connect it directly to your server, invoke a tool, and watch the notification stream. If the server works in the Inspector but not in your client, the problem is in your client config, not the server. If it fails in the Inspector too, the problem is in the server itself.
You can also just run the server command in the same shell the client uses. If it crashes standalone, you've found your bug without touching any client at all.
Step 6: Restart the client fully
Most MCP clients do not hot-reload configuration changes. After editing the config file you must fully quit and relaunch the application — for Claude Desktop, Cursor, and VS Code, closing and reopening the chat window is not enough. Quit the whole app and reopen it. A surprising number of "my fix didn't work" reports are just a config change that was never reloaded.
If you suspect a plugin, hook, or one bad server is poisoning startup for everything, launch Claude Code with all customizations disabled to confirm:
claude --safe-mode
If the other servers connect in safe mode, you've confirmed the culprit is one of your customizations, and you can re-enable them one at a time to find it.
Step 7: Remote servers and the "connected but no tools" case
For servers using SSE or Streamable HTTP transport rather than a local process, the failure modes shift to networking. Confirm the endpoint is actually reachable:
curl -I https://your-server.example.com/mcp
Check that the URL is correct, the server is running, and — for browser-based clients — that CORS headers are present. Remote servers also don't write to a local stderr you can tail, so lean on the server's own logs or standard HTTP tooling to inspect the request and the Mcp-Session-Id header.
Finally, the confusing case: connected, but zero tools. The transport handshake succeeded, but the server returned an empty tool list. This is a server-side initialization problem, not a connection problem. Reconnect it, read the stderr with claude --debug mcp, and look for a runtime error during startup or a capability the server declared in its initialize exchange but never actually implemented. A JSON-RPC -32602 ("Invalid params") error here usually means the two sides disagreed about what capabilities were negotiated.
Conclusion
MCP connection failures feel opaque because one generic message ("could not connect") stands in for at least four distinct causes. The fix is to stop guessing and follow the evidence: check the status with /mcp, validate the JSON, read the logs, then work through the binary path, environment, auth, and transport in order. Two habits prevent most of these failures outright — always use absolute paths, and always fully restart the client after a config change. When a server still won't cooperate, testing it standalone with the MCP Inspector tells you immediately whether the problem is your config or the server itself.
For a deeper look at how MCP fits into the broader agent ecosystem, see our explainer on how AI agents talk to each other.