You run your MCP server by hand, it starts, nothing crashes. You add it to Claude Desktop, Cursor or another client and it shows up red, "failed", or connects and then no tool works.
Almost always the server is fine. The difference is the environment the client launches it in. These are the causes we keep hitting, roughly in order of how often.
1. Something writes to stdout
With the stdio transport, stdout IS the protocol. Every byte on it must be a JSON-RPC message. One console.log("server started"), a banner from a library, or a deprecation warning printed to stdout, and the client fails to parse the stream.
Fix: send all logs to stderr (console.error in Node, print(..., file=sys.stderr) in Python, the logging module defaults to stderr). Clients usually capture stderr into their log file, so you lose nothing.
2. The client does not have your shell's PATH
On macOS, an app started from the Dock or Finder does not read .zshrc or .bash_profile. It gets the minimal launchd PATH, /usr/bin:/bin:/usr/sbin:/sbin. If node came from Homebrew (/opt/homebrew/bin) or nvm (~/.nvm/versions/...), the client cannot find npx or node, and you get spawn npx ENOENT.
Fix, pick one:
- Use the absolute path as the command: run
which npxin your terminal and paste the result. - Set PATH explicitly in the server's
envblock in the client config.
With nvm, the absolute path also pins the Node version, which avoids cause 6.
3. Your environment variables are not there either
The same applies to API keys and settings you exported in your shell. The server process only gets what the client passes it.
Fix: put them in the env block of that server's config entry, not in your shell profile.
4. npx is waiting or still downloading
npx some-package on a machine that does not have it cached first downloads it. Without -y it can stop to ask "Ok to proceed?", and nobody is there to answer. Even with -y, a slow first download can outlast the client's startup timeout, so the first launch fails and the second one works.
Fix: always npx -y, and run the exact command once in a terminal so the package is cached before the client starts it.
5. Relative paths and the working directory
Clients do not promise a working directory. On macOS it is often /. A server that opens ./config.json or writes ./data.db works from your project folder and fails everywhere else.
Fix: absolute paths in args, or resolve paths relative to the script file, not the current directory.
6. A different Node or Python than you think
Your terminal uses the version from nvm, pyenv or a virtualenv. The client may hit the system one, or an old Homebrew one. Syntax errors at import time or "unsupported engine" warnings in the log are the sign.
Fix: point the command at the exact interpreter (/path/to/venv/bin/python, the nvm node), not a bare python or node.
7. Windows: npx is not an executable
On Windows npx is npx.cmd, a batch file, and some clients spawn the command without a shell, so "command": "npx" fails.
Fix: launch it through cmd:
{
"command": "cmd",
"args": ["/c", "npx", "-y", "your-mcp-package"]
}
8. It is configured for a different project (Claude Code)
Added from the comments, thanks to @heyitsjakub. claude mcp add uses the local scope by default: the server is registered for the project you ran it in, and nowhere else. Open another project and it looks like the server vanished, while the config is fine.
Fix: run claude mcp list inside the project where it is missing. For every project, add it with --scope user. To share it with your team, use --scope project, which writes a .mcp.json at the repo root (Claude Code asks each person to approve it once). Cursor has the same split: ~/.cursor/mcp.json for all projects, .cursor/mcp.json for one.
Where to look first
-
Claude Desktop logs:
~/Library/Logs/Claude/on macOS (mcp.logplus onemcp-server-<name>.logper server),%APPDATA%\Claude\logson Windows. Your stderr ends up there. -
Claude Code:
claude mcp listshows which servers connect, and/mcpinside a session shows their status. -
MCP Inspector:
npx @modelcontextprotocol/inspector <your command> <args>starts the server the way a client would and lets you call each tool by hand. If it works there and not in the client, the problem is the client's environment (causes 2 to 6). - Restart for real. Most clients read the config only at startup. On macOS that means Cmd+Q, not closing the window.
A 30-second checklist
- Nothing but JSON-RPC on stdout?
- Command is an absolute path, or PATH is set in
env? - API keys in the config's
env, not only in your shell? -
npx -y, and the package already cached? - No relative paths?
- The interpreter the client runs is the one you tested with?
- On Windows,
cmd /c npx? - In Claude Code, added in the right scope?
Why we wrote this
We build Auten, an MCP server that gives Claude Code, Codex, Cursor or any MCP client hands on a real screen: your computer and your Android phone (beta). It is launched with npx -y @autenai/mcp, so every item on this list is a support question we have either hit ourselves or expect to.
Hit another cause? Put it in the comments and we will add it.
Top comments (4)
Another possible reason is the scope configuration.
By default,
claude mcp addadds the server only to the current project. This means that when you open a different project, the server might not be available there.So, it may look like the server has disappeared, even though it is still configured correctly. You can check this by running
claude mcp list. If you want the server to be available across multiple projects, you can use--scope userwhen adding it.Good one, thanks. That "it disappeared" moment looks exactly like a broken server, so it belongs on this list.
One addition: there is also --scope project, which writes a .mcp.json at the repo root. It is meant to be committed, so everyone on the team gets the server, and Claude Code asks each person to approve it the first time. Cursor has the same split: ~/.cursor/mcp.json for all projects, .cursor/mcp.json inside one project.
Thanks for adding that to the article. Distinguishing personal and team scopes is super useful, and I really like the Cursor example too.
Glad it helped, and thanks again for the tip. One small extra for anyone debugging this:
claude mcp get <name>prints which scope a server lives in, so you can see at a glance whether it is local, project or user before re-adding it.