How to Test an MCP Server with MCP Inspector
Test a local MCP server in three passes before you add it to Claude Desktop or Cursor: open it in the MCP Inspector web UI, script the same checks with the Inspector CLI, and call every tool with bad input as well as good input.
Everything below was run on 2026-10-18 against two projects generated by MCP Server Generator, using @modelcontextprotocol/inspector 2.11.0, @modelcontextprotocol/sdk 1.32.1 (TypeScript) and mcp 2.3.0 on Python 3.13. The output shown is what those runs printed.
What to test before you connect a host
A host such as Claude Desktop hides most errors behind “server disconnected” or a tool that never appears. Testing the server on its own first tells you whether a problem is in your code or in the host config. Check four things:
- Handshake: the server starts and answers
initializewith its name and atoolscapability. - Tool list:
tools/listreturns every tool, with the names, descriptions and required parameters you meant. The model reads exactly this. - Happy path: each tool returns a sensible result for realistic arguments.
- Failure paths: a missing argument, a wrong type and an unknown tool come back as readable errors, not a crash.
1. Get a server to test
Any stdio server works. To follow along exactly, generate one in the form on the homepage with two tools: get_weather (string city, required) and convert_temperature (number value, required; boolean to_fahrenheit, optional). Download the TypeScript or Python ZIP and install it:
# TypeScript (weather-ts/)
npm install
# Python (weather-py/)
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
The handlers echo their arguments until you replace the TODOs, which is fine for testing the wiring. The template guide explains every generated file.
2. Explore it in the Inspector web UI
Pass the Inspector the same command that starts your server. Run it from the project folder:
# TypeScript
npx @modelcontextprotocol/inspector npm start
# Python
npx @modelcontextprotocol/inspector .venv/bin/python server.py
The Inspector prints a local URL such as http://127.0.0.1:6274?MCP_INSPECTOR_API_TOKEN=… and opens it. Click Connect, open the Tools tab, click List Tools, pick a tool, fill in the arguments and run it. Read each description as if you were the model: would you know when to call this tool and what to pass?
Inspector 2.x declares Node.js 22.19 or newer for the machine running it. Your server has no such requirement; the Python project never touches Node except for this npx call. On a headless machine without an OS keychain, the Inspector warns that it stores secrets in a plain file under ~/.mcp-inspector/; that only matters for OAuth-protected remote servers.
3. Script the checks with the Inspector CLI
Add --cli and a --method, and the Inspector runs one request, prints the result and exits. A handshake-only probe:
$ npx @modelcontextprotocol/inspector --cli npm start --method initialize
{
"serverInfo": { "name": "weather-ts", "version": "1.0.0" },
"protocolVersion": "2025-11-25",
"capabilities": { "tools": { "listChanged": true } }
}
List the tools. Check that required parameters are in required and optional ones are not:
$ npx @modelcontextprotocol/inspector --cli npm start --method tools/list
{
"tools": [
{
"name": "get_weather",
"description": "Get current weather for a city",
"inputSchema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "City name, e.g. San Francisco" }
},
"required": ["city"],
...
Call a tool. --tool-arg takes key=value pairs and converts numbers and booleans for you:
$ npx @modelcontextprotocol/inspector --cli npm start \
--method tools/call --tool-name convert_temperature \
--tool-arg value=21.5 --tool-arg to_fahrenheit=true
{
"content": [
{ "type": "text", "text": "[convert_temperature] value=21.5, to_fahrenheit=true" }
]
}
The Python server gives the same answers with .venv/bin/python server.py in place of npm start. Its tools/list also includes an outputSchema, and each result carries structuredContent next to the text, because MCPServer describes the -> str return type. Both are expected.
4. Test the failure paths
Models send incomplete or mistyped arguments more often than people do. Call each tool without its required parameter:
$ npx @modelcontextprotocol/inspector --cli npm start \
--method tools/call --tool-name get_weather
{
"content": [{ "type": "text",
"text": "MCP error -32602: Input validation error: Invalid arguments for tool get_weather: Required at city" }],
"isError": true
}
{"error":{"code":"tool_is_error","message":"Tool 'get_weather' returned isError:true."}}
$ echo $?
5
The Python server rejects the same call with a Pydantic message, Error executing tool get_weather: 1 validation error for get_weatherArguments … Field required, and the same exit code. Calling a tool that does not exist (--tool-name get_forecast) exits 5 with tool_not_found. In both cases the model gets an error it can read and retry on, which is what you want.
One CLI trap: --tool-arg parses values as JSON, so city=10001 arrives as a number and the string schema rejects it (Input should be a valid string). Use --tool-args-json when a value must stay a string:
npx @modelcontextprotocol/inspector --cli .venv/bin/python server.py \
--method tools/call --tool-name get_weather --tool-args-json '{"city":"10001"}'
5. Check that stdout carries only protocol messages
A stdio server must write nothing but JSON-RPC to stdout. The Inspector is forgiving here: in our run it still listed the tools after we added a console.log("starting weather-ts") to the server, so a green Inspector check does not prove stdout is clean. Send a raw initialize and look at the first line instead:
INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}'
# TypeScript: run tsx directly, the way the README host config does
printf '%s\n' "$INIT" | node_modules/.bin/tsx src/index.ts 2>/dev/null | head -1
# Python
printf '%s\n' "$INIT" | .venv/bin/python server.py 2>/dev/null | head -1
A clean server prints a line starting with {"result": or {"jsonrpc":. With the stray console.log, the first line was starting weather-ts. Log with console.error or print(..., file=sys.stderr).
Run the probe on the tsx command, not npm start: npm writes its own > weather-ts@1.0.0 start banner to stdout. The Inspector tolerates it, which is why npm start is fine for testing, but the README host config launches node_modules/.bin/tsx directly so the banner never reaches Claude Desktop or Cursor.
6. Turn it into a smoke test for CI
The CLI exits 0 on success and non-zero on failure (5 when a tool returns isError or is missing), so a short script can guard every commit. Pin the Inspector version so a new release cannot change the result under you:
smoke.sh
#!/usr/bin/env bash
# smoke.sh: fail fast if the MCP server stops starting, loses a tool, or breaks a call.
set -euo pipefail
INSPECTOR="npx -y @modelcontextprotocol/inspector@2.11.0 --cli -q"
SERVER="${SERVER:-node_modules/.bin/tsx src/index.ts}" # Python: .venv/bin/python server.py
# 1. The server starts and completes the MCP handshake
$INSPECTOR $SERVER --method initialize > /dev/null
# 2. Every tool you depend on is listed
tools=$($INSPECTOR $SERVER --method tools/list --format json)
for name in get_weather convert_temperature; do
if ! grep -q "\"name\":\"$name\"" <<< "$tools"; then
echo "missing tool: $name" >&2
exit 1
fi
done
# 3. A known-good call succeeds (the CLI exits 5 if the tool returns isError)
$INSPECTOR $SERVER --method tools/call --tool-name get_weather \
--tool-args-json '{"city":"Paris"}' > /dev/null
echo "smoke test passed"
Run it as ./smoke.sh in the TypeScript project or SERVER=".venv/bin/python server.py" ./smoke.sh in the Python one. Both printed smoke test passed. After we renamed one tool, the script stopped with missing tool: convert_temperature and exit code 1. Replace the tool names and the sample call with your own.
Common test failures and fixes
| What you see | Likely cause | Fix |
|---|---|---|
{"error":{"code":"error","message":"Connection closed"}} | Server exited during startup. We got this by launching the Python server with a system python3 that had no mcp installed | Use .venv/bin/python, or run npm install; run the command by hand to read the traceback |
A tool is missing from tools/list | Typo in the name, or the registration is never reached | Compare against the generated README tool list; restart the Inspector after edits |
Input should be a valid string for a value you typed as text | --tool-arg parsed it as a number | Use --tool-args-json |
| Works in the Inspector, missing in the host | Relative path, command not on the host's PATH, or a missing env value | Use absolute paths from the README; see the connect guide |
Python tool returns only Error executing tool … | An ordinary exception in the handler; mcp 2.x hides its text from the model | Raise ToolError with a clear message (example in the internal API guide) |
This guide covers local stdio servers, which is what the generator writes. A remote server on Streamable HTTP is tested the same way with --transport http --server-url https://…/mcp, plus whatever authentication it needs. The Inspector checks protocol behaviour, not whether a model picks the right tool; for that, try real prompts in the host once these checks pass.
Generate a server that passes these checks
Describe your tools in the form and download a TypeScript or Python project with typed schemas, an Inspector command in the README and absolute-path host configs. Free, no account, no OpenAPI.
FAQ
How do I test an MCP server without Claude Desktop or Cursor?
Run it under the MCP Inspector: npx @modelcontextprotocol/inspector followed by the command that starts your server, for example npm start or .venv/bin/python server.py. The Inspector launches the server over stdio the way a host does, so you can list and call tools without editing any host config.
What is the difference between the Inspector web UI and the CLI?
Both use the same connection code. The web UI is for exploring by hand: connect, open the Tools tab, fill in arguments and read results. The CLI (--cli) runs one method such as tools/list or tools/call, prints the result and exits with a status code, which makes it the right tool for scripts and CI.
Why does the Inspector say Connection closed?
The server process exited before the MCP handshake finished. The usual cause is launching it with an interpreter that does not have the dependencies installed, such as a system python3 instead of the project's .venv/bin/python, or a TypeScript project where npm install was never run. Run the same command by hand to see the traceback.
Why does --tool-arg turn my string into a number?
The Inspector CLI parses --tool-arg values as JSON where it can, so city=10001 arrives as the number 10001 and a string parameter rejects it. Pass the arguments with --tool-args-json and a JSON object, for example '{"city":"10001"}', to keep the value a string.
Which Node.js version does MCP Inspector need?
MCP Inspector 2.x declares Node.js 22.19 or newer in its package engines field. Your server can still run on an older Node or on Python; the requirement only applies to the machine running the Inspector.
If my server passes in the Inspector, will it work in Claude Desktop?
It means the server itself is correct. Hosts can still fail on their side: a relative path in the config, a command that is not on the host's PATH, or a missing env value. Use the absolute-path config from the generated README and check the host's MCP log if the tools do not appear.