Large Language ModelsGenerate imagesGenerate videos

MCP Inspector npx and CLI: How to Test an MCP Server

MCP Inspector v2 plays the client so you can test an MCP server on its own. This article shows how to launch it with npx, pass arguments and environment variables, use a config file, call tools from the CLI with JSON arguments, read exit codes, run checks in CI with jq, and fix stdout and transport errors.

MCP Inspector npx and CLI: How to Test an MCP Server
Cristian Da Conceicao
Founder of Picasso IA

An MCP server can start without a single error and still be useless. The process is running, the log is quiet, and the client you plug it into shows an empty tool list or a vague "failed to connect" message. Before you blame the client, test the server on its own. MCP Inspector is the Model Context Protocol project's own tool for that job: it plays the role of the client, performs the handshake, and lets you list and call everything your server exposes. This article shows how to launch it with npx, how to drive it from the CLI, how to read its exit codes, and how to fix the errors that waste the most time.

💡 Version check: Most tutorials online describe Inspector v1. The latest release on npm at the time of writing is 2.9.0, and v2 changed ports, environment variables, flags and exit codes. Every command below follows the v2 documentation.

What MCP Inspector Does

Inspector is an MCP client built for debugging. It starts your server (stdio) or connects to it (HTTP or SSE), runs the initialize handshake, and shows you exactly what comes back. There is no language model in the loop, so when something fails you know the fault sits in the server or the connection, not in prompt behavior.

Overhead view of a desk with a laptop terminal, a sketch of two connected boxes and an espresso cup

The Handshake It Checks

The first call, initialize, proves that the server speaks MCP. The reply carries four things worth reading line by line:

  • serverInfo: the name and version your server reports.
  • protocolVersion: the protocol revision both sides agreed on.
  • capabilities: which features exist, such as tools, resources and prompts.
  • instructions: optional text the server hands to clients.

If capabilities has no tools entry, no client will ever show a tool, however many you registered in code. That single check explains a large share of "my tools don't appear" reports.

Web UI, CLI, and TUI

One package, three front ends. The mode flag must come first, right after the package name.

ModeCommandBest for
Web UInpx @modelcontextprotocol/inspectorPoking at a server by hand
CLInpx @modelcontextprotocol/inspector --cliScripts, quick checks, CI
TUInpx @modelcontextprotocol/inspector --tuiStaying inside the terminal

Use the web UI while you build, and the CLI when you need an answer you can repeat.

Launch It With npx

There is nothing to install. npx downloads the package, runs it, and passes everything after the package name to the server you want to test. A sensible first run takes one line and one minute.

Close-up of hands typing at a laptop with a dark terminal window blurred behind

Node Version and Ports

v2 needs Node.js 22.19.0 or newer. Run node --version before anything else, because an older runtime is the first thing to rule out.

Port and variable changes bite people who follow older posts, so here is the short comparison:

SettingInspector v1Inspector v2
Node.js22.7.5 or newer22.19.0 or newer
Web UI port62746274
Proxy port6277Removed, there is no proxy
Auth token variableMCP_PROXY_AUTH_TOKENMCP_INSPECTOR_API_TOKEN (old name still works as a fallback)
Config file--config, read only--config (read only) or --catalog (writable)
Tool arguments--tool-arg--tool-arg and --tool-args-json
Failing tool callThe shell chain kept goingExit code 5 stops it

Change the web UI port with CLIENT_PORT, a fixed integer between 1 and 65535. v2 also reserves 6275 for the MCP Apps sandbox and 6278 for the app-origin server, so keep both free.

CLIENT_PORT=6280 npx @modelcontextprotocol/inspector node build/index.js

On Windows PowerShell, set the variable first with $env:CLIENT_PORT = "6280" and then run the same npx line.

Pass Arguments and Environment Variables

For a built Node server, put the command right after the package name:

npx @modelcontextprotocol/inspector node build/index.js

Environment variables go in with -e:

npx @modelcontextprotocol/inspector -e API_TOKEN=your-token -- node build/index.js

A TypeScript server without a build step works the same way, for example npx @modelcontextprotocol/inspector tsx src/index.ts. Most projects wrap the line in an npm script so the whole team runs the same command:

{
  "scripts": {
    "inspect": "npx @modelcontextprotocol/inspector node dist/index.js"
  }
}

💡 The double dash flips meaning. In web and TUI mode, everything after -- goes to your server. In CLI mode, everything before -- is the target and everything after it is an Inspector option. In CLI mode the server command must also come first: --cli --method tools/list node build/index.js silently drops the target.

The Token Behind the UI

