← Back to Articles
DebuggingTroubleshootingMCP ServerTutorial

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.

By Web MCP GuideFebruary 14, 20266 min read


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:

  • See available tools, resources, and prompts

  • Test tool calls with custom inputs

  • View raw request/response payloads

  • Check error messages
  • Claude Desktop Logs

    On macOS, Claude Desktop logs MCP activity:

    View MCP-specific logs


    tail -f ~/Library/Logs/Claude/mcp*.log

    View 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:

  • Claude shows no tools from your server

  • Server process exits immediately
  • 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:

  • Server starts but tools don't show in Claude

  • MCP Inspector shows empty tool list
  • 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:

  • Tools appear but return errors when called

  • "Tool execution failed" messages
  • 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:

  • Server works initially, then stops responding

  • Intermittent failures
  • 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:

  • API keys or config not available

  • undefined values for environment variables
  • Debug 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:

  • "Unexpected token" errors

  • Communication failures
  • 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/health

    Test SSE connection


    curl -N http://localhost:8080/sse

    Send 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:

  • [ ] Server runs without errors when started manually

  • [ ] Configuration uses absolute paths

  • [ ] JSON config is valid (no trailing commas)

  • [ ] Node.js 18+ is installed

  • [ ] Dependencies are installed (npm install)

  • [ ] Using console.error instead of console.log

  • [ ] Environment variables are spelled correctly

  • [ ] Claude Desktop was restarted after config changes

  • [ ] MCP Inspector shows expected tools

  • [ ] Tool handlers have proper error handling
  • Getting 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.