MCP server not showing up or failing to start? Causes and fixes
The error you see, usually "Connection closed", is a symptom. This guide goes through the nine causes behind most failed MCP servers, with real error text we reproduced and the fix for each, on Claude Desktop, Claude Code, Cursor and VS Code.

You added an MCP server to your client, restarted it, and nothing. The server is missing, or it shows a red dot, or it says "Connection closed", or it connects with no tools. The message on screen is usually not the cause. It's the last thing that happened after the cause.
This guide starts with a one-minute triage that finds the failing step, then goes through nine causes with the error text each one produces. We reproduced most of the errors ourselves on Windows 11 with Node 22 and the official TypeScript SDK (version 1.32.2). Where a message comes from a bug report or the docs and not from our own test, we say so.
The one-minute triage

The four steps between a config entry and a working tool. Each one has its own typical failure.
Almost every failure falls at one of those four steps, so work through them in order.
- Run the exact command in a terminal. Copy the
commandandargsfrom your config and run them. A healthy stdio server prints nothing and waits for input, which looks like a hang and is correct. If it prints an error, that's your answer. - Test it outside your client with the MCP Inspector. The official debugging docs make this the first stop. The command-line mode is the quickest:
npx @modelcontextprotocol/inspector --cli node path/to/server.js --method tools/list
Use your own command in place of node path/to/server.js. If the Inspector lists tools and your client doesn't, the server is fine and the problem is your client's environment: PATH, environment variables, working directory or the config file itself. The Inspector needs Node 22.19 or newer for its current version.
- Read the log. Where it lives depends on the client, below.
- Check the config file. Right file, right top-level key, valid JSON, and a full restart of the client.
Where the logs are
- Claude Desktop:
~/Library/Logs/Claudeon macOS and%APPDATA%\Claude\logson Windows.mcp.logrecords connection events andmcp-server-<name>.logholds that server's stderr. On Windows, see the note under cause 6 before you trust that path. - Claude Code: run
/mcpinside a session, orclaude mcp listfrom a shell to health-check every server.claude mcp get <name>prints the reason for a failure. For the server's own output, start withclaude --debug=mcp, which includes the server's stderr. - Cursor: open the Output panel and choose "MCP Logs" from the dropdown.
- VS Code: run
MCP: List Serversfrom the Command Palette, pick the server and choose "Show Output". - Zed: open Settings, then the MCP Servers section, for status, and run the
zed: open logaction for the log. - Windsurf and Cline: the docs show server status in the UI and we couldn't find a log location, so check the panel first.
Cause 1: the server process dies before it answers
What you see: MCP error -32000: Connection closed. In Claude Desktop issue reports the log reads "Server transport closed unexpectedly, this is likely due to the process exiting early", then "Server disconnected". Claude Code shows Failed to connect.
Why: the child process exited before finishing the handshake. "Connection closed" tells you that it happened and not why. The reason is on the server's stderr.
We reproduced two versions of this with the SDK client. Pointing it at a command that doesn't exist gave MCP error -32000: Connection closed as the headline, and the real cause, spawn not-a-real-binary ENOENT, arrived separately as a transport error. A server that exits because an API key is missing also gave only Connection closed. Its explanation, API_KEY is not set, went to stderr.
Fix: run the command by hand (triage step 1), or read the server's stderr in the log. Common reasons we saw in public trackers are a missing dependency, an allowed directory that doesn't exist, a Node version that's too old, and a Docker run with -d, which detaches the container (a stdio server needs it attached).
Cause 2: Windows can't find npx
What you see: spawn npx ENOENT. Also seen: spawn EINVAL, and 'npx' is not recognized as an internal or external command.
Why: on Windows npx is a batch shim called npx.cmd. A client that starts it without a shell can't find a file named exactly npx. We reproduced this with plain Node on Windows 11:
spawn('npx', ['--version']) -> spawn npx ENOENT
spawn('npx.cmd', ['--version']) -> spawn EINVAL
spawn('cmd', ['/c', 'npx', '--version']) -> works
spawn('npx', ['--version'], {shell: true}) -> works
The EINVAL is a Node security hardening from 2024. Newer Node versions refuse to run .cmd and .bat files directly without a shell, so the "obvious" fix of calling npx.cmd fails too.
Fix: wrap the call in cmd /c. The README of the official reference servers says to do exactly this:
{
"mcpServers": {
"memory": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@modelcontextprotocol/server-memory"]
}
}
}
Two caveats. First, this is client-specific. In our tests the official SDK client resolves npx by itself, so a bare npx worked there, and a recent Claude Code on Windows also connected with a bare npx. Use the wrapper anywhere you are unsure. Second, if you add the server with claude mcp add from Git Bash, the shell rewrites /c into C:/. Run it from PowerShell, or set MSYS_NO_PATHCONV=1 for the command, or edit the JSON by hand.
Cause 3: the server writes to stdout
What you see: Unexpected token 'S', "Server sta"... is not valid JSON, or a connection that hangs and then times out.
Why: on stdio, stdout carries the protocol and nothing else. The spec says a server must not write anything to stdout that isn't a valid MCP message, and logs belong on stderr. A startup banner, a console.log left in a dependency, or a Python print all break the rule.
We tested it with a server that printed Server starting on stdio... to stdout before connecting. The SDK client connected and listed the tool, but raised exactly that parse error. A whole stray line is tolerated by some clients, and some report the error loudly. Text written without a trailing newline is worse, because it glues itself onto the next message and corrupts it. In a Claude Code test, that ended in connection timed out after 30000ms, and it looked intermittent because one stray write was absorbed on the first connect and failed on later ones.
Fix: send all logging to stderr or a file. To find which dependency is printing, run the server with stdout redirected to a file and read the file.
Cause 4: the client doesn't see your PATH
What you see: the server works in your terminal and fails in the app, often with spawn node ENOENT or "No such file or directory".
Why: desktop apps don't inherit your shell setup. The official debugging docs say stdio servers get only a limited, platform-dependent set of environment variables, and the working directory may be undefined. Node installed through nvm, fnm, Volta or asdf lives in a directory added by your shell profile, which a GUI app never reads. Homebrew on Apple Silicon is a separate case. A pasted Claude Desktop log from a macOS user showed it searching /usr/local/bin, /opt/homebrew/bin, /usr/bin and a few system folders and nothing else.
Fix, in order of reliability. Use an absolute path in command, found with which node or where node. Or put the interpreter's folder in the entry's env.PATH. Or install Node system-wide for the apps. In an SDK test, an env.PATH we set replaced the inherited one, so include every folder you need.
Cause 5: environment variables don't arrive
What you see: the server starts and shows connected but exposes zero tools, or fails on the first call with an auth error.
Why: the same limited environment as above. A variable that's set in your shell doesn't automatically reach the server. In our SDK test a variable set in the parent process was unset inside the child until we passed it through env. Claude Code's docs say a server that connects with no tools usually means a required variable, such as an API key, is missing.
Fix: put the variables in the server's env block. In Claude Code you can write ${VAR} or ${VAR:-default} in .mcp.json, and an unset variable shows up as Missing environment variables: <NAME> in the claude mcp list output. Placeholder support differs by client, so check your client's docs before relying on it. Also check for a stray space or newline in a pasted token.
Cause 6: the config is in the wrong place or the wrong shape
What you see: the server never appears, or the app reports a parse error. Claude Desktop's dialog starts with "There was an error reading or parsing claude_desktop_config.json".
Why: each client has its own file and its own top-level key.
- Claude Desktop:
claude_desktop_config.json, keymcpServers, localcommandentries only. Remote servers are added in the app as connectors, not in this file. - Claude Code:
.mcp.jsonin the project, keymcpServers. An entry with aurland notypeis skipped. Add"type": "http". - Cursor:
.cursor/mcp.jsonor~/.cursor/mcp.json, keymcpServers. - VS Code:
.vscode/mcp.json, and the key isservers, notmcpServers. - Windsurf and Cline:
mcpServers. Zed usescontext_serversin its settings file.
The servers against mcpServers mix-up is the most common one when pasting between clients. Claude Code's claude mcp list prints it plainly: Missing "mcpServers" — found "servers" instead. A trailing comma gives MCP config is not a valid JSON.
Fix: use the right file and key, validate the JSON, and quit the client completely before reopening it. For Claude Desktop that includes the tray icon, because closing the window leaves the app running.
One Windows trap. Several user reports say the Windows store (MSIX) build of Claude Desktop can read a different copy of the config than the one in %APPDATA%. If edits seem to be ignored, open the file from Settings, then Developer, then Edit Config, and look at which path the app reports reading. On the one Windows machine we checked, a current build wrote its live logs to %LOCALAPPDATA%ClaudeLogs, and the documented %APPDATA%Claudelogs folder was months stale. If the files in the documented folder are old, look in the other one, and check the modified dates.
Cause 7: relative paths
What you see: it works from one directory and fails from another.
Why: a relative path in command or args is resolved against wherever the client was launched, which for a GUI app may be /. We saw a user-scope entry with node ../server.mjs connect from one folder and fail with Connection closed from another.
Fix: use absolute paths in config and in the server's own .env loading. Claude Code also exports CLAUDE_PROJECT_DIR, and you can write ${CLAUDE_PROJECT_DIR:-.} in .mcp.json.
Cause 8: a slow first start
What you see: it fails the first time, then works. In Claude Code the text is MCP server "<name>" connection timed out after 30000ms.
Why: npx and uvx download the package on first run, and that can take longer than the client waits. Claude Code's startup timeout is MCP_TIMEOUT, 30 seconds by default, and we saw a server with an 8-second startup connect at the default and fail when we set it to 3 seconds. Claude Desktop documents no timeout, and one bug report measured its initialize request being cancelled at exactly 60 seconds, which is not an official statement.
Fix: run the command once in a terminal so the package is cached, pin the version, or raise the limit:
MCP_TIMEOUT=60000 claude
In PowerShell that's $env:MCP_TIMEOUT = "60000"; claude. Claude Code's own quickstart notes that the first claude mcp list after adding an npx server can show a failure while the package downloads, and that running it again is enough.
Cause 9: remote servers
What you see: MCP endpoint not found at <origin>. Check the URL in your MCP config., or SSE error: Non-200 status code (405), or a message about needing to sign in.
Why: three separate things.
- The wrong URL or transport. A 404 means the path is wrong. A 405 from the SSE client means you pointed the old SSE transport at a server that only speaks Streamable HTTP. Cline defaults to SSE when an entry has no
type, so set"type": "streamableHttp". Claude Code and VS Code try Streamable HTTP first and fall back to SSE, which hides most of this. - Authentication. A 401 or 403 usually means sign-in is needed. In Claude Code, run
/mcporclaude mcp login <name>. A staticAuthorizationheader you set yourself disables the OAuth fallback, so a wrong header fails with a message about the rejected header. - Reachability. Claude Desktop's remote connectors are called from Anthropic's network, not from your laptop. A server on localhost, or behind a VPN, works from your machine and fails from the connector.
If your client can only start local commands, the mcp-remote bridge forwards to a remote URL. Our remote vs local post covers when to use it.
Two newer problems
Too many tools. VS Code allows at most 128 tools per chat request, and the error is Cannot have more than 128 tools per request. Deselect servers in the tool picker. Claude Code defers tool definitions by default so only names load at the start, and /context shows what is using the window. A server that exposes dozens of tools costs context in every client, which is one more reason to keep tool lists small (tool description guide).
"It worked yesterday." The spec's 2026-07-28 revision removed the initialize handshake for newer servers, and clients are updating at different speeds. Claude Code's newer runtime probes a server for the new version first and falls back, and its docs describe MCP_PROTOCOL_NEGOTIATION=legacy to skip the probe. If a server broke right after a client update, try that setting and report it to the server's author.
If it still fails
Go back to the triage. If the Inspector connects and your client doesn't, compare the working command with your config line by line: absolute paths, env, the shell, the file. If the Inspector fails too, the server is the problem, and its stderr will say why. Before you spend an evening on it, check the server's own issues, because the same error has usually been seen before. For servers we list, every page has copy-ready configs for ten clients, which removes the file-and-key mistakes from the list. Start from the install guide or browse the directory.
Sources
- Debugging MCP servers and Connect to local MCP servers
- MCP specification, stdio transport
- Official reference servers README, for the Windows
cmd /cwrapper - Claude Code MCP documentation
- Cursor MCP documentation
- VS Code MCP configuration reference
- Cline: configuring MCP servers
- Zed: MCP servers
- Node.js child_process documentation
Servers in this post
Zotero MCP
by 54yyyu · Python
Search, read, annotate, and add to your Zotero research library, local or web.
Excel MCP Server
by haris-musa · Python
Create and edit Excel files with AI: charts, PivotTables, formulas. No Excel needed.
ha-mcp
by homeassistant-ai · Python
Comprehensive Model Context Protocol server for managing Home Assistant through AI assistants.