v2 creates a random API token on every launch and requires it on every /api/* route. A page opened without it is rejected. Set MCP_INSPECTOR_API_TOKEN yourself if you want a stable value, and relaunch if a tab complains, because the old token died with the old process.

The web server binds to 127.0.0.1 by default through HOST. Opening it to other interfaces takes an explicit DANGEROUSLY_BIND_ALL_INTERFACES, and DANGEROUSLY_OMIT_AUTH=true switches the token check off entirely. Inspector starts local processes on your behalf, so treat the token like a password and keep both overrides out of shared machines.

Use a Config File

Typing the command gets old once a server needs three arguments and two environment variables. Put them in a file and select the server by name. Two flags exist, and they exclude each other:

FlagWritten by InspectorIf the file is missing
--config <path>No, read onlyError
--catalog <path>Yes, editable in the web UICreated and seeded

The default catalog lives at ~/.mcp-inspector/mcp.json. Neither flag can be combined with an ad-hoc target on the same command line.

Over-the-shoulder view of a woman highlighting printed configuration lines at a standing desk

Stdio Entries

{
  "mcpServers": {
    "my-server": {
      "type": "stdio",
      "command": "node",
      "args": ["build/index.js"],
      "env": { "API_TOKEN": "your-token" },
      "cwd": "/path/to/server"
    }
  }
}

Keep command and each item of args as separate entries. Inspector spawns them directly instead of joining them into one string, which preserves argument boundaries when a path contains spaces.

HTTP and SSE Entries

{
  "mcpServers": {
    "remote-server": {
      "type": "http",
      "url": "https://mcp.internal.example/mcp",
      "headers": { "X-Tenant": "acme" }
    }
  }
}

The type field accepts stdio, http (Streamable HTTP) or sse. In the CLI you pick an entry with --server:

npx @modelcontextprotocol/inspector --cli --config ./mcp.json --server my-server --method tools/list

--server only selects a server in CLI mode. The web client warns and ignores it when you load a file, and the TUI rejects it as an unknown option.

Test an MCP Server From the CLI

CLI mode skips the browser and prints the reply on stdout, which makes it the right tool for quick checks and for anything you want to automate. Every command has the same shape: the target first, then --method, then whatever that method needs.

A developer leaning back at a desk, studying two plain terminal windows in late afternoon light

List Tools First

Always start by asking what the server thinks it offers:

npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list

Swap the method for resources/list or prompts/list to check the other two features. A remote server needs an address and a transport, and a bearer token if it is protected:

npx @modelcontextprotocol/inspector --cli \
  --transport http --server-url https://example.com/mcp \
  --header 'Authorization: Bearer <token>' \
  --method tools/list

Compare the names in the output with what your client expects. A tool that is registered as generateImage but requested as generate_image is a classic, and it only takes one command to spot.

Call a Tool With JSON Arguments

npx @modelcontextprotocol/inspector --cli node build/index.js \
  --method tools/call \
  --tool-name generate_image \
  --tool-args-json '{"prompt":"a ceramic mug on an oak desk, soft window light","aspect_ratio":"16:9"}'

--tool-args-json takes one JSON object and applies no coercion, so numbers stay numbers and booleans stay booleans. For a quick test, --tool-arg prompt="a red door" is shorter, but its values are parsed as JSON when they are valid, so a string that looks like a number becomes a number. When types matter, use the JSON form.

💡 Windows note: Windows PowerShell 5.1 strips the inner double quotes of JSON passed to native programs. Escape each one with a backslash, or fall back to --tool-arg for short values.

Read the Exit Codes

The CLI reports the outcome in its exit code, so a script never has to scrape text to know what happened.

A pilot's hand ticking items on a paper checklist inside a small cockpit

CodeMeaning
0Success
1Usage error or unexpected failure
2No MCP App found (--app-info probe)
3Authentication required
4Server unreachable: DNS, timeout or refused connection
5Tool returned isError: true, or the tool was not found
6Schema portability error with --strict

Code 5 matters most for testing. A tool that fails cleanly now fails the command, so inspector --cli ... && next-step stops where v1 would have carried on. Connections give up after 15 seconds by default for ad-hoc runs, and --connect-timeout <ms> raises that limit when your server loads a database or a model at startup.

Run Inspector Checks in CI

A useful smoke test asserts four things: the server connects, the tool exists, a valid call succeeds, and an invalid call fails. Four commands, no browser, and a broken release never reaches users.

Low-angle view down a cold aisle between two rows of black server racks

Pin the Version

Pin an exact version in CI, never a range such as @2.x, because flags and exit codes changed between major releases:

npx --yes @modelcontextprotocol/inspector@2.9.0 --cli node build/index.js --method initialize

Give each job its own token store so one run can never reuse another run's sign-in state:

export MCP_STORAGE_DIR="$(mktemp -d)"
export MCP_INSPECTOR_OAUTH_STATE_PATH="$MCP_STORAGE_DIR/oauth.json"

Assert With jq

Add --format json and the CLI prints one JSON object with a result field, ready for jq. Two traps are worth knowing. Never merge stderr into stdout with 2>&1 while parsing, because diagnostics land inside the JSON. And capture the exit status before piping, because a pipeline reports the status of its last command, which hides a failed CLI behind a happy jq.

#!/usr/bin/env bash
set -u
INSPECT="npx --yes @modelcontextprotocol/inspector@2.9.0 --cli node build/index.js"

# 1. The handshake works
$INSPECT --method initialize --format json > init.json || exit 1

# 2. The tool exists (status captured before the pipe)
tools=$($INSPECT --method tools/list --format json); code=$?
[ "$code" -eq 0 ] || { echo "tools/list failed with $code"; exit "$code"; }
echo "$tools" | jq -e '.result.tools | map(.name) | index("generate_image")' > /dev/null || exit 1

# 3. A valid call succeeds
$INSPECT --method tools/call --tool-name list_models --tool-args-json '{}' \
  --format json > call.json || exit 1

# 4. An invalid call fails
if $INSPECT --method tools/call --tool-name generate_image --tool-args-json '{}' \
  > /dev/null 2>&1; then
  echo "tool accepted empty input"; exit 1
fi

The fourth check is the one people skip. A server that accepts a missing prompt and returns a blank image will pass every positive test you write.

Fix the Errors You Will See

Most failures fall into a handful of patterns. Match the symptom first, then read the section that explains it.

A hand pulling one black cable out of a tangled bundle on a scratched wooden workbench

SymptomLikely causeFix
Exit code 4, connection timeoutThe server crashed on start or boots slowlyRun the server command alone, then raise --connect-timeout
Handshake dies with parse errorsSomething printed to stdoutSend logs to stderr
Transport error on a URLThe path does not end in /mcp or /sseAdd --transport http or --transport sse
Exit code 3The server wants a token or sign-inPass --header, and use --stored-auth-only in CI
Exit code 5Tool error, or a wrong tool nameRun tools/list and copy the exact name
UI rejects the pageStale API tokenRelaunch Inspector to get a fresh one

Stdout Pollution on Stdio

The stdio transport carries its JSON-RPC messages on stdout, and the protocol says a server must not write anything there that is not a valid MCP message. One stray console.log, a startup banner, or a dependency printing a warning corrupts the stream. The handshake then fails with parse errors or simply hangs.

Fix it by sending every log line to stderr (console.error in Node, sys.stderr in Python). To hunt the culprit, run the server command by itself: a healthy stdio server prints nothing until a client speaks to it.

Transport Not Detected

v2 no longer guesses. It infers the transport only when the URL path ends in /mcp or /sse, and anything else needs the flag spelled out:

npx @modelcontextprotocol/inspector --cli --server-url https://example.com/api \
  --transport http --method tools/list

When an error message means nothing to you, paste the stderr output and your tool schema into Claude Sonnet 5 or GPT 5.6 Sol and ask for the three likeliest causes. Both read stack traces well, and you still verify the answer with Inspector.

Testing an Image Generation Server

Servers that make media behave differently under test. Calls are slow, they can cost money, and the work usually runs in the background. PicassoIA's developer API shows the pattern clearly. It is Replicate-style: you create a prediction with POST /v1/models/{owner}/{name}/predictions on https://api.picassoia.com/v1, authenticate with a bearer token, poll GET /v1/predictions/{id}, and read the result when it finishes.

A camera on a tripod facing a white vase on a grey backdrop in a small photo studio

At the time of writing, the API and the MCP connection expose the same four models: PicassoIA Image, PicassoIA Image Editor Pro, PicassoIA Video and Seedance 2.5 Lite. An account allows 5 concurrent predictions, shared across tokens and MCP connections, with prompts of up to 4,000 characters.

💡 Test serially. A loop of tool calls in one Inspector session can fill all five slots and starve your real client. Run image tests one call at a time.

Async Tools Need a Status Tool

A tool that starts a job should return an ID within seconds, and a second tool should report progress. Test both halves on their own:

  • The start call returns quickly with an ID instead of holding the connection open for minutes.
  • The status call accepts that ID and reports a progress state and a final state.
  • A failed job comes back as a result with isError: true, not as a call that hangs.
  • A bad ID produces exit code 5, not a crash of the server process.
  • The output URL answers with status 200 and an image content type when you fetch it.

That last check is the cheapest one, and it catches the failure readers notice first: a broken image on a published page.

Make Your First Image Next

You now have a way to prove that a server works before anyone depends on it. The same habit pays off on the creative side: run a small test, read the result, and change one thing at a time.

A smiling designer examining printed landscape photographs at a bright studio table

Open Picasso IA and try the loop yourself. Write a one-sentence prompt in PicassoIA Image, refine the result with PicassoIA Image Editor Pro, then bring the still to life with PicassoIA Video. Change the lens, the light or the subject between runs and compare the outputs side by side. Five commands from this article are worth keeping next to your terminal:

  • --method initialize to confirm the handshake.
  • --method tools/list to confirm the tool names.
  • --method tools/call --tool-args-json to confirm the behavior.
  • --format json plus jq to assert on the answer.
  • The exit code, always read before the output.

Share this article