MCP Inspector is the reference debugging tool for Model Context Protocol servers, and you run it with npx @modelcontextprotocol/inspector. That command starts a local web UI at http://127.0.0.1:6274, where you connect a server, call its tools, read its resources and test its prompts. Add --cli for a scriptable client or --tui for a terminal UI. There is no install step, but the current release needs Node 22.19.0 or newer, according to the MCP Inspector docs.
I ran version 2.9.0 on an Apple M1 against a small Python FastMCP server over stdio, Streamable HTTP and SSE, and the outputs, exit codes and timings on this page come from that run. Three results matter most. v2 has no proxy on port 6277, so a "proxy session token" error means you are still on v1. The transport is inferred from the last path segment of the URL, so /mcp works and /mcp/ with a trailing slash fails. And the CLI exits with fixed codes from 0 to 5, which makes it usable in CI.
What MCP Inspector is and which version you are running
MCP Inspector is a developer client for the Model Context Protocol (MCP), the open protocol that lets AI applications call tools and read data from external servers. It ships as one npm package, @modelcontextprotocol/inspector, with three front ends behind one mcp-inspector binary: web (the default), CLI and TUI.
The version matters because many tutorials still describe v1. The latest tag on the npm registry is 2.9.0, a v2 rewrite published on 2026-09-30, and v2 changed exactly the parts people search for. This table condenses the official v1 to v2 migration guide.
v1 (v1-latest tag) | v2 (latest tag) | |
|---|---|---|
| Node floor | >=22.7.5 | >=22.19.0 |
| Processes and ports | UI on 6274 plus MCP proxy on 6277 | one server on 6274, MCP Apps sandbox on 6275 |
| Token variable | MCP_PROXY_AUTH_TOKEN | MCP_INSPECTOR_API_TOKEN |
| Modes | web, --cli | web, --cli, --tui |
| Server list | browser localStorage | catalog file ~/.mcp-inspector/mcp.json |
| CLI exit codes | 0 or 1 | 0 to 5 plus a JSON error line |
To stay on v1, pin it with npx @modelcontextprotocol/inspector@v1-latest. v1 now receives security fixes only.
When I checked, npm resolved latest to 2.9.0 and v1-latest to 1.0.2. I also tried Node v22.18.0, one release below the floor: a cold npx install printed 2 EBADENGINE warnings (for the Inspector and undici), yet the stdio tools/list call still exited 0. The migration guide warns that an older Node "fails later, obscurely", so I would upgrade anyway.
How to use MCP Inspector with a Python FastMCP server
The Inspector is a Node tool, but it inspects servers written in any language because it only needs the command that starts them. There is no separate Python build of the official Inspector. FastMCP is the most common Python framework for MCP servers, and the FastMCP docs note that its mcp.run() call defaults to stdio.
This is the whole server I tested: one tool, one resource and one prompt.
import sys
from fastmcp import FastMCP
mcp = FastMCP("inspector-demo")
@mcp.tool
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b
@mcp.resource("config://app")
def app_config() -> str:
"""Static app configuration."""
return '{"env": "dev", "version": "1.0"}'
@mcp.prompt
def review_code(code: str) -> str:
"""Ask for a code review."""
return f"Review this code and list bugs:\n\n{code}"
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "http":
mcp.run(transport="http", host="127.0.0.1", port=8000)
else:
mcp.run()Install FastMCP in a virtual environment, then hand the Inspector the Python command:
uv venv venv && uv pip install --python venv/bin/python fastmcp
npx @modelcontextprotocol/inspector venv/bin/python server.pyFastMCP also has its own launcher, fastmcp dev inspector server.py, which starts your server together with the Inspector. In my run, FastMCP 4.0.10 pulled in the mcp SDK 2.2.0. Its fastmcp dev inspector --help still describes --server-port as the "Port for the MCP Inspector Proxy server", and the source sets SERVER_PORT. The package spec it passes to npx has no version pin, so it launches the v2 Inspector. My reading is that in v2 SERVER_PORT only moves the MCP Apps sandbox, so that flag no longer changes anything you browse.
Connecting over stdio, Streamable HTTP or SSE
Stdio is the default: the positional command is what the Inspector spawns. For a running server, pass its URL. With no --transport flag, v2 infers the transport from the URL's last path segment and nothing else.
To see how strict that is, I ran the same FastMCP server with transport="http" on port 8000 and transport="sse" on port 8001, then pointed the CLI at each URL shape. Nothing listened on port 8999.
URL passed to --cli | --transport | Result |
|---|---|---|
http://127.0.0.1:8000/mcp | none | connected as Streamable HTTP, exit 0 |
http://127.0.0.1:8000/mcp/ | none | exit 1: "Transport type not specified and could not be determined from URL" |
http://127.0.0.1:8000 | none | exit 1, same message |
http://127.0.0.1:8000/mcp/ | http | connected, exit 0 |
http://127.0.0.1:8001/sse | none | connected as SSE, exit 0 |
http://127.0.0.1:8000/mcp | sse | exit 1: "SSE error: Non-200 status code (400)" |
http://127.0.0.1:8999/mcp | none | exit 4, unreachable, ECONNREFUSED |
One documentation trap: the CLI README still says a bare remote URL defaults to SSE. In 2.9.0 it does not; the bare-host row above failed. Prefer Streamable HTTP for new servers, since FastMCP calls SSE a legacy transport kept for older clients.
MCP Inspector CLI mode: tools, resources and prompts
CLI mode connects, runs one --method, prints the result and exits, which suits terminals, CI jobs and coding agents. With --format json, the CLI docs say, you get one {"result": ...} object with no banners, ready for jq.
These are the calls I ran against the stdio server, with what came back:
| Task | Command after npx @modelcontextprotocol/inspector --cli venv/bin/python server.py | Output |
|---|---|---|
| Handshake | --method initialize | serverInfoinspector-demo, capabilities logging, prompts, resources, tools |
| List tools | --method tools/list | add, with an input and output schema |
| Call a tool | --method tools/call --tool-name add --tool-arg a=2 --tool-arg b=3 | text 5 and structuredContent.result5 |
| List resources | --method resources/list | config://app |
| Read a resource | --method resources/read --uri config://app | text/plain JSON string |
| List prompts | --method prompts/list | review_code |
| Get a prompt | --method prompts/get --prompt-name review_code --prompt-args code=print(1) | one user message |
--tool-arg JSON-parses each value, so count=1 arrives as a number. Use --tool-args-json '{"zip":"10001"}' when a string must stay a string.
Argument order is the classic mistake. Under --cli, the leading run of non-dash tokens is the server, so --cli --method tools/list venv/bin/python server.py drops the target and falls back to your catalog. In my run that command exited 1 with "No servers found in config file" and left an empty {"mcpServers":{}} catalog behind. When the server itself takes flags, the migration guide says to put them before a -- and the Inspector's options after it.
A warm CLI round trip took a median of 1413 ms over stdio, which includes spawning Python, and 834 ms over HTTP against a running server (five runs each).
Exit codes for CI and scripts
Every non-zero exit maps to a fixed failure class, and the CLI also writes one JSON line to stderr, so a script can branch on the cause instead of parsing prose.
| Code | Meaning | What triggered it in my run |
|---|---|---|
0 | success | every valid call |
1 | usage or unexpected error | no --method, bad URL shape, wrong transport |
2 | no MCP App on the tool | not exercised |
3 | server requires authentication | not exercised |
4 | server unreachable | nothing on port 8999 |
5 | tool error or tool not found | a=x for an int, and --tool-name nope |
A failing tool now fails the process. tools/call with a=x printed the result with isError true on stdout and still exited 5 with {"error":{"code":"tool_is_error"}}. A missing tool returned tool_not_found, also exit 5. In v1 a failing tool call exited 0.
A minimal CI check is mcp-inspector --cli <server> --method tools/list --format json | jq -e '.result.tools | length > 0'. For OAuth-protected servers, add --stored-auth-only so the job fails fast instead of waiting on a browser login.
Using a config file: --config vs --catalog
Both flags take the familiar mcpServers JSON shape, but they behave differently. --config is a read-only session file that must exist; --catalog is the Inspector's own writable list, created if missing, with ~/.mcp-inspector/mcp.json as the default.
This is the file I used, with one stdio entry and one Streamable HTTP entry:
{ "mcpServers": {
"demo-stdio": { "type": "stdio", "command": "/path/to/venv/bin/python", "args": ["/path/to/server.py"] },
"demo-http": { "type": "streamable-http", "url": "http://127.0.0.1:8000/mcp" }
} }Select an entry with --server, which only works under --cli:
npx @modelcontextprotocol/inspector --cli --config ./mcp.json --server demo-http --method tools/list
Both entries connected. --method servers/list returned demo-http and demo-stdio without connecting, and the file's hash was unchanged after the runs. A missing file failed with "Config file not found", and combining --config with a positional server failed with "--config cannot be combined with an ad-hoc server URL/command".
Ports, the session token and Docker
The official Inspector runs locally, not as a hosted website. The web launcher binds to 127.0.0.1 by default and guards every /api/* route with a bearer token in the x-mcp-remote-auth header. The environment variables reference says the token is random per launch unless you set MCP_INSPECTOR_API_TOKEN.
On my machine the web process listened on 127.0.0.1 ports 6274, 6275 and 6278, and the v1 proxy port did not answer. The banner printed a 64-character token both in the URL and on its own line, so open the exact printed URL. The page itself loads without it: GET / returned 200 and embedded the token in the page, while /api/config returned 401 without the header or with a wrong token, and 200 with it. Setting MCP_INSPECTOR_API_TOKEN made the banner use my fixed value, which gives you a stable URL. HOST=0.0.0.0 exited 1 with "Refusing to bind".
Because the page itself carries the token, anyone who can reach port 6274 can drive a backend that spawns processes. Keep it on loopback, as the official container recipe in the migration guide does:
docker run --rm -p 127.0.0.1:6274:6274 ghcr.io/modelcontextprotocol/inspector:latest
Publish 6275 as well if you test MCP Apps, and never set DANGEROUSLY_OMIT_AUTH on a reachable host.
Fixing common MCP Inspector errors
Most "MCP Inspector not connecting" reports fit a few patterns. The first three messages below come from v1; a Stack Overflow question on the proxy error has over 14,000 views and no accepted answer. The v2 causes come from the migration guide, and the last four messages are the exact text my run produced.
| Error you see | Likely cause | Fix |
|---|---|---|
| "Connection Error - Check if your MCP server is running and proxy token is correct" | v1 web UI could not reach its proxy with the token | upgrade to latest, open the exact printed URL, or set MCP_PROXY_AUTH_TOKEN on v1 |
| "Error Connecting to MCP Inspector Proxy" | v1 proxy on 6277 not running or blocked | v2 has no proxy; upgrade, or free port 6277 on v1 |
"PORT IS IN USE" on 6277 | an old v1 process still running | kill it; v2 does not use 6277 |
| "Transport type not specified and could not be determined from URL" | URL ends in neither /mcp nor /sse, or has a trailing slash | add --transport http or fix the path |
| "SSE error: Non-200 status code (400)" | SSE client pointed at a Streamable HTTP endpoint | use --transport http |
unreachable / ECONNREFUSED | server not running on that host and port | start it, check the port |
| "No servers found in config file" | --cli target placed after the flags | put the server command first |
On Windows the command is the same: install Node 22.19.0 or newer, then run npx @modelcontextprotocol/inspector from PowerShell or Command Prompt, followed by your server's start command. I did not test on Windows. A 2026 "Cannot find native binding" failure after an update was tracked in issue #1852 and closed as fixed, so update to the latest release before debugging further.
How I tested this
- Machine: Apple M1, macOS (Darwin 26.3, arm64).
- Versions: MCP Inspector 2.9.0 (npm
latest), Node v22.23.3 for the main runs and v22.18.0 for the below-floor check, Python 3.13.5, FastMCP 4.0.10 with themcpSDK 2.2.0. - Date: October 2, 2026.
- What I ran: a shell harness that installs the Inspector through
npxin an isolated directory, starts the FastMCP server above (the harness copy adds anssebranch on port 8001), runs every CLI method and error case shown on this page, times five warm round trips per transport, and probes the web launcher's ports and token checks with HTTP requests. - What I did not test: Windows or Linux, the TUI, clicking through the web UI, OAuth-protected servers (exit
3), MCP Apps (exit2), the Docker image, and v1 itself, which I only resolved on npm. - Raw record: every metric is in the public results.json.
Related coverage
- FastMCP vs MCP Python SDK: Which to Use in 2026
- FastMCP vs FastAPI-MCP: Which Python MCP Library?
- MCP vs RAG: Differences, Use Cases, and a 12-Query Test
References
- FastMCP docs - https://gofastmcp.com/deployment/running-server
- Inspector environment variables - https://github.com/modelcontextprotocol/inspector/blob/main/docs/environment-variables.md
- Inspector migration guide - https://github.com/modelcontextprotocol/inspector/blob/main/docs/v1-to-v2-migration.md
- MCP Inspector CLI docs - https://modelcontextprotocol.io/docs/2026-07-28/tools/inspector/cli
- MCP Inspector docs - https://modelcontextprotocol.io/docs/2026-07-28/tools/inspector
- npm registry - https://www.npmjs.com/package/@modelcontextprotocol/inspector

