# MCP Inspector: Find the Cause Behind “Connection Closed”

> MCP Inspector turns a vague connection failure into server output you can act on. Run the exact command, check stderr, and fix the cause.

Claude Code can report `CONNECTION_CLOSED: Connection closed` when a server crashes at startup, a required environment variable is missing, or a relative script path points nowhere. Those are three different failures with the same message. Inside a session the same failure surfaces as `MCP error -32000: Connection closed` (as in [claude-code issue #78726](https://github.com/anthropics/claude-code/issues/78726)); start with the same check.

I ran seven broken stdio servers, plus a healthy one and one that logs to stdout, through the MCP Inspector, Claude Code and Codex to see which output actually separates them.

**The short answer:** to debug an MCP server, launch the Inspector CLI with the same command, arguments, working directory and environment as your client. Read the server’s stderr above the Inspector’s final error. A `✔ Connected` label or a configured entry proves less than it looks like: it may not prove tools loaded or a tool call works.

## What does MCP Inspector show that the client hides?

The MCP Inspector launches the server as a child process and speaks MCP to it. Its CLI prints the process’s own stderr, so a Python traceback or missing-key message can sit right above `Connection closed`. Claude Code showed that same connection message for three different faults in my lab; Codex sometimes printed nothing and had no tool available.

I ran each fault once on 8 November 2026: one minimal Python 3.15 JSON-RPC server on macOS 26 / Apple Silicon, with MCP Inspector CLI 2.13.0, Claude Code 2.1.319 and Codex CLI 0.167.0. This is a diagnostic sample, not a reliability trial. The Inspector was run from its cached package; `npx` reported 2.14.0 as the latest version that day.

| Deliberate fault                         | Inspector CLI 2.13.0                               | Claude Code 2.1.319                                             | Codex 0.167.0                                             |
| ---------------------------------------- | -------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------- |
| Healthy server                           | Lists `get_weather`                                | `✔ Connected`                                                   | Tool starts; this lab call was blocked by approval policy |
| One plain stdout log line                | Lists tool                                         | `✔ Connected`                                                   | Tool starts                                               |
| Crash at startup (stack trace on stderr) | Traceback, then `Connection closed`                | `CONNECTION_CLOSED: Connection closed`                          | No message; model says “no tool”                          |
| Missing `WEATHER_API_KEY`                | Error names the variable, then `Connection closed` | Same `CONNECTION_CLOSED`                                        | No message; “no tool”                                     |
| Wrong relative path                      | `can't open file`, then `Connection closed`        | Same `CONNECTION_CLOSED`                                        | No message; “no tool”                                     |
| Command not on `PATH`                    | `spawn weather-mcp-server ENOENT`                  | `ENOENT: Executable not found in $PATH`                         | No message; “no tool”                                     |
| Exits after initialize                   | Connection timed out after 15,000 ms               | Connected, then tools fetch failed: connection closed           | No message; “no tool”                                     |
| Schema lacks `"type": "object"`          | Returns `"tools": []`                              | Connected, then tools fetch fails with `inputSchema.type` error | Tool starts; schema accepted                              |
| 70-second startup                        | Connection timed out after 15,000 ms               | Times out after 30,000 ms                                       | No message; run ended at about 10 seconds                 |

If the client says connection closed, run the process directly through Inspector CLI; if Inspector names a missing file, key or executable, fix that input first.

![A decision path from a client error to Inspector output and the matching server fix](/blog/mcp-inspector/mcp-error-decision-tree.svg)

_Decision paths follow the 8 November 2026 lab output and the [MCP Inspector CLI documentation](https://github.com/modelcontextprotocol/inspector/blob/main/clients/cli/README.md), checked 8 November 2026._

## How do I run MCP Inspector against the same server?

Use `--cli` for a short, repeatable probe; use the web UI when you want to inspect schemas and manually call tools.

The lab used Node with `npx`, Python 3.15 and Inspector CLI 2.13.0. For the current package, the [Inspector README](https://github.com/modelcontextprotocol/inspector/blob/main/README.md) listed Node `^22.23.4 || ^24.22.1 || >=26.12.1` when checked 8 November 2026; pin `@2.13.0` below to match the measurements.

Start the Inspector web UI with `npx -y @modelcontextprotocol/inspector`. The web client uses port 6274 by default ([Inspector README](https://github.com/modelcontextprotocol/inspector/blob/main/README.md), [web client README](https://github.com/modelcontextprotocol/inspector/blob/main/clients/web/README.md), checked November 2026). If the command prints a different URL, use that address.

For a stdio server, substitute the exact command and arguments from your client:

```sh
npx -y @modelcontextprotocol/inspector@2.13.0 --cli python3 ~/mcp-lab/server.py ok --method tools/list
```

Expected result: exit code `0` and a JSON object containing `get_weather` with an object input schema. The command and result are from the 8 November lab. Replace `python3 ~/mcp-lab/server.py ok` with your own launcher and args; don't use a different shell command from the one your client runs.

To test a specific tool, request a call and pass the tool’s real argument names:

```sh
npx -y @modelcontextprotocol/inspector@2.13.0 --cli python3 ~/mcp-lab/server.py ok --method tools/call --tool-name get_weather --tool-arg city=Paris
```

Expected result: text `Paris: 14C, light rain` (run on 8 November). If the server needs a key, pass it to the Inspector process with `-e`. This is the lab server that died with `WEATHER_API_KEY is not set`:

```sh
npx -y @modelcontextprotocol/inspector@2.13.0 --cli python3 ~/mcp-lab/server.py missing_env -e WEATHER_API_KEY=replace-with-your-key --method tools/list
```

Expected output: the `get_weather` tool list, where the same command without `-e` ended in `Connection closed`. If that happens with your server, the client config is missing the variable: add it to the server's `env` block there. `-e KEY=value` is the Inspector CLI's documented environment option ([CLI reference](https://github.com/modelcontextprotocol/inspector/blob/main/clients/cli/README.md), checked November 2026). Keep a real key out of shell history and screenshots.

To reuse a server config instead, use a JSON file with `mcpServers`, `command`, `args`, `env` and `cwd`, then run `npx -y @modelcontextprotocol/inspector@2.13.0 --cli --config ./mcp.json --server my-server --method tools/list`; the [Inspector config guide](https://github.com/modelcontextprotocol/inspector/blob/main/docs/mcp-server-configuration.md) defines that shape. Expected result: a `tools` array for the named server.

## What should I fix when the Inspector prints an error?

Read the first server-side line before the Inspector’s closing message. It usually gives a more specific repair than the client does.

| Inspector output                                              | Likely cause                                                                | Fix to try                                                                                                                                   |
| ------------------------------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `ModuleNotFoundError` or another traceback                    | Process crashed while importing or initializing                             | Run the same command in a terminal and install/fix the named dependency in that runtime.                                                     |
| `WEATHER_API_KEY is not set` or equivalent                    | Client process did not receive required environment                         | Add the required value to the client’s `env` config; for Inspector, use `-e KEY=value`.                                                      |
| `can't open file` / `ENOENT`                                  | Bad script path or executable missing from the client’s `PATH`              | Use an absolute script path; configure the executable’s full path if the GUI client cannot find it.                                          |
| `Connection timed out after 15000 ms`                         | Server did not finish connecting before Inspector’s default ad-hoc deadline | Fix a hung startup first; a longer `--connect-timeout <ms>` did not get our 70-second server past a 60-second limit on `initialize` (below). |
| `"tools": []` or Claude’s `inputSchema.type` validation error | Tool schema is missing the required object type for that client             | Return `"type": "object"` and valid `properties` in each tool’s `inputSchema`; rerun `tools/list`.                                           |
| Process exits after the initialize response                   | Server died before discovery completed                                      | Keep the process alive and answer `tools/list`; inspect the exit path after initialization.                                                  |

Relative paths often work in your terminal because its working directory is the project root. A desktop client may start the same command from elsewhere.

The Inspector config supports an explicit `cwd`, while Claude Code sets `CLAUDE_PROJECT_DIR` for its spawned server; use the configured working directory or an absolute script path ([Inspector config guide](https://github.com/modelcontextprotocol/inspector/blob/main/docs/mcp-server-configuration.md), [Claude Code MCP docs](https://code.claude.com/docs/en/mcp), checked November 2026).

For a missing executable, the lab showed Claude Code’s `ENOENT` message directly. If the command works in your terminal but not in the client, compare `command -v your-server` with the executable configured in the client; set an absolute executable path when needed.

On Windows, wrap `npx` as `cmd /c npx …`; Claude Code's docs issue [#47635](https://github.com/anthropics/claude-code/issues/47635) quotes the docs' warning that without it you get `Connection closed`.

## Why does one client connect while another drops the tool?

“Connected” may only mean the initialization handshake succeeded. In my run, Claude Code printed `! Connected · tools fetch failed` for both a server that exited after initialization and one whose tool schema lacked `"type": "object"`.

The second case exposed an actual compatibility failure: Anthropic rejected our production tool list with HTTP 400 because a schema omitted that field, while OpenAI-style endpoints accepted the same tool list. Pinning `zod@3.25.76` restored the object schema; Octomind now skips non-object schemas and logs a named error so the whole turn isn't rejected.

I build Octomind, so that production example is my own. Octomind’s `/mcp health` checks server health; `/mcp validate` checks tool parameter schemas.

Its `timeout_seconds` default is 30 and acts as an idle deadline that progress notifications reset, with an absolute cap at 20 times the configured value ([MCP tools docs](/docs/usage/07-mcp-tools), checked November 2026). Those checks help when the server is configured in Octomind; Inspector is the direct probe when you need to reproduce the child process outside a client.

The lab also challenged the usual stdout diagnosis. A plain `Starting weather server v1.2 ...` line did **not** break Inspector, Claude Code or Codex in this test.

That result doesn't make stdout logging safe: stdio uses stdout as the JSON-RPC channel, and the [MCP TypeScript SDK docs](https://ts.sdk.modelcontextprotocol.io/v2/serving/stdio) explain that non-JSON output can break parsing.

The Claude Code [issue #47635](https://github.com/anthropics/claude-code/issues/47635) requested clearer malformed-stdout troubleshooting and was closed as not planned; the issue is context, not a reproduced bug report for every client. Send logs to stderr, but when one harmless line appears to work, don't stop checking other causes.

## Which timeout should I change?

Only raise a timeout after the Inspector confirms that the server eventually responds. A missing executable, crash or missing key will not be repaired by waiting longer.

In my lab, Inspector CLI 2.13.0 used a 15,000 ms default `--connect-timeout`; Claude Code 2.1.319 reported 30,000 ms; the Codex 0.167.0 run ended after about 10 seconds without an error message. Those are measurements from this version set, not universal defaults.

For documented settings, Claude Code uses `MCP_TIMEOUT` for server startup and `MCP_TOOL_TIMEOUT` for tool execution; per-server `timeout` in `.mcp.json` overrides the latter. Codex exposes `startup_timeout_sec` and `tool_timeout_sec` in a server's `[mcp_servers.<name>]` table ([Claude Code MCP docs](https://code.claude.com/docs/en/mcp), [Codex config reference](https://developers.openai.com/codex/config-reference), checked November 2026).

I didn't rerun Claude Code or Codex with new values.

The Inspector's own knobs didn't stretch far enough. I gave the 70-second server a 90-second limit two ways: the `--connect-timeout 90000` flag, and a config file with `connectionTimeout` and `requestTimeout` both at 90000, the fields the [Inspector config guide](https://github.com/modelcontextprotocol/inspector/blob/main/docs/mcp-server-configuration.md) describes. Both runs failed after about 61 seconds:

```text
{"error":{"code":"error","message":"Request timed out after 1m00s (initialize). 1 request is unanswered: initialize (sent 1m00s ago). No response has been received this session.","cause":"Request timed out"}}
```

In Inspector 2.13.0 the first request has a 60-second ceiling that those settings didn't lift. A server that needs more than a minute before it answers `initialize` can't be probed this way, and Claude Code and Codex gave up sooner in the lab.

Make the server answer `initialize` first and do the slow work afterwards: download packages ahead of time, and load models lazily on the first tool call. A slow first tool call can then report progress, which resets idle deadlines in clients that support it.

## How do I know the server is healthy, and how do I undo the probe?

For this tutorial, healthy means `tools/list` returns the expected tools and `tools/call` returns the expected result. A client’s status is one clue, not the complete check.

Codex `mcp list` reports configuration (`enabled` in this lab), while a `codex exec` that can't see the server may say nothing about why. Claude Code's `claude mcp list` performs a health check ([Claude Code MCP docs](https://code.claude.com/docs/en/mcp), checked November 2026); in the lab, its `! Connected · tools fetch failed` line still meant no tools were loaded.

If you only ran Inspector CLI commands above, there is no client config to undo. Stop the web Inspector with `Ctrl+C`. If you added a temporary Claude Code entry, remove it with `claude mcp remove <name>`; for Codex, remove that server’s `[mcp_servers.<name>]` table from `~/.codex/config.toml` ([Claude Code MCP docs](https://code.claude.com/docs/en/mcp), [Codex MCP docs](https://developers.openai.com/codex/mcp), checked November 2026).

Keep a copy of the relevant config before removing a server you already depend on.

If the process connects and then dies later, check the machine’s memory limit and kernel logs. We saw a memory MCP server killed by the kernel’s OOM handler on a 1 GB cloud machine: its two default models needed about 1.1 GB and 547 MB, and the memory peak reached the 1 GB limit. Smaller defaults brought peak use to 542 MB.

This is a separate “connected, then closed” failure; if you see a sudden mid-session transport close, check whether the operating system killed the process before changing a startup timer.

For a server you are still building, start with the [custom MCP server tutorial](/blog/build-custom-mcp-server-tutorial). For the protocol context, see [what MCP is](/blog/what-is-model-context-protocol).
