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
<span class="hljs-comment">
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"User requested weather for London"</span>);
<span class="hljs-comment">// ✅ RIGHT — goes to stderr, visible in logs</span>
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"User requested weather for London"</span>);
All MCP clients capture stderr for debugging. Use it.
Structured Logging
Go beyond console.error:
<span class="hljs-keyword">function</span> <span class="hljs-title function_">log</span>(<span class="hljs-params">level, message, data = {}</span>) {
<span class="hljs-keyword">const</span> entry = {
<span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>(),
level,
message,
...data
};
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(entry));
}
<span class="hljs-comment">
<span class="hljs-title function_">log</span>(<span class="hljs-string">"info"</span>, <span class="hljs-string">"Tool called"</span>, {
<span class="hljs-attr">tool</span>: <span class="hljs-string">"get_weather"</span>,
<span class="hljs-attr">args</span>: { <span class="hljs-attr">city</span>: <span class="hljs-string">"London"</span> }
});
<span class="hljs-comment">// → {"timestamp":"2026-07-07T10:00:00Z","level":"info","message":"Tool called","tool":"get_weather","args":{"city":"London"}}</span>
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:
<span class="hljs-comment">
server.<span class="hljs-title function_">setRequestHandler</span>(<span class="hljs-string">"tools/list"</span>, <span class="hljs-title function_">async</span> () => {
<span class="hljs-title function_">log</span>(<span class="hljs-string">"debug"</span>, <span class="hljs-string">"tools/list called"</span>, { <span class="hljs-attr">toolCount</span>: <span class="hljs-variable constant_">TOOLS</span>.<span class="hljs-property">length</span> });
<span class="hljs-keyword">return</span> { <span class="hljs-attr">tools</span>: <span class="hljs-variable constant_">TOOLS</span> };
});
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.<span class="hljs-title function_">on</span>(<span class="hljs-string">"uncaughtException"</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =></span> {
<span class="hljs-title function_">log</span>(<span class="hljs-string">"fatal"</span>, <span class="hljs-string">"Uncaught exception"</span>, { <span class="hljs-attr">error</span>: err.<span class="hljs-property">message</span>, <span class="hljs-attr">stack</span>: err.<span class="hljs-property">stack</span> });
process.<span class="hljs-title function_">exit</span>(<span class="hljs-number">1</span>);
});
process.<span class="hljs-title function_">on</span>(<span class="hljs-string">"unhandledRejection"</span>, <span class="hljs-function">(<span class="hljs-params">reason</span>) =></span> {
<span class="hljs-title function_">log</span>(<span class="hljs-string">"fatal"</span>, <span class="hljs-string">"Unhandled rejection"</span>, { <span class="hljs-attr">reason</span>: <span class="hljs-title class_">String</span>(reason) });
process.<span class="hljs-title function_">exit</span>(<span class="hljs-number">1</span>);
});
3. Empty Response
Symptom: Tool runs but agent sees no output.
Diagnosis: Check the response format.
<span class="hljs-comment">
<span class="hljs-keyword">return</span> { <span class="hljs-attr">text</span>: <span class="hljs-string">"Hello"</span> };
<span class="hljs-comment">// ✅ Correct</span>
<span class="hljs-keyword">return</span> { <span class="hljs-attr">content</span>: [{ <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Hello"</span> }] };
4. Schema Mismatch
Symptom: Agent passes wrong argument types.
Diagnosis: Validate your inputSchema.
<span class="hljs-comment">
<span class="hljs-keyword">function</span> <span class="hljs-title function_">validateArgs</span>(<span class="hljs-params">schema, args</span>) {
<span class="hljs-keyword">if</span> (schema.<span class="hljs-property">required</span>) {
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> field <span class="hljs-keyword">of</span> schema.<span class="hljs-property">required</span>) {
<span class="hljs-keyword">if</span> (!(field <span class="hljs-keyword">in</span> args)) {
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Missing required field: <span class="hljs-subst">${field}</span>`</span>);
}
}
}
}
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:
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">DEBUG</span> = process.<span class="hljs-property">env</span>.<span class="hljs-property">MCP_DEBUG</span> === <span class="hljs-string">"true"</span>;
<span class="hljs-keyword">function</span> <span class="hljs-title function_">debug</span>(<span class="hljs-params">...args</span>) {
<span class="hljs-keyword">if</span> (<span class="hljs-variable constant_">DEBUG</span>) <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"[DEBUG]"</span>, ...args);
}
<span class="hljs-comment">
<span class="hljs-title function_">debug</span>(<span class="hljs-string">"Handling tool:"</span>, name, <span class="hljs-string">"with args:"</span>, 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
<span class="hljs-comment">// ❌ WRONG — breaks MCP protocol</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"User requested weather for London"</span>); <span class="hljs-comment">// ✅ RIGHT — goes to stderr, visible in logs</span> <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"User requested weather for London"</span>);All MCP clients capture stderr for debugging. Use it.
Structured Logging
Go beyond
console.error:<span class="hljs-keyword">function</span> <span class="hljs-title function_">log</span>(<span class="hljs-params">level, message, data = {}</span>) { <span class="hljs-keyword">const</span> entry = { <span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>(), level, message, ...data }; <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(entry)); } <span class="hljs-comment">// Usage</span> <span class="hljs-title function_">log</span>(<span class="hljs-string">"info"</span>, <span class="hljs-string">"Tool called"</span>, { <span class="hljs-attr">tool</span>: <span class="hljs-string">"get_weather"</span>, <span class="hljs-attr">args</span>: { <span class="hljs-attr">city</span>: <span class="hljs-string">"London"</span> } }); <span class="hljs-comment">// → {"timestamp":"2026-07-07T10:00:00Z","level":"info","message":"Tool called","tool":"get_weather","args":{"city":"London"}}</span>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:
<span class="hljs-comment">// Add diagnostic logging to tools/list</span> server.<span class="hljs-title function_">setRequestHandler</span>(<span class="hljs-string">"tools/list"</span>, <span class="hljs-title function_">async</span> () => { <span class="hljs-title function_">log</span>(<span class="hljs-string">"debug"</span>, <span class="hljs-string">"tools/list called"</span>, { <span class="hljs-attr">toolCount</span>: <span class="hljs-variable constant_">TOOLS</span>.<span class="hljs-property">length</span> }); <span class="hljs-keyword">return</span> { <span class="hljs-attr">tools</span>: <span class="hljs-variable constant_">TOOLS</span> }; });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.<span class="hljs-title function_">on</span>(<span class="hljs-string">"uncaughtException"</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =></span> { <span class="hljs-title function_">log</span>(<span class="hljs-string">"fatal"</span>, <span class="hljs-string">"Uncaught exception"</span>, { <span class="hljs-attr">error</span>: err.<span class="hljs-property">message</span>, <span class="hljs-attr">stack</span>: err.<span class="hljs-property">stack</span> }); process.<span class="hljs-title function_">exit</span>(<span class="hljs-number">1</span>); }); process.<span class="hljs-title function_">on</span>(<span class="hljs-string">"unhandledRejection"</span>, <span class="hljs-function">(<span class="hljs-params">reason</span>) =></span> { <span class="hljs-title function_">log</span>(<span class="hljs-string">"fatal"</span>, <span class="hljs-string">"Unhandled rejection"</span>, { <span class="hljs-attr">reason</span>: <span class="hljs-title class_">String</span>(reason) }); process.<span class="hljs-title function_">exit</span>(<span class="hljs-number">1</span>); });3. Empty Response
Symptom: Tool runs but agent sees no output.
Diagnosis: Check the response format.
<span class="hljs-comment">// ❌ Wrong — missing content wrapper</span> <span class="hljs-keyword">return</span> { <span class="hljs-attr">text</span>: <span class="hljs-string">"Hello"</span> }; <span class="hljs-comment">// ✅ Correct</span> <span class="hljs-keyword">return</span> { <span class="hljs-attr">content</span>: [{ <span class="hljs-attr">type</span>: <span class="hljs-string">"text"</span>, <span class="hljs-attr">text</span>: <span class="hljs-string">"Hello"</span> }] };4. Schema Mismatch
Symptom: Agent passes wrong argument types.
Diagnosis: Validate your
inputSchema.<span class="hljs-comment">// Add validation in tools/call</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">validateArgs</span>(<span class="hljs-params">schema, args</span>) { <span class="hljs-keyword">if</span> (schema.<span class="hljs-property">required</span>) { <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> field <span class="hljs-keyword">of</span> schema.<span class="hljs-property">required</span>) { <span class="hljs-keyword">if</span> (!(field <span class="hljs-keyword">in</span> args)) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">`Missing required field: <span class="hljs-subst">${field}</span>`</span>); } } } }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:
<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">DEBUG</span> = process.<span class="hljs-property">env</span>.<span class="hljs-property">MCP_DEBUG</span> === <span class="hljs-string">"true"</span>; <span class="hljs-keyword">function</span> <span class="hljs-title function_">debug</span>(<span class="hljs-params">...args</span>) { <span class="hljs-keyword">if</span> (<span class="hljs-variable constant_">DEBUG</span>) <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">"[DEBUG]"</span>, ...args); } <span class="hljs-comment">// In tools/call:</span> <span class="hljs-title function_">debug</span>(<span class="hljs-string">"Handling tool:"</span>, name, <span class="hljs-string">"with args:"</span>, 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