Sam Austinon October 22, 202616 min read

Testing and Debugging MCP Servers with MCP Inspector

Testing and Debugging MCP Servers with MCP Inspector
Contents

Figure 1: Testing and Debugging MCP Servers with MCP Inspector — read the transcript before you blame the client

One disclosure first: I could not run the Inspector for this article. It installs through npm, and this machine cannot reach the package registry, so everything below about the Inspector comes from its official docs. To test something real, I built two stand-ins for its main jobs: a protocol transcript tap and an automated test suite. Both ran against a demo server, and one of them found a real bug in code I had written earlier.

What the Inspector Is

The Inspector is the MCP project's developer tool for testing servers. One package, @modelcontextprotocol/inspector, gives you three clients that share the same core [1]:

Client What it is
Web A graphical UI in the browser. The default and the richest
CLI A scriptable client for CI and shell pipelines
TUI An interactive terminal UI

It needs Node 22.19.0 or newer and runs through npx. For a local stdio server you pass the launch command:

npx @modelcontextprotocol/inspector node path/to/server/index.js

For a remote one, add --server-url and --transport http. The Python SDK's mcp dev server.py opens the Inspector too, and the docs say it needs npx on your PATH [1].

The Web Client

The launcher prints a URL that carries a per-launch token. Open that URL, and do not type localhost:6274 from memory, because the backend guards its API with that token. The docs explain why: the Node server behind the page can spawn processes on your machine [2].

The tab bar adapts to your server. Tools, Prompts and Resources appear when the server declares them. Tools shows each schema as a form, so you can fill arguments and call it. Three monitoring tabs show the traffic:

  • Protocol: the JSON-RPC transcript, with requests paired to responses.
  • Network: the HTTP layer for HTTP servers, with status codes and headers.
  • Console: the stderr of a stdio server process.

Network and Console never appear together, since a server is one or the other. You can pin the monitoring tabs into a sidebar and watch traffic while you work in Tools [2].

The CLI: Your Tests in CI

The CLI sends one request, prints the result and exits. The docs list methods like tools/list, tools/call, resources/read and prompts/get, and a --format json option for piping into jq [3]. Exit codes tell a script why it failed:

Code Meaning
3 The server needs authentication
4 The server is unreachable
5 A tool error: isError: true came back, or the tool was not found
6 --strict found a tool-schema portability problem

A call that returns isError: true still prints its payload but exits 5, so an && chain stops on a failed call. That single behaviour is what makes the CLI worth wiring into CI [3].

What I Ran Instead, Part One: A Protocol Tap

The Protocol tab shows the conversation between client and server. I built a tiny relay that sits between them, copies every line unchanged, and logs it. I ran a client through it: list tools, call one, then call another with a wrong argument type. The log held eight lines:

  1. The first message was server/discover. There is no initialize handshake, as the 2026-07-28 spec says.
  2. The server's first reply took about one second, which is process startup. Later replies took roughly 3 to 4 milliseconds.
  3. The tools/list response was about 2.4 KB for six tools.
  4. The wrong-type call came back as an ordinary response with isError: true, not a protocol error.

That last point matters when you debug. A failed tool call looks like success at the transport level. You have to read isError.

What I Ran Instead, Part Two: A Test Suite

I wrote six automated checks using only the standard library, since pytest is not installed here. They run the server as a subprocess, the way a real client does:

  • Tool names use the characters the spec recommends.
  • Every tool has a description.
  • Stdout carries only protocol messages.
  • The first answer arrives within five seconds.
  • Expected failures give the model an actionable message.
  • A round trip returns the data you would expect.

Run against my earlier demo server, one test failed, and it was a real bug. My add_note tool rejected blank text by raising a plain ValueError. As I found in the server article, the SDK withholds a plain exception's message from the model, so the model saw only "Error executing tool add_note". I changed it to ToolError with a message that says what to send, and all six tests passed.

Then I made a second server with a stray print() at startup. This one taught me something. Five of the six tests still passed, because the SDK's client logs the bad line and carries on. Only the raw-stdout test failed. If you test only through the SDK's client, a stdout leak can slip through, and a stricter client may break on it. Keep one test that reads the raw bytes.

The suite also warns that all six tools have fewer than three sentences of description, while Anthropic's docs suggest at least three or four. I treat that as a warning, not a failure, and it is an honest finding about my own demo.

A Debugging Checklist

