ToolAIPilotTAP
Sub

Ad

Common MCP Errors and How to Fix Them: Complete Troubleshooting Guide With Code Examples
developerGuideยท 8 min readยท 3,089

Common MCP Errors and How to Fix Them: Complete Troubleshooting Guide With Code Examples

Model Context Protocol errors fail silently or with cryptic messages that make debugging difficult. This guide documents every common MCP error, the root cause for each, the exact fix with working TypeScript code, and the testing approach that catches these errors before they reach production.

๐Ÿ”ง Tools mentioned in this article
MCP SDK

MCP SDK

Official TypeScript SDK for building MCP servers and clients

github.com

Visit
Claude Desktop

Claude Desktop

Primary MCP client for testing MCP server implementations

claude.ai

Visit
Marcus Webb

Marcus Webb

June 19, 2026

#common mcp errors how to fix guide 2026 typescript#model context protocol errors debugging guide developer 2026#mcp errors fix complete guide code examples 2026#mcp server errors troubleshoot guide complete 2026#model context protocol debugging fix guide developer

Introduction

MCP (Model Context Protocol) errors fall into four categories: transport errors that prevent the connection from establishing, schema errors where the tool definitions are malformed, runtime errors where tools execute but produce wrong results, and security errors where the server rejects requests it should accept or accepts requests it should reject. Each category has distinct error messages and distinct fixes.

The Problem: Why MCP Errors Are Hard to Debug

MCP servers communicate over stdio or HTTP with JSON-RPC 2.0 messages. When something goes wrong the error often appears in the client (Claude Desktop) as a generic 'tool call failed' message rather than the underlying error. The actual error is in the MCP server logs which developers frequently do not have configured for easy access. This guide shows where to find the real error and what each specific error means.

Causes and Fixes: Every Common MCP Error

Error 1: Server Not Found / Connection Failed

json
// Error in Claude Desktop logs:
// "Failed to connect to MCP server: Connection refused"
// or
// "MCP server process exited with code 1"

// Root cause 1: Wrong path in Claude Desktop config
// Fix: Check claude_desktop_config.json

// WRONG โ€” relative path
{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["server.js"]
    }
  }
}

// CORRECT โ€” absolute path required
{
  "mcpServers": {
    "my-server": {
      "command": "node",
      "args": ["/Users/yourname/projects/mcp-server/dist/server.js"]
    }
  }
}

// Root cause 2: TypeScript source not compiled
// The config points to .ts file instead of compiled .js
// Fix: compile first, point to dist/ directory
// package.json:
{
  "scripts": {
    "build": "tsc",
    "start": "node dist/server.js"
  }
}

// Root cause 3: Missing shebang for executable scripts
// If using npx to run the server:
// Add to top of your server.ts:
// #!/usr/bin/env node

Error 2: Tool Schema Validation Failed

typescript
// Error: "Invalid tool definition: schema validation failed"
// or: Tool appears in list but calling it returns error

// WRONG โ€” missing required fields in schema
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server(
  { name: "my-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// WRONG schema โ€” properties not defined correctly
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [{
    name: "search_web",
    description: "Search the web",
    inputSchema: {
      type: "object",
      // MISSING: properties definition
      // MISSING: required array
    }
  }]
}));

// CORRECT schema โ€” complete and valid
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "search_web",
      description: "Search the web for current information",
      inputSchema: {
        type: "object",
        properties: {
          query: {
            type: "string",
            description: "The search query to execute"
          },
          num_results: {
            type: "number",
            description: "Number of results to return (1-10)",
            default: 5
          }
        },
        required: ["query"]  // List all required properties
      }
    }
  ]
}));

// CORRECT tool call handler with proper error handling
import {
  ListToolsRequestSchema,
  CallToolRequestSchema,
  ErrorCode,
  McpError
} from "@modelcontextprotocol/sdk/types.js";

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;
  
  if (name === "search_web") {
    // Validate args before using them
    if (!args || typeof args.query !== "string") {
      throw new McpError(
        ErrorCode.InvalidParams,
        "query parameter is required and must be a string"
      );
    }
    
    try {
      const results = await performSearch(args.query, args.num_results || 5);
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify(results, null, 2)
          }
        ]
      };
    } catch (error) {
      // Proper error propagation
      throw new McpError(
        ErrorCode.InternalError,
        `Search failed: ${error instanceof Error ? error.message : "Unknown error"}`
      );
    }
  }
  
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
});

Error 3: Tool Returns Empty or Truncated Results

typescript
// Error: Tool executes but returns nothing useful
// Root cause: content array format incorrect

// WRONG โ€” returning plain string
return {
  result: "Here are the search results..."
  // MCP expects 'content' array, not 'result'
};

// WRONG โ€” content items missing 'type' field
return {
  content: [
    { text: "Here are the results" }  // Missing type field
  ]
};

// CORRECT โ€” proper content array format
return {
  content: [
    {
      type: "text",  // Required: 'text' | 'image' | 'resource'
      text: "Here are the search results:\n\n1. Result one\n2. Result two"
    }
  ]
  // isError field: omit for success, set to true for errors
};

// For image content:
return {
  content: [
    {
      type: "image",
      data: base64EncodedImageData,  // Base64 encoded
      mimeType: "image/png"           // Required for images
    }
  ]
};

