When debugging MCP servers, we've found that most issues fall into four main categories. Recognizing these patterns helps identify and fix problems faster.
Configuration And Connection Issues
The most basic problems happen when the server can't start or the host can't connect to it. These typically show up as:
Connection refused
errorsServer disconnected
messagesCommand not found
errorsSilent failures where tools don't appear
These issues usually stem from:
Incorrect paths: The server executable or script can't be found
Port conflicts: Another process is using the same port
Missing environment variables: Required configuration is absent
Transport mismatch: The host and server use different communication methods
To debug these issues, check your server logs first. For Claude Desktop on macOS, you can view logs with:
tail -f
JSON-RPC Format Problems
MCP relies on properly formatted JSON-RPC 2.0 messages. Common errors include:
Malformed JSON (missing commas, extra quotes)
Missing required fields like
jsonrpc
,id
, ormethod
Writing non-protocol data to stdout
A critical rule: MCP servers must only write JSON-RPC messages to stdout. All logs and debugging output should go to stderr instead.
// Correct logging in Node.js console.error("Debug info"); // Goes to stderr, won't break protocol // Wrong - breaks the protocol console.log("Debug info"); // Goes to stdout, corrupts messages
Tool Execution Failures
When a tool runs but encounters problems, it might:
Throw an unhandled exception
Return invalid data
Time out while waiting for external resources
Fail to access required services
These errors are often the trickiest because they depend on your specific implementation and external dependencies.
The best approach is defensive coding with try/catch blocks around all tool handlers:
server.tool("fetchData", { url: z.string() }, async ({ url }) => { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return { content: await response.text() }; } catch (error) { console.error("Error fetching data:", error); throw new McpError( McpErrorCode.ServerError, `Failed to fetch data: ${error.message}` ); } });
Protocol Version Mismatches
MCP clients and servers negotiate a protocol version during initialization. If they can't agree on a compatible version, the connection fails.
Version mismatch errors appear as:
Protocol version not supported
Incompatible version
Connection closing immediately after initialization
To fix these issues, ensure your server supports the version expected by the host application. Check both the initialization request and response to confirm the versions match.
Logging And Observability
Effective logging is the foundation of MCP server debugging. Without visibility into what's happening, you're troubleshooting blind.
Setting Up Proper Logging
Remember these key principles for MCP logging:
Use stderr, not stdout: All logs must go to stderr to avoid corrupting the protocol
Include context: Log the tool name, operation, and relevant parameters
Capture errors completely: Include stack traces and error messages
Use structured formats: JSON logs are easier to parse and analyze
For Node.js servers:
const logger = { info: (msg, data) => console.error(`[INFO] ${msg}`, data || ''), error: (msg, err) => console.error(`[ERROR] ${msg}`, err?.stack || err || '') };
For Python servers:
import logging import sys logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', stream=sys.stderr )
Using MCP Inspector
The MCP Inspector is an invaluable debugging tool that lets you:
Test MCP servers directly without a host application
View raw JSON-RPC messages
Execute tools with custom inputs
Inspect server responses and errors
To use it:
This launches a web interface (default: http://localhost:5173) where you can interact with your server and see exactly what's happening.
Debugging Workflow
When facing an MCP server issue, follow this step-by-step approach to identify and fix the problem.
1. Reproduce The Error
First, create a reliable way to trigger the error. Document:
The exact input that causes the problem
The environment settings (host application, OS, etc.)
Any relevant configuration
Having a reproducible test case makes debugging much more efficient.
2. Check The Logs
Examine logs from both the server and the host application. Look for:
Error messages or exceptions
Unusual patterns or warnings
Missing or unexpected responses
For Claude Desktop, check the MCP logs as mentioned earlier. For VS Code, use the "Show Output" option in the error notification.
3. Isolate The Problem
Narrow down where the issue occurs:
Is it a connection problem?
Is it a specific tool that fails?
Does it happen with certain inputs only?
Is it related to external dependencies?
Try simplifying your server to a minimal example that still shows the problem. This helps eliminate unrelated factors.
4. Test With MCP Inspector
The MCP Inspector provides a controlled environment to test your server without the host application. This helps determine if the issue is in your server or in the host-server interaction.
5. Fix And Verify
After identifying the root cause, make the necessary changes. Test thoroughly to ensure:
The original error is resolved
No new issues were introduced
The fix works consistently
Handling Schema And Version Compatibility
One challenge with MCP servers is maintaining compatibility across different clients and protocol versions.
Schema Validation
Validate all inputs and outputs against the MCP schema to catch problems early:
// Using zod for schema validation in Node.js import { z } from "zod"; const inputSchema = z.object({ url: z.string().url(), timeout: z.number().optional() }); server.tool("fetchUrl", inputSchema, async (input) => { // Input is guaranteed to match the schema // ... });
Version Negotiation
MCP uses date-based versioning (e.g., "2025-06-01"). During initialization, the client proposes a version, and the server either accepts it or suggests an alternative.
Your server should support the versions used by your target hosts. The MCP SDKs typically handle this negotiation automatically.
Advanced Debugging Techniques
For more complex issues, these advanced techniques can help.
Automated Testing
Write tests that simulate MCP client-server interactions:
// Jest test example test("fetchUrl tool handles 404 errors", async () => { const result = await runTool("fetchUrl", { url: "https://example.com/not-found" }); expect(result.error).toBeDefined(); expect(result.error.code).toBe(-32000); });
Monitoring Production Servers
For MCP servers in production, monitor:
Error rates by tool
Response times
Resource usage (CPU, memory)
External service availability
Tools like Prometheus, Grafana, or cloud monitoring services can track these metrics.
Building reliable MCP servers
Building reliable MCP servers requires attention to error handling and debugging. By understanding the common error categories, setting up proper logging, and following a structured debugging workflow, you can create robust MCP servers that provide valuable tools to LLMs.
At Stainless, we've applied these lessons to our SDK generation platform, which now includes MCP server capabilities. Our approach ensures that the MCP servers we generate handle errors gracefully and provide clear debugging information.
FAQs About MCP Error Handling
How do I debug MCP servers running in Docker containers?
Access container logs using docker logs <container_id>
and ensure your server writes debug information to stderr. Mount a volume to preserve logs outside the container, and consider exposing a debug port for the MCP Inspector to connect to your containerized server.
What's the best way to handle timeouts in MCP tool execution?
Implement timeout handling in your tool code using Promise timeouts (JavaScript) or asyncio timeouts (Python). Log both the timeout event and the operation that timed out. Return a structured error response that explains the timeout to the client rather than letting the request hang indefinitely.
How can I test my MCP server with different protocol versions?
Use the MCP Inspector's custom initialization feature to test with specific protocol versions. Create a version compatibility matrix documenting which features work with which protocol versions, and maintain backward compatibility where possible by supporting multiple versions in your server implementation.
When debugging MCP servers, we've found that most issues fall into four main categories. Recognizing these patterns helps identify and fix problems faster.
Configuration And Connection Issues
The most basic problems happen when the server can't start or the host can't connect to it. These typically show up as:
Connection refused
errorsServer disconnected
messagesCommand not found
errorsSilent failures where tools don't appear
These issues usually stem from:
Incorrect paths: The server executable or script can't be found
Port conflicts: Another process is using the same port
Missing environment variables: Required configuration is absent
Transport mismatch: The host and server use different communication methods
To debug these issues, check your server logs first. For Claude Desktop on macOS, you can view logs with:
tail -f
JSON-RPC Format Problems
MCP relies on properly formatted JSON-RPC 2.0 messages. Common errors include:
Malformed JSON (missing commas, extra quotes)
Missing required fields like
jsonrpc
,id
, ormethod
Writing non-protocol data to stdout
A critical rule: MCP servers must only write JSON-RPC messages to stdout. All logs and debugging output should go to stderr instead.
// Correct logging in Node.js console.error("Debug info"); // Goes to stderr, won't break protocol // Wrong - breaks the protocol console.log("Debug info"); // Goes to stdout, corrupts messages
Tool Execution Failures
When a tool runs but encounters problems, it might:
Throw an unhandled exception
Return invalid data
Time out while waiting for external resources
Fail to access required services
These errors are often the trickiest because they depend on your specific implementation and external dependencies.
The best approach is defensive coding with try/catch blocks around all tool handlers:
server.tool("fetchData", { url: z.string() }, async ({ url }) => { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return { content: await response.text() }; } catch (error) { console.error("Error fetching data:", error); throw new McpError( McpErrorCode.ServerError, `Failed to fetch data: ${error.message}` ); } });
Protocol Version Mismatches
MCP clients and servers negotiate a protocol version during initialization. If they can't agree on a compatible version, the connection fails.
Version mismatch errors appear as:
Protocol version not supported
Incompatible version
Connection closing immediately after initialization
To fix these issues, ensure your server supports the version expected by the host application. Check both the initialization request and response to confirm the versions match.
Logging And Observability
Effective logging is the foundation of MCP server debugging. Without visibility into what's happening, you're troubleshooting blind.
Setting Up Proper Logging
Remember these key principles for MCP logging:
Use stderr, not stdout: All logs must go to stderr to avoid corrupting the protocol
Include context: Log the tool name, operation, and relevant parameters
Capture errors completely: Include stack traces and error messages
Use structured formats: JSON logs are easier to parse and analyze
For Node.js servers:
const logger = { info: (msg, data) => console.error(`[INFO] ${msg}`, data || ''), error: (msg, err) => console.error(`[ERROR] ${msg}`, err?.stack || err || '') };
For Python servers:
import logging import sys logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', stream=sys.stderr )
Using MCP Inspector
The MCP Inspector is an invaluable debugging tool that lets you:
Test MCP servers directly without a host application
View raw JSON-RPC messages
Execute tools with custom inputs
Inspect server responses and errors
To use it:
This launches a web interface (default: http://localhost:5173) where you can interact with your server and see exactly what's happening.
Debugging Workflow
When facing an MCP server issue, follow this step-by-step approach to identify and fix the problem.
1. Reproduce The Error
First, create a reliable way to trigger the error. Document:
The exact input that causes the problem
The environment settings (host application, OS, etc.)
Any relevant configuration
Having a reproducible test case makes debugging much more efficient.
2. Check The Logs
Examine logs from both the server and the host application. Look for:
Error messages or exceptions
Unusual patterns or warnings
Missing or unexpected responses
For Claude Desktop, check the MCP logs as mentioned earlier. For VS Code, use the "Show Output" option in the error notification.
3. Isolate The Problem
Narrow down where the issue occurs:
Is it a connection problem?
Is it a specific tool that fails?
Does it happen with certain inputs only?
Is it related to external dependencies?
Try simplifying your server to a minimal example that still shows the problem. This helps eliminate unrelated factors.
4. Test With MCP Inspector
The MCP Inspector provides a controlled environment to test your server without the host application. This helps determine if the issue is in your server or in the host-server interaction.
5. Fix And Verify
After identifying the root cause, make the necessary changes. Test thoroughly to ensure:
The original error is resolved
No new issues were introduced
The fix works consistently
Handling Schema And Version Compatibility
One challenge with MCP servers is maintaining compatibility across different clients and protocol versions.
Schema Validation
Validate all inputs and outputs against the MCP schema to catch problems early:
// Using zod for schema validation in Node.js import { z } from "zod"; const inputSchema = z.object({ url: z.string().url(), timeout: z.number().optional() }); server.tool("fetchUrl", inputSchema, async (input) => { // Input is guaranteed to match the schema // ... });
Version Negotiation
MCP uses date-based versioning (e.g., "2025-06-01"). During initialization, the client proposes a version, and the server either accepts it or suggests an alternative.
Your server should support the versions used by your target hosts. The MCP SDKs typically handle this negotiation automatically.
Advanced Debugging Techniques
For more complex issues, these advanced techniques can help.
Automated Testing
Write tests that simulate MCP client-server interactions:
// Jest test example test("fetchUrl tool handles 404 errors", async () => { const result = await runTool("fetchUrl", { url: "https://example.com/not-found" }); expect(result.error).toBeDefined(); expect(result.error.code).toBe(-32000); });
Monitoring Production Servers
For MCP servers in production, monitor:
Error rates by tool
Response times
Resource usage (CPU, memory)
External service availability
Tools like Prometheus, Grafana, or cloud monitoring services can track these metrics.
Building reliable MCP servers
Building reliable MCP servers requires attention to error handling and debugging. By understanding the common error categories, setting up proper logging, and following a structured debugging workflow, you can create robust MCP servers that provide valuable tools to LLMs.
At Stainless, we've applied these lessons to our SDK generation platform, which now includes MCP server capabilities. Our approach ensures that the MCP servers we generate handle errors gracefully and provide clear debugging information.
FAQs About MCP Error Handling
How do I debug MCP servers running in Docker containers?
Access container logs using docker logs <container_id>
and ensure your server writes debug information to stderr. Mount a volume to preserve logs outside the container, and consider exposing a debug port for the MCP Inspector to connect to your containerized server.
What's the best way to handle timeouts in MCP tool execution?
Implement timeout handling in your tool code using Promise timeouts (JavaScript) or asyncio timeouts (Python). Log both the timeout event and the operation that timed out. Return a structured error response that explains the timeout to the client rather than letting the request hang indefinitely.
How can I test my MCP server with different protocol versions?
Use the MCP Inspector's custom initialization feature to test with specific protocol versions. Create a version compatibility matrix documenting which features work with which protocol versions, and maintain backward compatibility where possible by supporting multiple versions in your server implementation.
When debugging MCP servers, we've found that most issues fall into four main categories. Recognizing these patterns helps identify and fix problems faster.
Configuration And Connection Issues
The most basic problems happen when the server can't start or the host can't connect to it. These typically show up as:
Connection refused
errorsServer disconnected
messagesCommand not found
errorsSilent failures where tools don't appear
These issues usually stem from:
Incorrect paths: The server executable or script can't be found
Port conflicts: Another process is using the same port
Missing environment variables: Required configuration is absent
Transport mismatch: The host and server use different communication methods
To debug these issues, check your server logs first. For Claude Desktop on macOS, you can view logs with:
tail -f
JSON-RPC Format Problems
MCP relies on properly formatted JSON-RPC 2.0 messages. Common errors include:
Malformed JSON (missing commas, extra quotes)
Missing required fields like
jsonrpc
,id
, ormethod
Writing non-protocol data to stdout
A critical rule: MCP servers must only write JSON-RPC messages to stdout. All logs and debugging output should go to stderr instead.
// Correct logging in Node.js console.error("Debug info"); // Goes to stderr, won't break protocol // Wrong - breaks the protocol console.log("Debug info"); // Goes to stdout, corrupts messages
Tool Execution Failures
When a tool runs but encounters problems, it might:
Throw an unhandled exception
Return invalid data
Time out while waiting for external resources
Fail to access required services
These errors are often the trickiest because they depend on your specific implementation and external dependencies.
The best approach is defensive coding with try/catch blocks around all tool handlers:
server.tool("fetchData", { url: z.string() }, async ({ url }) => { try { const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error: ${response.status}`); } return { content: await response.text() }; } catch (error) { console.error("Error fetching data:", error); throw new McpError( McpErrorCode.ServerError, `Failed to fetch data: ${error.message}` ); } });
Protocol Version Mismatches
MCP clients and servers negotiate a protocol version during initialization. If they can't agree on a compatible version, the connection fails.
Version mismatch errors appear as:
Protocol version not supported
Incompatible version
Connection closing immediately after initialization
To fix these issues, ensure your server supports the version expected by the host application. Check both the initialization request and response to confirm the versions match.
Logging And Observability
Effective logging is the foundation of MCP server debugging. Without visibility into what's happening, you're troubleshooting blind.
Setting Up Proper Logging
Remember these key principles for MCP logging:
Use stderr, not stdout: All logs must go to stderr to avoid corrupting the protocol
Include context: Log the tool name, operation, and relevant parameters
Capture errors completely: Include stack traces and error messages
Use structured formats: JSON logs are easier to parse and analyze
For Node.js servers:
const logger = { info: (msg, data) => console.error(`[INFO] ${msg}`, data || ''), error: (msg, err) => console.error(`[ERROR] ${msg}`, err?.stack || err || '') };
For Python servers:
import logging import sys logging.basicConfig( level=logging.DEBUG, format='%(asctime)s %(levelname)s %(message)s', stream=sys.stderr )
Using MCP Inspector
The MCP Inspector is an invaluable debugging tool that lets you:
Test MCP servers directly without a host application
View raw JSON-RPC messages
Execute tools with custom inputs
Inspect server responses and errors
To use it:
This launches a web interface (default: http://localhost:5173) where you can interact with your server and see exactly what's happening.
Debugging Workflow
When facing an MCP server issue, follow this step-by-step approach to identify and fix the problem.
1. Reproduce The Error
First, create a reliable way to trigger the error. Document:
The exact input that causes the problem
The environment settings (host application, OS, etc.)
Any relevant configuration
Having a reproducible test case makes debugging much more efficient.
2. Check The Logs
Examine logs from both the server and the host application. Look for:
Error messages or exceptions
Unusual patterns or warnings
Missing or unexpected responses
For Claude Desktop, check the MCP logs as mentioned earlier. For VS Code, use the "Show Output" option in the error notification.
3. Isolate The Problem
Narrow down where the issue occurs:
Is it a connection problem?
Is it a specific tool that fails?
Does it happen with certain inputs only?
Is it related to external dependencies?
Try simplifying your server to a minimal example that still shows the problem. This helps eliminate unrelated factors.
4. Test With MCP Inspector
The MCP Inspector provides a controlled environment to test your server without the host application. This helps determine if the issue is in your server or in the host-server interaction.
5. Fix And Verify
After identifying the root cause, make the necessary changes. Test thoroughly to ensure:
The original error is resolved
No new issues were introduced
The fix works consistently
Handling Schema And Version Compatibility
One challenge with MCP servers is maintaining compatibility across different clients and protocol versions.
Schema Validation
Validate all inputs and outputs against the MCP schema to catch problems early:
// Using zod for schema validation in Node.js import { z } from "zod"; const inputSchema = z.object({ url: z.string().url(), timeout: z.number().optional() }); server.tool("fetchUrl", inputSchema, async (input) => { // Input is guaranteed to match the schema // ... });
Version Negotiation
MCP uses date-based versioning (e.g., "2025-06-01"). During initialization, the client proposes a version, and the server either accepts it or suggests an alternative.
Your server should support the versions used by your target hosts. The MCP SDKs typically handle this negotiation automatically.
Advanced Debugging Techniques
For more complex issues, these advanced techniques can help.
Automated Testing
Write tests that simulate MCP client-server interactions:
// Jest test example test("fetchUrl tool handles 404 errors", async () => { const result = await runTool("fetchUrl", { url: "https://example.com/not-found" }); expect(result.error).toBeDefined(); expect(result.error.code).toBe(-32000); });
Monitoring Production Servers
For MCP servers in production, monitor:
Error rates by tool
Response times
Resource usage (CPU, memory)
External service availability
Tools like Prometheus, Grafana, or cloud monitoring services can track these metrics.
Building reliable MCP servers
Building reliable MCP servers requires attention to error handling and debugging. By understanding the common error categories, setting up proper logging, and following a structured debugging workflow, you can create robust MCP servers that provide valuable tools to LLMs.
At Stainless, we've applied these lessons to our SDK generation platform, which now includes MCP server capabilities. Our approach ensures that the MCP servers we generate handle errors gracefully and provide clear debugging information.
FAQs About MCP Error Handling
How do I debug MCP servers running in Docker containers?
Access container logs using docker logs <container_id>
and ensure your server writes debug information to stderr. Mount a volume to preserve logs outside the container, and consider exposing a debug port for the MCP Inspector to connect to your containerized server.
What's the best way to handle timeouts in MCP tool execution?
Implement timeout handling in your tool code using Promise timeouts (JavaScript) or asyncio timeouts (Python). Log both the timeout event and the operation that timed out. Return a structured error response that explains the timeout to the client rather than letting the request hang indefinitely.
How can I test my MCP server with different protocol versions?
Use the MCP Inspector's custom initialization feature to test with specific protocol versions. Create a version compatibility matrix documenting which features work with which protocol versions, and maintain backward compatibility where possible by supporting multiple versions in your server implementation.