The MCP debugging guide and my runs point to the same short list [5]:

  1. Log to stderr, never stdout, on stdio servers. For HTTP servers, stderr is not captured by the client, so use your own log aggregation or OpenTelemetry. The guide says logging over the protocol (notifications/message) is deprecated as of 2026-07-28.
  2. Use absolute paths. The guide warns that a client may launch your server with an undefined working directory, such as / on macOS.
  3. Pass environment variables explicitly. Stdio servers inherit only a small set (the Inspector's docs list HOME, LOGNAME, PATH, SHELL, TERM and USER), plus whatever you configure [2].
  4. Restart the client after server code changes. For Claude Desktop, the guide says to fully quit and reopen it.
  5. Check versions with server/discover when a connection fails. An unsupported version returns error code -32022 and lists what the server supports.
  6. Test standalone first. The guide says to try the server in the Inspector before blaming the client.

The guide's workflow is simple: use the Inspector for basic testing, then test in your real client and watch its logs [5].

Security: Treat the Inspector Like a Tool With a Shell

The Inspector's own security page is blunt, and worth reading before you install it [4]. The key points:

  1. The web backend can spawn processes, and it binds to 127.0.0.1 by default. Binding all interfaces is refused unless you set DANGEROUSLY_BIND_ALL_INTERFACES=true.
  2. The served page discloses the token, so the bind address matters more than the token's value.
  3. DANGEROUSLY_OMIT_AUTH=true removes the guard completely. Never combine it with a non-loopback bind.
  4. Stdio servers run as you. Only connect ones you would trust with your files.
  5. On a machine with no OS keychain, such as a headless Linux box like mine, the Inspector falls back to a secrets file that is unencrypted unless you supply a key.
  6. In Docker, publish ports as 127.0.0.1:6274:6274. A bare -p 6274:6274 exposes the backend to your whole network.

Common Pitfalls

  1. Reading only the status code when a tool call fails at the application level.
  2. Testing solely through an SDK client, which forgives a stdout leak.
  3. Printing debug output to stdout on a stdio server.
  4. Reusing http://localhost:6274 without the launcher's token.
  5. Blaming the client before trying the server standalone in the Inspector.
  6. Leaving a plain exception where the model needs an actionable message.
  7. Running the Inspector with auth omitted on a shared host.

One Subscription Instead of Five AI Tools

Partner offer

GPTAstra Max: one subscription instead of five AI tools

Chat model, image model and video tool behind one interface with a lifetime licence — useful when you are building and debugging agents that call several of them.

Get GPTAstra Max

Affiliate link — we may earn a commission at no extra cost to you.

Wrapping Up

The Inspector gives you a fast way to poke a server by hand, read its traffic, and script checks in CI. Pair it with automated tests, because the most useful bugs I found were quiet ones: a hidden error message and a stdout leak the SDK client forgave.

Start with three steps this month. Open your server in the Inspector and read the Protocol tab. Add a raw-stdout test to your suite. Then replace every plain exception that a model should see with a clear, actionable error.

If you are still writing the thing you are testing, MCP server development in Python covers the first tools, and the client walkthrough shows the loop that these tests stand in for. Once a server ships, connecting it to Claude Desktop is the next failure surface.

Frequently Asked Questions

What is the MCP Inspector used for?

It is the MCP project's developer tool for testing servers, shipped as one package, @modelcontextprotocol/inspector, with three clients that share the same core: a browser UI, a scriptable CLI for CI and shell pipelines, and a terminal UI. The web client turns each tool schema into a form, shows the JSON-RPC transcript, the HTTP layer and the server's stderr, which makes it the fastest way to see what a server actually receives and returns.

Why does a failed MCP tool call look like a success?

Because a tool error is not a transport error. When a tool raises, the server answers with an ordinary JSON-RPC response carrying isError set to true, so the connection, the request shape and the status code all look healthy. You have to read the payload. The Inspector's CLI makes this visible by exiting with code 5 while still printing the result, which is why an && chain stops on a failed call.

How do I test an MCP server in CI?

Use the Inspector CLI, which sends one request, prints the result and exits, with a JSON output format you can pipe into jq. Its exit codes carry the reason: 3 for authentication required, 4 for unreachable, 5 for a tool error, and 6 when strict mode finds a schema portability problem. Pair that with a test suite that launches the server as a subprocess, and keep one test that reads the raw stdout bytes rather than going through an SDK client.

Is the MCP Inspector safe to run?

Treat it as a tool with a shell. The web backend can spawn processes on your machine, so it binds to 127.0.0.1 by default, and binding all interfaces is refused unless you set DANGEROUSLY_BIND_ALL_INTERFACES. The served page discloses the token, which is why the bind address matters more than the token. DANGEROUSLY_OMIT_AUTH removes the guard entirely, and stdio servers run with your permissions, so only connect servers you would trust with your files.

Why doesn't my MCP server log show up?

Two usual reasons. On a stdio server the client captures stdout as protocol traffic, so anything printed there is either dropped or treated as a parse error, and logging must go to stderr, which the Inspector shows in its Console tab. On an HTTP server, stderr is not captured by the client at all, so you need your own log aggregation or OpenTelemetry. Note also that logging over the protocol itself, via notifications/message, is deprecated as of 2026-07-28.

How do I diagnose a protocol version mismatch?

Ask the server what it supports. A modern server answers server/discover, and an unsupported version returns error code -32022 with the list of versions it does accept. That turns a vague connection failure into a specific one. It is also the reason the 2026-07-28 flow has no initialize handshake: the first message is server/discover, so a transcript that starts with initialize belongs to an older client or an older revision.

Sources

  • [1] MCP documentation, MCP Inspector — the three clients, Node requirement, npx launch and mcp dev: docs/2026-07-28/tools/inspector
  • [2] MCP documentation, Inspector web client — tabs, the launch token, Protocol/Network/Console, the stdio environment allow-list: tools/inspector/web
  • [3] MCP documentation, Inspector CLI client — methods, --format json, exit codes 3–6 and CI recipes: tools/inspector/cli
  • [4] MCP documentation, Inspector security — bind address, the two dangerous flags, secret storage, Docker port publishing: tools/inspector/security
  • [5] MCP documentation, Debugging guide — stderr logging, absolute paths, restarts, server/discover and -32022: tools/debugging

What are You Looking For?

esc