MCP Inspector: How to Test and Debug an MCP Server

The MCP Inspector is the reference tool for poking at an MCP server without an AI in the way. How to run it, connect it over stdio or Streamable HTTP, sign in, read the traffic, script it, and get past the errors people hit first.

8 min read

The MCP Inspector is the official tool for testing and debugging an MCP server by hand. It acts as the client, so you can list a server’s tools, call them with arguments you type, and read every request and response, with no model deciding anything. Run it with npx @modelcontextprotocol/inspector followed by the command that starts your server (for stdio), or with --server-url and --transport http for a remote one. It needs Node 22.19 or newer, installs nothing permanently, and opens in your browser through a URL it prints. The same package also has a command-line mode for scripts and CI, and a terminal UI for when there is no browser.

Writing the server is covered in how to build an MCP server (TypeScript) and building an MCP server in Python. When a server works in the Inspector but an assistant still uses it badly, that is a different problem, and how to debug MCP tools takes it layer by layer. This page is about the Inspector itself.

What the Inspector is

One npm package, @modelcontextprotocol/inspector, with three clients behind one binary. The Inspector documentation (opens in a new tab) describes them as the web client (the default, and the richest), the CLI (--cli, machine-readable, made for CI and coding agents) and the TUI (--tui, an interactive terminal interface). All three share one core: the same transports, the same configuration files, the same stored sign-ins, and the same handling of old and new protocol versions. A connection that works in one works the same way in the others.

Run it locally

Terminal
# A local stdio server: everything after the package name is the command that starts it
npx @modelcontextprotocol/inspector node build/index.js
npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/my-repo

# A remote server over Streamable HTTP
npx @modelcontextprotocol/inspector --server-url https://api.example.com/mcp --transport http

# No target: open the UI and add servers there
npx @modelcontextprotocol/inspector

The command prints a URL with a one-time session token in it. Open that URL rather than typing localhost:6274 from memory: the small Node server behind the web client can start processes on your machine, so every call to it needs the token. It listens on localhost only and accepts requests only from its own origins, and it refuses to bind every network interface unless you set an environment variable whose name starts with DANGEROUSLY_. Take the hint.

Launched with no target, the web client keeps a server list in ~/.mcp-inspector/mcp.json and seeds it on first run with two samples: a filesystem server limited to /tmp and the “everything” reference server. They are useful for seeing what a healthy connection looks like before you try your own.

Connect a stdio server

For stdio, the Inspector starts your server as a child process and talks to it over standard input and output, exactly as a desktop client would. Three details save time:

  • Put -- before any flags meant for your server. Without it, a server argument such as --config is read as one of the Inspector’s own flags and never reaches the server.
  • Pass environment variables with -e KEY=value (repeatable) and the working folder with --cwd. A client launches servers with a thin environment, so testing with your full shell environment can hide a missing variable.
  • Read the Console tab. It shows the server process’s standard error, which is where a stdio server’s own logs and stack traces go, and it is the first place to look when a connection fails with no visible reason.

Connect over Streamable HTTP

--transport takes http for Streamable HTTP and sse for the older HTTP with Server-Sent Events transport, which you only need for servers that have not moved on (the difference is in MCP transports). Add static headers with --header "Name: Value".

An HTTP server also has a protocol era. The 2026-07-28 revision of MCP dropped the initialize handshake and sessions, so the Inspector treats each server as legacy (the default: a plain initialize), modern (only 2026-07-28, no fallback) or auto (probe with server/discover, fall back if that fails). The protocol eras page (opens in a new tab) explains why legacy is the default: a debugging tool should not send probes you did not ask for into the transcript you are trying to read. If your server supports both eras, test it in both from Server Settings.

Sign in to a protected server

You do not set up OAuth in advance. When a server answers 401, the Inspector reads its WWW-Authenticate header, discovers the authorization server, registers or identifies itself, opens the sign-in page in your browser, exchanges the code for tokens and retries the connection. The Inspector’s authorization guide (opens in a new tab) walks through each step, including step-up, where a request needs a scope the current token lacks and the Inspector asks for the extra scope without dropping the connection.

  • Callback addresses: the web client uses http://localhost:6274/oauth/callback; the CLI and TUI share http://127.0.0.1:6276/oauth/callback. If your identity provider needs redirect URIs registered in advance, register those exactly. localhost and 127.0.0.1 count as different URIs.
  • Tokens are stored in ~/.mcp-inspector/storage/oauth.json, keyed by server URL and written owner-only, so a sign-in done once in the web client is usable from the CLI.
  • The Connection Info panel shows what was discovered, the registered client, the granted scopes and the token state, with a button to clear the stored sign-in and start again.

Read the requests and responses

