Skip to content
Blog

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.

MCPpedia13 min readNotes

GuideMCPTroubleshooting

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

Animated diagram of the four steps an MCP client takes to start a server (read config, start process, handshake, list tools), with the typical failure and error message at each step

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.

  1. Run the exact command in a terminal. Copy the command and args from 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.
  2. 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.

  1. Read the log. Where it lives depends on the client, below.
  2. 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/Claude on macOS and %APPDATA%\Claude\logs on Windows. mcp.log records connection events and mcp-server-<name>.log holds that server's stderr. On Windows, see the note under cause 6 before you trust that path.
  • Claude Code: run /mcp inside a session, or claude mcp list from a shell to health-check every server. claude mcp get <name> prints the reason for a failure. For the server's own output, start with claude --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 Servers from 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 log action 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, key mcpServers, local command entries only. Remote servers are added in the app as connectors, not in this file.
  • Claude Code: .mcp.json in the project, key mcpServers. An entry with a url and no type is skipped. Add "type": "http".
  • Cursor: .cursor/mcp.json or ~/.cursor/mcp.json, key mcpServers.
  • VS Code: .vscode/mcp.json, and the key is servers, not mcpServers.
  • Windsurf and Cline: mcpServers. Zed uses context_servers in 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 /mcp or claude mcp login <name>. A static Authorization header 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

Share X LinkedIn Email
Trust score 77 of 100, Good

Zotero MCP

by 54yyyu · Python

Search, read, annotate, and add to your Zotero research library, local or web.

Registry
LicenseQualityMaintenanceLicense: A. MIT is a permissive open source license.Quality: Not graded. No README or tool list has been captured yet.Maintenance: A. Last commit or release 19 days ago.
5.6K stars—/wk weekly downloads— tools
Trust score 77 of 100, Good

Create and edit Excel files with AI: charts, PivotTables, formulas. No Excel needed.

Registry
LicenseQualityMaintenanceLicense: A. MIT is a permissive open source license.Quality: Not graded. No README or tool list has been captured yet.Maintenance: A. Last commit or release 14 days ago.
4.3K stars—/wk weekly downloads— tools
Trust score 77 of 100, Good

Comprehensive Model Context Protocol server for managing Home Assistant through AI assistants.

Registry
LicenseQualityMaintenanceLicense: A. MIT is a permissive open source license.Quality: Not graded. No README or tool list has been captured yet.Maintenance: A. Last commit or release 8 days ago.
5.3K stars—/wk weekly downloads— tools

Top 5 MCP servers

Daily or weekly. Confirm by email, unsubscribe any time.

How often