// For resource content (files, URLs):
return {
  content: [
    {
      type: "resource",
      resource: {
        uri:      "file:///path/to/file.txt",
        mimeType: "text/plain",
        text:     fileContents  // For text resources
      }
    }
  ]
};

Error 4: Environment Variables Not Available in MCP Server

json
// Error: API keys and environment variables undefined in MCP server
// Root cause: MCP servers launched by Claude Desktop do not inherit shell env

// WRONG โ€” relying on process.env from shell profile
// Your .env or shell config does not apply to MCP server process

// CORRECT โ€” pass env vars explicitly in Claude Desktop config
{
  "mcpServers": {
    "my-api-server": {
      "command": "node",
      "args": ["/path/to/dist/server.js"],
      "env": {
        "API_KEY":          "your-actual-api-key-here",
        "DATABASE_URL":     "postgresql://localhost:5432/mydb",
        "LOG_LEVEL":        "info"
      }
    }
  }
}

// For sensitive keys: use a secrets manager and fetch at startup
// server.ts โ€” fetch secrets before server starts
async function loadSecrets() {
  // Option 1: Read from a local secrets file not in source control
  const secrets = JSON.parse(
    await fs.readFile(path.join(process.env.HOME!, '.mcp-secrets.json'), 'utf-8')
  );
  process.env.API_KEY = secrets.apiKey;
}

async function main() {
  await loadSecrets();
  const transport = new StdioServerTransport();
  await server.connect(transport);
}
main();

Examples: Complete Working MCP Server

typescript
// Complete MCP server with all error handling patterns applied
// Copy this as a starting template to avoid common errors

import { Server }              from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from "@modelcontextprotocol/sdk/types.js";

const server = new Server(
  { name: "example-server", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

// Tool definitions
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_weather",
      description: "Get current weather for a city",
      inputSchema: {
        type: "object" as const,
        properties: {
          city: {
            type: "string",
            description: "City name to get weather for"
          },
          units: {
            type: "string",
            enum: ["celsius", "fahrenheit"],
            description: "Temperature units",
            default: "celsius"
          }
        },
        required: ["city"]
      }
    }
  ]
}));

// Tool execution with complete error handling
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  if (name === "get_weather") {
    // Input validation
    if (!args?.city || typeof args.city !== "string") {
      throw new McpError(ErrorCode.InvalidParams, "city is required");
    }
    
    try {
      const data = await fetchWeather(args.city, args.units as string || "celsius");
      return {
        content: [{ type: "text" as const, text: formatWeatherResponse(data) }]
      };
    } catch (err) {
      // Return error in isError format rather than throwing
      // This allows Claude to handle the error gracefully
      return {
        content: [{
          type: "text" as const,
          text: `Failed to fetch weather: ${err instanceof Error ? err.message : "Unknown error"}`
        }],
        isError: true
      };
    }
  }
  
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
});

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  // MCP servers should NOT console.log โ€” it corrupts the stdio protocol
  // Use console.error for debug output (goes to stderr, not stdout)
  console.error("Server started");
}

main().catch(console.error);

Common Mistakes

  • Mistake 1: Using console.log in an MCP server โ€” stdout is the communication channel with the MCP client. Any console.log corrupts the JSON-RPC stream and breaks the connection. Use console.error for all debug output.
  • Mistake 2: Not handling async errors โ€” unhandled promise rejections crash the server process and appear as connection errors in the client.
  • Mistake 3: Returning responses outside the content array format โ€” any property not in the MCP specification is ignored by the client.
  • Mistake 4: Not restarting Claude Desktop after config changes โ€” Claude Desktop loads MCP config at startup. Config changes require a full restart.
  • Mistake 5: Using process.exit() on errors โ€” the server should handle errors gracefully and keep running. process.exit causes Claude Desktop to show a disconnected state.

Best Practices

  • Test MCP servers with the MCP inspector tool before connecting to Claude Desktop: npx @modelcontextprotocol/inspector node dist/server.js
  • Use Zod for input validation in tool handlers โ€” catches malformed arguments before they reach business logic and produces clear error messages.
  • Implement health monitoring with a ping tool that returns server status โ€” helps diagnose connectivity issues in production MCP deployments.
  • Version the MCP server and log the version on startup to stderr โ€” simplifies debugging when multiple versions are deployed.

FAQ

  • Q: Where are MCP error logs in Claude Desktop? A: macOS: ~/Library/Logs/Claude/mcp*.log. Windows: %APPDATA%/Claude/logs/. Check these files first when debugging.
  • Q: Can an MCP server have multiple transports? A: No. Choose stdio for local servers or HTTP/SSE for remote servers. A single server instance uses one transport.
  • Q: How do I test MCP tools without Claude Desktop? A: Use the MCP Inspector: npx @modelcontextprotocol/inspector [server command]. It provides a web UI for testing tool calls.
  • Q: Why does my MCP server work locally but fail when others run it? A: Usually an absolute path issue in the config or a missing environment variable. Use environment-independent paths and explicit env config.

Conclusion

MCP errors are diagnosable once the log location is known and the error categories are understood. Connection errors trace to config or path issues. Schema errors trace to malformed tool definitions. Runtime errors trace to content format issues or unhandled exceptions. The most important practices are using console.error instead of console.log, validating all inputs before processing, and testing with the MCP Inspector before connecting to Claude Desktop.

Ad

Common MCP Errors and How to Fix Them: Complete Troubleshooting Guide With Code Examples | ToolAIPilot