Which tabs appear depends on what the server declares. The ones you will use most:

  • Tools: pick a tool and the Inspector turns its input schema into a form, shows its description and annotations, calls it and renders the result, including structured content and images.
  • Resources and Prompts: list, read and fill in arguments, so you can see exactly what a prompt template produces.
  • Protocol: the JSON-RPC transcript, with each request paired with its response. This is the ground truth when a client and server disagree.
  • Network (HTTP servers only): status codes, headers and bodies. On modern connections the Mcp-* headers are highlighted.
  • Console (stdio servers only): the server’s standard error.

Pin the monitoring group and Protocol, Network, Console and Logs move into a sidebar, so you can watch traffic while you call tools. Secrets are masked in these views, and entries can be exported, which is handy when you need to attach a transcript to a bug.

The CLI: the same checks, scripted

Each CLI run connects, sends the one request you name with --method, prints the result and exits. The CLI reference (opens in a new tab) lists the methods: initialize as a connect-only probe, the list and get methods for tools, resources and prompts, and tools/call with --tool-name.

Terminal
# Does it start, and what does it say about itself?
npx @modelcontextprotocol/inspector --cli python server.py --method initialize

# Call a tool with exact arguments, as JSON
npx @modelcontextprotocol/inspector --cli python server.py \
  --method tools/call --tool-name add_task --tool-args-json '{"title":"Tag the release"}'

# In CI: never open a browser, fail at once if no token is stored
npx @modelcontextprotocol/inspector --cli https://api.example.com/mcp --transport http \
  --stored-auth-only --method tools/list --format json | jq -e '.result.tools | length > 0'

Prefer --tool-args-json to --tool-arg key=value, which parses each value as JSON and turns the string "012" into the number 12. Exit codes are stable: 3 when the server needs authentication, 4 when it cannot be reached, 5 when the tool returned isError: true or does not exist. On any failure the CLI also writes one line of JSON to standard error, so a script can branch on why it failed without parsing prose.

Common errors and what they mean

  • “Specify at most one of --web, --cli, or --tui.” You passed two mode flags. Mode flags also have to come first; anything after the first argument the launcher does not recognise goes to the client unchanged.
  • The browser page says it is unauthorised. You opened the address without the session token. Use the URL the command printed.
  • A stdio server connects and then hangs or shows garbled messages. Something is writing to standard output, which is the protocol channel. Move logging to standard error and check the Console tab.
  • Exit code 3 and auth_required in CI. No stored token for that URL. Sign in once in the web client on that machine, then run with --use-stored-auth or --stored-auth-only.
  • EADDRINUSE during sign-in. Another CLI or TUI sign-in holds port 6276. Finish it, or set a different callback with --callback-url.
  • The server works in legacy and fails in modern, or the reverse. It only speaks one era. Decide whether that is intended; MCP specification changes sets out what a dual-era server has to do.
  • The Inspector will not start at all. Check node --version; it needs 22.19 or newer.

Pointing it at a real remote server

A remote server that uses OAuth is a good test of the sign-in path. fenbs, a task board where AI assistants are members with roles, serves its tools at https://fenbs.ai/api/mcp. Point the CLI at it with --stored-auth-only and it exits with code 3, because the server answers 401 with a WWW-Authenticate header naming its protected-resource metadata. Open the same URL in the web client instead and the Inspector registers itself through the server’s dynamic client registration and runs the sign-in: fenbs’s approval page names the app asking (marked unverified), the board it will work in and the permissions you tick. Then the Tools tab lists every fenbs_ tool, and calling fenbs_whoami shows the role the token holds. A permission refusal comes back as a result with isError: true that names what was missing, which is what you would want to see before an assistant ever reads it.

Related

Stdio, Streamable HTTP and the old SSE transport: MCP transports. Fixing a tool an assistant misuses: how to debug MCP tools. The fenbs server and its tools: MCP docs.

Questions people ask.

Do I need to install the MCP Inspector?

No. It runs through npx, which downloads and runs the package on demand. You need Node 22.19 or newer. Installing it globally gives you an mcp-inspector command, which takes the same flags.

What port does the MCP Inspector use?

The web interface listens on port 6274 on localhost by default, and CLIENT_PORT changes it. The CLI and TUI receive OAuth sign-ins on 127.0.0.1 port 6276. Always open the URL the command prints, because it carries the session token.

Can the MCP Inspector test a Python MCP server?

Yes. It tests any server regardless of language: pass the command that starts it, such as python server.py or uv run mcp run server.py, or give it the URL of a server running over HTTP. The Python SDK also wraps it as uv run mcp dev server.py.

Can I run the MCP Inspector in CI?

Yes, with the --cli flag. Use --format json for output a script can read, check the exit code, and add --stored-auth-only so a job with no stored token fails straight away instead of waiting for a browser sign-in.

Start with one thing.

There is nothing to set up first. Write one line and you’ve started.