# Fix an MCP server that will not connect

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](/guides/mcp-fundamentals)
and [MCP configuration](/guides/mcp-configuration) first. This guide assumes you
[added a server](/guides/install-your-first-mcp-server) and it shows up broken.

## How do I check an MCP server's status?

Inside a Claude Code session, run the status panel:

```bash
/mcp
```

The `/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:

```bash
claude mcp list
claude mcp get my-server
```

`claude mcp list` prints every configured server and whether it connected.
`claude mcp get ` 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:

```bash
claude mcp add my-server -- npx -y @scope/my-mcp-server
```

The `--` separator splits Claude Code's own options from the command it runs.
Everything after `--` is the command and its args, passed through untouched.With `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/node` instead of
  `node`.
- **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.

```bash
claude mcp add files -- npx -y @scope/server-filesystem /home/me/projects/notes
```

Pass 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 the `claude mcp add` command. Confirm the stored args with
  `claude 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:

```bash
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.A 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:

```json
{
  "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:

```bash
tail -n 20 -F ~/Library/Logs/Claude/mcp*.log
```

On 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:

```bash
npx @modelcontextprotocol/inspector node /srv/mcp/index.js
```

Pass 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](/guides/local-vs-remote-mcp-servers) fails for different reasons:

- **Authentication.** When a remote server returns `401` or `403`, Claude Code
  flags it in `/mcp` so you can complete the OAuth flow. Run `/mcp`
  and 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 like `curl`.

## 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:

```text
My MCP server "my-server" shows as failed in /mcp.

Run `claude mcp get my-server` and read back the command, args, env, and transport. Then work this checklist with me: (1) confirm the command resolves to a real executable and is on the PATH; (2) check that every path in the args and env is absolute; (3) check that required env vars are present; (4) tell me where the logs for this server are and what to look for; (5) give me the exact command to run the server standalone and to launch it under the MCP Inspector.

For each step, tell me what you found and what to fix. Do not change the config until I confirm.
```

## 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:

1. `claude mcp get ` · read the stored config; catch typos here.
2. **Command** · does the binary resolve, and is it executable? Use an absolute
   path.
3. **Paths** · is every path in args and env absolute, not relative?
4. **Env** · are required keys passed with `--env` or in `.mcp.json`?
5. **Logs** · what is the last error on stderr before the disconnect?
6. **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.
