Debugging MCP Tools: Logs, Traces, and Common Pitfalls
MCP servers run on stdio — no console to watch. Debugging requires different strategies. This guide covers them all.
The Stdio Challenge
MCP servers communicate via stdin/stdout with the client. console.log() goes to stdout and breaks the protocol.
Rule #1: Log to stderr
console.log("User requested weather for London");
console.error("User requested weather for London");
All MCP clients capture stderr for debugging. Use it.
Structured Logging
Go beyond console.error:
function log(level, message, data = {}) {
const entry = {
timestamp: new Date().toISOString(),
level,
message,
...data
};
console.error(JSON.stringify(entry));
}
log("info", "Tool called", {
tool: "get_weather",
args: { city: "London" }
});
Viewing Logs
Claude Desktop
Help → Developer → MCP Logs
Cursor
Settings → MCP → Click server → View Logs
Windsurf
~/.windsurf/logs/mcp.log
Manual (any client)
Since the server runs as a child process, redirect stderr:
node index.js 2> debug.log
Common Pitfalls & Fixes
1. "Tool not found"
Symptom: Agent says it can't find your tool.
Diagnosis:
server.setRequestHandler("tools/list", async () => {
log("debug", "tools/list called", { toolCount: TOOLS.length });
return { tools: TOOLS };
});
Fix: Check that tools/list returns the correct structure and tools/call handles the tool name.
2. "Connection closed unexpectedly"
Symptom: Client disconnects after startup.
Diagnosis: The server likely crashed.
process.on("uncaughtException", (err) => {
log("fatal", "Uncaught exception", { error: err.message, stack: err.stack });
process.exit(1);
});
process.on("unhandledRejection", (reason) => {
log("fatal", "Unhandled rejection", { reason: String(reason) });
process.exit(1);
});
3. Empty Response
Symptom: Tool runs but agent sees no output.
Diagnosis: Check the response format.
return { text: "Hello" };
return { content: [{ type: "text", text: "Hello" }] };
4. Schema Mismatch
Symptom: Agent passes wrong argument types.
Diagnosis: Validate your inputSchema.
function validateArgs(schema, args) {
if (schema.required) {
for (const field of schema.required) {
if (!(field in args)) {
throw new Error(`Missing required field: ${field}`);
}
}
}
}
Debugging Checklist
When your tool doesn't work:
- ✅
console.error for all logging (not console.log)
- ✅ Server starts without errors (
node index.js in terminal)
- ✅
tools/list returns valid JSON
- ✅
tools/call handles the tool name
- ✅ Response follows
{ content: [{ type, text }] } format
- ✅ Error messages are clear and structured
- ✅ Environment variables are set correctly
- ✅ Check client-specific logs
Interactive Debugging
For deep issues, use Node.js inspector:
node --inspect index.js
Then open chrome://inspect in Chrome. Set breakpoints in your tool handlers.
Debug Mode Flag
Add a debug mode to your server:
const DEBUG = process.env.MCP_DEBUG === "true";
function debug(...args) {
if (DEBUG) console.error("[DEBUG]", ...args);
}
debug("Handling tool:", name, "with args:", args);
Enable with: MCP_DEBUG=true in your client config env vars.
Debugging MCP tools takes practice, but with stderr logging and structured error handling, you'll catch issues fast.
#MCP #Tutorial #Debugging #mcpm #DevTools
Debugging MCP Tools: Logs, Traces, and Common Pitfalls
MCP servers run on stdio — no console to watch. Debugging requires different strategies. This guide covers them all.
The Stdio Challenge
MCP servers communicate via stdin/stdout with the client.
console.log()goes to stdout and breaks the protocol.Rule #1: Log to stderr
// ❌ WRONG — breaks MCP protocol console.log("User requested weather for London"); // ✅ RIGHT — goes to stderr, visible in logs console.error("User requested weather for London");All MCP clients capture stderr for debugging. Use it.
Structured Logging
Go beyond
console.error:function log(level, message, data = {}) { const entry = { timestamp: new Date().toISOString(), level, message, ...data }; console.error(JSON.stringify(entry)); } // Usage log("info", "Tool called", { tool: "get_weather", args: { city: "London" } }); // → {"timestamp":"2026-07-07T10:00:00Z","level":"info","message":"Tool called","tool":"get_weather","args":{"city":"London"}}Viewing Logs
Claude Desktop
Help → Developer → MCP Logs
Cursor
Settings → MCP → Click server → View Logs
Windsurf
~/.windsurf/logs/mcp.logManual (any client)
Since the server runs as a child process, redirect stderr:
Common Pitfalls & Fixes
1. "Tool not found"
Symptom: Agent says it can't find your tool.
Diagnosis:
// Add diagnostic logging to tools/list server.setRequestHandler("tools/list", async () => { log("debug", "tools/list called", { toolCount: TOOLS.length }); return { tools: TOOLS }; });Fix: Check that
tools/listreturns the correct structure andtools/callhandles the tool name.2. "Connection closed unexpectedly"
Symptom: Client disconnects after startup.
Diagnosis: The server likely crashed.
process.on("uncaughtException", (err) => { log("fatal", "Uncaught exception", { error: err.message, stack: err.stack }); process.exit(1); }); process.on("unhandledRejection", (reason) => { log("fatal", "Unhandled rejection", { reason: String(reason) }); process.exit(1); });3. Empty Response
Symptom: Tool runs but agent sees no output.
Diagnosis: Check the response format.
// ❌ Wrong — missing content wrapper return { text: "Hello" }; // ✅ Correct return { content: [{ type: "text", text: "Hello" }] };4. Schema Mismatch
Symptom: Agent passes wrong argument types.
Diagnosis: Validate your
inputSchema.// Add validation in tools/call function validateArgs(schema, args) { if (schema.required) { for (const field of schema.required) { if (!(field in args)) { throw new Error(`Missing required field: ${field}`); } } } }Debugging Checklist
When your tool doesn't work:
console.errorfor all logging (notconsole.log)node index.jsin terminal)tools/listreturns valid JSONtools/callhandles the tool name{ content: [{ type, text }] }formatInteractive Debugging
For deep issues, use Node.js inspector:
Then open
chrome://inspectin Chrome. Set breakpoints in your tool handlers.Debug Mode Flag
Add a debug mode to your server:
const DEBUG = process.env.MCP_DEBUG === "true"; function debug(...args) { if (DEBUG) console.error("[DEBUG]", ...args); } // In tools/call: debug("Handling tool:", name, "with args:", args);Enable with:
MCP_DEBUG=truein your client config env vars.Debugging MCP tools takes practice, but with stderr logging and structured error handling, you'll catch issues fast.
#MCP #Tutorial #Debugging #mcpm #DevTools