How to Debug MCP Server Issues
A practical troubleshooting guide for MCP server problems. Learn to diagnose connection failures, tool errors, and performance issues with step-by-step solutions.
MCP servers are powerful, but when they don't work, debugging can be frustrating. This guide covers the most common issues and how to fix them systematically.
If you haven't built an MCP server yet, start with our beginner tutorial first.
The Debugging Mindset
MCP debugging follows a pattern:
1. Identify the layer: Is it connection, communication, or application logic?
2. Isolate the problem: Test components independently
3. Check the logs: Both client and server side
4. Verify the basics: Configuration, environment, dependencies
Essential Debugging Tools
MCP Inspector
The MCP Inspector is your first tool for debugging:
npx @modelcontextprotocol/inspector node your-server.js
This opens a web interface where you can:
Claude Desktop Logs
On macOS, Claude Desktop logs MCP activity:
View MCP-specific logs
tail -f ~/Library/Logs/Claude/mcp*.logView all Claude logs
tail -f ~/Library/Logs/Claude/*.log
On Windows:
Get-Content "$env:APPDATA\Claude\Logs\mcp*.log" -Wait
Server-Side Logging
Add comprehensive logging to your server:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";const server = new McpServer({
name: "debug-server",
version: "1.0.0",
});
// Log all incoming requests
server.use(async (request, next) => {
console.error([${new Date().toISOString()}] ${request.method},
JSON.stringify(request.params, null, 2));
try {
const response = await next(request);
console.error([${new Date().toISOString()}] Response:,
JSON.stringify(response, null, 2));
return response;
} catch (error) {
console.error([${new Date().toISOString()}] Error:, error);
throw error;
}
});
Important: Use console.error, not console.log. STDIO transport uses stdout for protocol messages, so logging to stdout corrupts communication.
Common Issues and Solutions
Issue 1: Server Won't Start
Symptoms:
Debug Steps:
1. Run the server manually:
node your-server.js
Check for syntax errors or missing dependencies.
2. Verify the command in config:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["/absolute/path/to/server.js"]
}
}
}
Use absolute paths to avoid path resolution issues.
3. Check Node.js version:
node --version # Should be 18+
4. Verify dependencies:
cd /path/to/server
npm install
Issue 2: Tools Not Appearing
Symptoms:
Debug Steps:
1. Check tool registration:
// Make sure tools are registered BEFORE connecting
server.tool("my_tool", "Description", schema, handler);
// Then connect
const transport = new StdioServerTransport();
await server.connect(transport);
2. Verify the server is running:
ps aux | grep your-server
3. Test with MCP Inspector:
npx @modelcontextprotocol/inspector node your-server.js
4. Check for registration errors:
try {
server.tool("my_tool", "Description", schema, handler);
console.error("Tool registered successfully");
} catch (error) {
console.error("Tool registration failed:", error);
}
Issue 3: Tool Calls Fail
Symptoms:
Debug Steps:
1. Add try-catch in handlers:
server.tool(
"risky_tool",
"Does something risky",
{ input: z.string() },
async ({ input }) => {
try {
const result = await riskyOperation(input);
return {
content: [{ type: "text", text: JSON.stringify(result) }],
};
} catch (error) {
console.error("Tool error:", error);
return {
content: [{ type: "text", text: Error: ${error.message} }],
isError: true,
};
}
}
);
2. Validate input handling:
server.tool(
"process_data",
"Process input data",
{ data: z.string() },
async ({ data }) => {
console.error("Received data:", data, typeof data);
// ... rest of handler
}
);
3. Test with known-good inputs:
Use MCP Inspector to send simple, valid inputs first.
Issue 4: Connection Drops
Symptoms:
Debug Steps:
1. Check for unhandled promise rejections:
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection:", reason);
});
2. Monitor memory usage:
setInterval(() => {
const usage = process.memoryUsage();
console.error(Memory: ${Math.round(usage.heapUsed / 1024 / 1024)}MB);
}, 30000);
3. Add connection lifecycle logging:
transport.onclose = () => {
console.error("Transport closed");
};
transport.onerror = (error) => {
console.error("Transport error:", error);
};
Issue 5: Environment Variables Not Working
Symptoms:
undefined values for environment variablesDebug Steps:
1. Verify config syntax:
{
"mcpServers": {
"my-server": {
"command": "node",
"args": ["server.js"],
"env": {
"API_KEY": "your-key-here"
}
}
}
}
2. Log environment in server:
console.error("Environment:", {
API_KEY: process.env.API_KEY ? "[SET]" : "[NOT SET]",
NODE_ENV: process.env.NODE_ENV,
});
3. Check for typos: Environment variable names are case-sensitive.
Issue 6: JSON Parse Errors
Symptoms:
Debug Steps:
1. Don't write to stdout:
// WRONG - corrupts STDIO protocol
console.log("Debug info");
// CORRECT - use stderr
console.error("Debug info");
2. Check for stray output:
Any library writing to stdout will break things. Audit your dependencies.
3. Validate JSON responses:
const response = {
content: [{ type: "text", text: result }],
};
// Verify it's valid JSON
console.error("Response:", JSON.stringify(response));
Advanced Debugging Techniques
Protocol-Level Debugging
Capture raw protocol messages:
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";class DebugTransport extends StdioServerTransport {
async send(message: any) {
console.error("[SEND]", JSON.stringify(message));
return super.send(message);
}
}
const transport = new DebugTransport();
Testing with Mock Clients
Create a test client to isolate issues:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";const transport = new StdioClientTransport({
command: "node",
args: ["your-server.js"],
});
const client = new Client({
name: "test-client",
version: "1.0.0",
});
await client.connect(transport);
// Test specific tools
const result = await client.callTool({
name: "your_tool",
arguments: { input: "test" },
});
console.log("Result:", result);
Debugging Remote Servers
For HTTP/SSE servers, use curl:
Test server health
curl http://localhost:8080/healthTest SSE connection
curl -N http://localhost:8080/sseSend a request
curl -X POST http://localhost:8080/message \
-H "Content-Type: application/json" \
-d '{"method": "tools/list"}'
For more on remote servers, see Local vs Remote MCP Servers.
Debugging Checklist
When stuck, run through this checklist:
npm install)console.error instead of console.logGetting Help
If you're still stuck:
1. Check the GitHub Issues: Search the MCP SDK repository for similar problems
2. Review Working Examples: Compare your code to the official server examples
3. Simplify: Strip your server down to the minimum, then add back features one at a time
Conclusion
Debugging MCP servers becomes easier with the right tools and systematic approach. Start with the MCP Inspector, add comprehensive logging, and work through issues layer by layer.
Most problems fall into a few categories: configuration errors, missing error handling, or stdout pollution. Check for these first, and you'll solve most issues quickly.
For more advanced topics, explore our MCP Architecture Deep Dive and Security Best Practices.