Even the best-designed MCP servers can encounter issues. Whether you're building a new server or integrating an existing one, knowing how to debug effectively will save you hours of frustration. This guide covers everything from basic logging to advanced debugging techniques.
1. The MCP Inspector
The MCP Inspector is your best friend when debugging. It's an official tool that lets you interact with your server directly, bypassing the AI client entirely.
npx @modelcontextprotocol/inspector your-server-commandWhat You Can Do with the Inspector
- View capabilities: See all tools, resources, and prompts your server exposes
- Test tools: Manually invoke tools with custom arguments
- Read resources: Fetch and view resource contents
- Monitor messages: See raw JSON-RPC messages being exchanged
- Inspect errors: View detailed error responses and stack traces
Pro Tip: Development Workflow
Run your server in watch mode alongside the Inspector. Use tsx watch src/index.ts or nodemon so changes automatically reload. This lets you iterate quickly without restarting the Inspector.
Inspector Examples
# TypeScript server with tsx
npx @modelcontextprotocol/inspector tsx src/index.ts
# Compiled JavaScript
npx @modelcontextprotocol/inspector node dist/index.js
# Python server
npx @modelcontextprotocol/inspector python server.py
# With environment variables
GITHUB_TOKEN=xxx npx @modelcontextprotocol/inspector npx @modelcontextprotocol/server-github2. Logging Best Practices
Since MCP servers communicate over stdio, you can't use console.log in Node.js servers—it would corrupt the JSON-RPC stream. Here's how to log properly:
Use stderr for Debug Output
// ❌ Don't do this - corrupts stdio
console.log("Debug message");
// ✅ Do this instead - writes to stderr
console.error("[DEBUG] Tool called:", toolName);
process.stderr.write(`[INFO] Processing request\n`);Implement a Logger
// src/logger.ts
type LogLevel = "debug" | "info" | "warn" | "error";
const LOG_LEVELS: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3,
};
const currentLevel = (process.env.LOG_LEVEL as LogLevel) || "info";
export function log(level: LogLevel, message: string, data?: unknown) {
if (LOG_LEVELS[level] >= LOG_LEVELS[currentLevel]) {
const timestamp = new Date().toISOString();
const logLine = data
? `[${timestamp}] [${level.toUpperCase()}] ${message} ${JSON.stringify(data)}`
: `[${timestamp}] [${level.toUpperCase()}] ${message}`;
process.stderr.write(logLine + "\n");
}
}
// Usage
log("debug", "Tool invoked", { name: "get_weather", args });
log("error", "Failed to fetch data", { error: err.message });File-Based Logging
For persistent logs, write to a file:
import { appendFileSync } from "fs";
function logToFile(message: string) {
const logPath = process.env.MCP_LOG_FILE || "/tmp/mcp-server.log";
appendFileSync(logPath, `${new Date().toISOString()} ${message}\n`);
}3. Common Issues and Solutions
Server Won't Start
If your server fails to launch, check these common causes:
- Missing dependencies: Run
npm installorpip install -r requirements.txt - Wrong Node/Python version: Check version requirements in package.json or pyproject.toml
- Syntax errors: Run
npx tsc --noEmitto check for TypeScript errors - Missing environment variables: Verify all required env vars are set
Tools Not Appearing
If your tools don't show up in the client:
- Ensure
capabilities.toolsis set in server initialization - Verify
ListToolsRequestSchemahandler is implemented - Check that tool definitions include
name,description, andinputSchema - Look for errors in the server startup logs
// Make sure capabilities include tools
const server = new Server(
{ name: "my-server", version: "1.0.0" },
{
capabilities: {
tools: {}, // ← This must be present
},
}
);4. Connection Errors
Common Mistake: Relative Paths
Many connection issues stem from incorrect paths in your config file. Always use absolute paths for the command field, or ensure the binary is in your system's PATH. Don't use ~ or relative paths.
Path Issues
// ❌ Won't work - relative path
{
"command": "./dist/index.js"
}
// ❌ Won't work - tilde expansion
{
"command": "~/projects/server/dist/index.js"
}
// ✅ Works - absolute path
{
"command": "/Users/me/projects/server/dist/index.js"
}
// ✅ Works - command in PATH
{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem"]
}Permission Issues
On Unix systems, ensure your server script is executable:
chmod +x dist/index.js5. Tool Execution Failures
When a tool fails during execution:
Add Detailed Logging
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
console.error(`[TOOL] Executing: ${name}`);
console.error(`[TOOL] Arguments: ${JSON.stringify(args)}`);
try {
const result = await executeToolLogic(name, args);
console.error(`[TOOL] Success: ${name}`);
return result;
} catch (error) {
console.error(`[TOOL] Error in ${name}:`, error);
throw error;
}
});Return Descriptive Errors
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
// Instead of generic errors
throw new Error("Failed");
// Return descriptive MCP errors
throw new McpError(
ErrorCode.InvalidParams,
`City "${city}" not found. Available cities: ${cities.join(", ")}`
);6. Reading Claude Desktop Logs
Claude Desktop logs MCP activity to help with debugging:
Log Locations
# macOS
~/Library/Logs/Claude/mcp.log
~/Library/Logs/Claude/mcp-server-*.log
# Windows
%APPDATA%\Claude\Logs\mcp.log
# Linux
~/.config/Claude/logs/mcp.logWatching Logs in Real-Time
# macOS/Linux
tail -f ~/Library/Logs/Claude/mcp*.log
# Filter for specific server
tail -f ~/Library/Logs/Claude/mcp*.log | grep "my-server"
# Windows PowerShell
Get-Content -Path "$env:APPDATA\Claude\Logs\mcp.log" -WaitWhat to Look For
- Startup errors: Server failed to initialize
- Connection timeouts: Server took too long to respond
- JSON parse errors: Invalid output from server
- Tool errors: Exceptions during tool execution
7. Validating Your Server
Before deploying, run through this checklist:
Pre-Deployment Checklist
- ✓ All tools have clear descriptions and valid JSON schemas
- ✓ Error cases return helpful, actionable messages
- ✓ Server handles graceful shutdown (SIGTERM, SIGINT)
- ✓ Sensitive data is not logged or exposed in errors
- ✓ All tools work correctly in the MCP Inspector
- ✓ Server starts within a reasonable time (<5 seconds)
- ✓ Memory usage is stable over time (no leaks)
Automated Validation Script
// scripts/validate.ts
import { Client } from "@modelcontextprotocol/sdk/client";
async function validate() {
const client = new Client({ name: "validator" });
// Test tool listing
const tools = await client.listTools();
console.log(`✓ Found ${tools.tools.length} tools`);
// Validate each tool has required fields
for (const tool of tools.tools) {
if (!tool.name || !tool.description || !tool.inputSchema) {
throw new Error(`Tool ${tool.name} missing required fields`);
}
console.log(`✓ Tool "${tool.name}" is valid`);
}
console.log("\n✅ All validations passed!");
}8. Advanced Debugging Techniques
Message Tracing
Log all JSON-RPC messages for detailed debugging:
// Wrap the transport to log messages
class LoggingTransport {
constructor(private inner: Transport) {}
async send(message: JSONRPCMessage) {
console.error("[OUT]", JSON.stringify(message));
return this.inner.send(message);
}
async receive(): Promise<JSONRPCMessage> {
const message = await this.inner.receive();
console.error("[IN]", JSON.stringify(message));
return message;
}
}Memory Profiling
// Add memory logging
setInterval(() => {
const usage = process.memoryUsage();
console.error(`[MEM] Heap: ${Math.round(usage.heapUsed / 1024 / 1024)}MB`);
}, 30000);Performance Timing
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const start = performance.now();
try {
return await handleTool(request);
} finally {
const duration = performance.now() - start;
console.error(`[PERF] ${request.params.name}: ${duration.toFixed(2)}ms`);
}
});Conclusion
Debugging MCP servers requires a systematic approach. Start with the Inspector to isolate issues, add comprehensive logging, and use Claude Desktop logs for production debugging. With these techniques, you'll be able to quickly identify and fix problems, allowing you to focus on building amazing AI-powered integrations.
Outdated Content Warning
This guide was last updated on January 12, 2025 (12 months ago).
The information presented here may be significantly outdated. Technologies, APIs, and best practices may have changed since this content was written.
We strive to keep our content current, but with rapidly evolving technologies, some details may no longer be accurate.
Last updated
January 12, 2025
374 days ago
This content may be outdated
This content may contain outdated information. Please verify details before use.