When an MCP server will not connect, work the chain in order: confirm Claude Code can find and run the command, check the args and env it passes, read the server logs, then run the server on its own to prove the fault is in the wiring and not the server itself. This guide is that checklist, top to bottom.
If you have not configured a server yet, read MCP fundamentals and MCP configuration first. This guide assumes you added a server and it shows up broken.
How do I check an MCP server's status?
Inside a Claude Code session, run the status panel:
/mcpThe /mcp panel lists every server and its state. A healthy stdio server
shows as connected with a tool count next to it. A broken one shows as failed,
or as pending while Claude Code retries a remote connection.
To see the same picture from your shell without opening a session:
claude mcp list
claude mcp get my-serverclaude mcp list prints every configured server and whether it connected.
claude mcp get <name> prints one server's full configuration: the command, the
args, the env, the transport, and the scope. Read this first. Most connection
failures are a typo you can see in this output before you touch anything else.
Step 1 · The command path
A stdio server is a program Claude Code launches as a child process. If it cannot find or run that program, nothing else matters. You configure it like this:
claude mcp add my-server -- npx -y @scope/my-mcp-serverThe -- separator splits Claude Code's own options from the command it runs.
Everything after -- is the command and its args, passed through untouched.Common gotchaWith npx -y @scope/server, npx can launch a stale cached copy, so a fix you published never loads and the old bug appears to live forever.Pin the version (@scope/server@1.4.0) or clear the cache with npx clear-npx-cache to force the real one.
Check three things about the command:
- Is the binary on the PATH Claude Code sees? Claude Code launches the
process with a limited environment. A tool that works in your interactive
shell may not be on the PATH the child process inherits. When in doubt, use an
absolute path to the binary, for example
/usr/local/bin/nodeinstead ofnode. - Does the file exist where you pointed? A server you run with a script path,
such as
python /srv/mcp/server.py, breaks the moment that file moves. Confirm the path resolves. - Is it executable? A script without execute permission, or a Node entry point that was never built, fails at launch with a permission or "module not found" error.
PATH mismatches are the most common reason a server that "works on my machine" fails inside Claude Code. The fix is an absolute path.
Step 2 · Working directory and absolute paths
When Claude Code launches a stdio server, the working directory may be undefined
or the filesystem root, not the folder you started Claude from. A server that
reads ./config.json or writes to ./data looks in the wrong place and fails.
Use absolute paths everywhere: in the command, in the args, and inside any
.env file the server reads.
claude mcp add files -- npx -y @scope/server-filesystem /home/me/projects/notesPass the data directory as an absolute path in the args, never ./notes. The same
rule applies to a database file, a credentials file, or a log path the server
opens on startup.
Step 3 · The args
Args are the tokens after the command. One wrong flag and the process exits before
the connection opens. Read the args back with claude mcp get and check them
against the server's own documentation.
Common mistakes:
- A flag the server does not accept. The process prints a usage error to its output and exits. You see a failed server with no tools.
- A missing required arg, such as the directory a filesystem server needs or the connection string a database server needs.
- Quoting that the shell ate. A value with spaces or a
$can be mangled when you type theclaude mcp addcommand. Confirm the stored args withclaude mcp get, because the stored copy is what runs.
| Symptom | Likely cause | Fix |
|---|---|---|
| Server fails instantly | Wrong command or missing binary | Use absolute path, confirm it runs |
| Fails with a usage error | Bad or missing arg | Compare args to server docs |
| Connects but exposes no tools | Server started but errored after init | Read the logs |
| Remote server stuck pending | Auth not completed | Run /mcp and authenticate |
Step 4 · The environment
MCP servers launched over stdio inherit only a limited subset of environment variables, and the exact set is platform-dependent. A server that needs an API key or a connection string will not see a variable just because it is exported in your shell. You pass it explicitly:
claude mcp add --env API_KEY=your_key --transport stdio my-server -- node /srv/mcp/index.js--env accepts multiple KEY=value pairs. Place at least one other option
between --env and the server name, as shown above with --transport. If the
name comes right after --env, the CLI reads the name as another pair and
rejects it.Common gotchaA trap here is wrapping the value in quotes inside the pair, like --env API_KEY="abc". Your shell strips the outer quotes before the CLI sees them on a bare value, but when the value has spaces the quotes get stored literally and the server reads "abc" with the quote marks included, so every call 401s for no visible reason.Pass the raw value with no surrounding quotes and confirm the stored copy with claude mcp get.
For a project-scoped server, the values live in the project .mcp.json file, and
you can reference machine-specific values with environment-variable expansion:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/srv/mcp/index.js"],
"env": {
"API_KEY": "${MY_APP_API_KEY}",
"DATA_DIR": "${DATA_DIR:-/home/me/projects/notes}"
}
}
}
}The ${VAR:-default} form supplies a fallback when the variable is unset, which
keeps the config working for every teammate. A server that connects but every
tool call fails with "unauthorized" almost always has a missing or stale key
here. Confirm the value with claude mcp get, then test the key against the
service directly.
Step 5 · Where are the MCP server logs?
A stdio server writes its diagnostic output to stderr, and the host captures it. This is where the real error message lives.
If you run Claude Desktop alongside Claude Code, its MCP logs are written to disk and you can tail them:
tail -n 20 -F ~/Library/Logs/Claude/mcp*.logOn Windows the same logs live under %APPDATA%\Claude\logs. The log captures the
connection events, configuration problems, runtime errors, and the message
exchange. Read the last failure before the disconnect. It usually names the exact
problem: a module that would not load, a port already in use, a file it could not
open.
Why does my MCP handshake never complete?
There is one failure that produces no clear error and confuses every beginner: a
stdio server must never write to stdout. The protocol uses stdout
for its JSON-RPC messages. A stray print or console.log in the server, or a
startup banner from a dependency, injects junk into that stream and corrupts the
connection.
If the server starts but the handshake never completes, suspect stdout pollution. Logs go to stderr; data goes to stdout; nothing else goes to stdout. If you wrote the server, move every log line to stderr. If you did not write it, an upstream dependency printing a banner is the usual culprit, and the project's issue tracker is the place to look.
Step 6 · How do I test an MCP server on its own?
Once the config looks right, prove the fault is in the wiring by running the server outside Claude Code. Two ways:
Run the exact command by hand. Take the command and args from claude mcp get
and run them in your shell. If the process exits immediately or prints an error,
the problem is the server or its environment, not Claude Code. Fix it here, where
the feedback loop is tight, then add it back.
Use the MCP Inspector. The Inspector is the interactive testing tool from the MCP project. It connects to a stdio or HTTP server, lists the tools, prompts, and resources, and lets you invoke them while you watch the message stream. It is the recommended first stop for any server you are debugging:
npx @modelcontextprotocol/inspector node /srv/mcp/index.jsPass the same command and args you would give Claude Code. If the Inspector connects and the tools work, your server is healthy and the bug is in how Claude Code launches it: revisit the path, args, and env. If the Inspector cannot connect either, the bug is in the server.
Why won't my remote MCP server connect?
The steps above target stdio servers, which are local processes. A remote server over HTTP fails for different reasons:
- Authentication. When a remote server returns
401or403, Claude Code flags it in/mcpso you can complete the OAuth flow. Run/mcpand authenticate. A server stuck pending is usually waiting on this. - Reconnection. If an HTTP server drops mid-session, Claude Code reconnects
automatically with exponential backoff, up to five attempts starting at one
second and doubling each time. After five failures it is marked failed and you
retry from
/mcp. Stdio servers are local processes and are not reconnected automatically; restart the session. - The URL and transport. You add a remote server with the transport set
explicitly, for example
claude mcp add --transport http my-api https://api.example.com/mcp. A wrong path or scheme produces a connection error you can confirm with standard HTTP tooling likecurl.
Let Claude work the checklist with you
You do not have to run every step by hand. Hand Claude Code the symptom and the config and let it reason through the chain:
A fast triage order
When you are in a hurry, run the chain in this order and stop at the first thing that is wrong:
claude mcp get <name>· read the stored config; catch typos here.- Command · does the binary resolve, and is it executable? Use an absolute path.
- Paths · is every path in args and env absolute, not relative?
- Env · are required keys passed with
--envor in.mcp.json? - Logs · what is the last error on stderr before the disconnect?
- Standalone · does the Inspector connect? If yes, the bug is the wiring; if no, the bug is the server.
Most dead connections are a relative path, a missing key, or a stray write to stdout. Work the list in order and the failure names itself.