You built an MCP server. It works in your head. But Claude Desktop returns a cryptic error, or worse, silently ignores your tool. Local testing and debugging is where most MCP development time actually goes, and the tooling has gotten significantly better in 2026.
This guide covers the practical workflow: how to test tools without wiring up a full client, how to read what is happening on the wire, and how to fix the failures that trip up most developers.
The MCP Inspector
The MCP Inspector is the single most useful debugging tool in the ecosystem. It connects to your server, lists available tools and resources, and lets you call them manually with custom input.
Install it globally:
npx @modelcontextprotocol/inspector
This launches a browser UI on localhost:6274. Point it at your server:
npx @modelcontextprotocol/inspector node ./src/index.js
Or for Python servers:
npx @modelcontextprotocol/inspector python server.py
The Inspector shows you exactly what your server exposes. If a tool does not appear here, no client will see it either. Check the tool registration code before looking anywhere else.
What to verify in the Inspector
- Tool list loads without errors. If the tool list is empty, your server likely crashes during initialization. Check the Inspector’s stderr output.
- Input schemas match your expectations. Click a tool and inspect the JSON Schema. Mismatched types (string vs number, required vs optional) are a top source of silent failures.
- Tool calls return valid responses. Run each tool with sample input. Look for proper
contentarray responses, not raw strings or objects.
Structured Logging
Console.log works, but it pollutes stdout — and MCP uses stdout for protocol messages over stdio transport. Writing debug output to stdout will corrupt the JSON-RPC stream and crash the connection.
Two safe approaches:
1. Log to stderr:
console.error("[DEBUG] tool called:", toolName, input);
Stderr is not part of the MCP transport. The Inspector and Claude Desktop both surface stderr in their logs.
2. Use a file logger:
import { appendFileSync } from "fs";
function debug(msg: string) {
appendFileSync("/tmp/mcp-debug.log", `${new Date().toISOString()} ${msg}\n`);
}
Then tail the log in a separate terminal:
tail -f /tmp/mcp-debug.log
For Python servers, the same rule applies. Use sys.stderr.write() or a file handler — never print() to stdout.
import sys
def debug(msg):
print(msg, file=sys.stderr)
Testing Without a Client
You do not need Claude Desktop or any AI client to test basic MCP functionality. The protocol is JSON-RPC over stdio. You can drive it manually.
Direct stdin/stdout testing:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | node ./src/index.js
If your server responds with a valid initialize result, the basics work. Follow it with tools/list to verify tool registration.
For a faster feedback loop, write a small test script that sends JSON-RPC messages and validates responses:
import { spawn } from "child_process";
const server = spawn("node", ["./src/index.js"]);
server.stdout.on("data", (data) => {
const response = JSON.parse(data.toString());
console.error("Response:", JSON.stringify(response, null, 2));
});
const init = {
jsonrpc: "2.0",
id: 1,
method: "initialize",
params: {
protocolVersion: "2025-03-26",
capabilities: {},
clientInfo: { name: "test-harness", version: "1.0" },
},
};
server.stdin.write(JSON.stringify(init) + "\n");
This gives you a repeatable test harness without spinning up any UI.
Common Failure Patterns
Server starts but no tools appear:
Your server.tool() or equivalent registration call runs after server.connect(). Tool registration must happen before the server starts listening. Move your tool definitions above the connect/listen call.
“Tool not found” errors in Claude Desktop: The tool name in your server must exactly match what the client requests. Names are case-sensitive. Check for trailing spaces or typos in the tool name string.
Connection drops immediately:
Your server is likely crashing on startup. Run it directly in the terminal (node ./src/index.js) and read the stack trace. Common causes: missing environment variables, unresolved imports, or syntax errors that only surface at runtime.
Tool returns results but Claude ignores them:
Check your response format. MCP tools must return a content array with typed blocks:
{
"content": [
{ "type": "text", "text": "The result goes here" }
]
}
Returning a plain string or an object without the content wrapper will not work.
“Transport error” or “Connection refused”:
For stdio servers: verify the command path in your Claude Desktop config (claude_desktop_config.json). Relative paths resolve from Claude Desktop’s working directory, not yours. Use absolute paths.
For HTTP/SSE servers: verify the port is not already in use and the server is actually listening before the client tries to connect.
Claude Desktop Log Locations
When something fails in Claude Desktop specifically, check the logs:
- macOS:
~/Library/Logs/Claude/mcp*.log - Windows:
%APPDATA%\Claude\logs\mcp*.log
These logs show the raw JSON-RPC messages exchanged between Claude Desktop and your server. Search for error to find the failure point.
You can also enable verbose MCP logging in Claude Desktop’s developer settings (Settings > Developer > Enable MCP Logging) to capture the full protocol exchange.
A Debugging Checklist
When an MCP server is not working, run through this sequence:
- Does the server start without errors when run directly? (
node ./src/index.jsorpython server.py) - Does the MCP Inspector see your tools?
- Do tool calls return valid
contentarray responses in the Inspector? - Is your Claude Desktop config pointing to the right command and path?
- Are Claude Desktop’s MCP logs showing errors?
Most issues resolve at step 1 or 2. If you get to step 5 without finding the problem, the issue is likely in your tool’s response format or an unhandled exception during execution.
FAQ
Q: Can I use breakpoints and a debugger with MCP servers?
A: Yes, but only with the Inspector or a test harness — not with Claude Desktop directly. For Node.js, run node --inspect ./src/index.js and connect Chrome DevTools. For Python, use debugpy or pdb writing to stderr. Just remember that pausing execution will stall the MCP connection, so set breakpoints in your tool handler, not in the transport layer.
Q: How do I test MCP servers that require authentication?
A: The MCP Inspector supports passing environment variables and headers. For API-key auth, set the variable before launching: API_KEY=your-key npx @modelcontextprotocol/inspector node ./src/index.js. For OAuth flows, you will need a test token — generate one from your auth provider’s dashboard and pass it as an environment variable.
Q: My server works in the Inspector but fails in Claude Desktop. What gives?
A: The two most common causes: (1) different working directories, so relative file paths break, and (2) different environment variables, since Claude Desktop does not inherit your shell environment. Set env explicitly in your claude_desktop_config.json to match what the Inspector uses.