Building MCP Servers

Error Handling in MCP

4 min read

Robust error handling is crucial for production MCP servers. Let's explore how to handle errors gracefully.

MCP Error Types

MCP defines standard error codes following JSON-RPC conventions:

Code Name Description
-32700 Parse Error Invalid JSON received
-32600 Invalid Request Malformed request
-32601 Method Not Found Unknown method called
-32602 Invalid Params Wrong parameters
-32603 Internal Error Server-side error

Raising Errors in Tools

When a tool encounters an error, raise it with a descriptive message:

from mcp.types import McpError

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "get_user":
        user_id = arguments.get("user_id")

        if not user_id:
            raise McpError(
                code=-32602,
                message="user_id is required"
            )

        user = await db.get_user(user_id)
        if not user:
            raise McpError(
                code=-32603,
                message=f"User {user_id} not found"
            )

        return [TextContent(type="text", text=user.to_json())]

Error Categories

Handle errors at the appropriate level:

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    try:
        # Validation errors
        validate_arguments(name, arguments)

        # Business logic errors
        result = await execute_tool(name, arguments)

        return result

    except ValidationError as e:
        # Client's fault - bad parameters
        raise McpError(code=-32602, message=str(e))

    except NotFoundError as e:
        # Resource not found
        raise McpError(code=-32603, message=str(e))

    except ExternalAPIError as e:
        # External service failure
        raise McpError(
            code=-32603,
            message=f"External service unavailable: {e}"
        )

    except Exception as e:
        # Unexpected error - log it
        logger.exception("Unexpected error in tool call")
        raise McpError(
            code=-32603,
            message="An unexpected error occurred"
        )

User-Friendly Error Messages

The AI reads your error messages. Make them helpful:

# Bad - cryptic
raise McpError(code=-32603, message="ERR_DB_CONN_FAIL")

# Good - actionable
raise McpError(
    code=-32603,
    message="Database connection failed. Please check if the database service is running."
)

Logging for Debugging

Always log errors with context:

import logging

logger = logging.getLogger("mcp-server")

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    logger.info(f"Tool called: {name}", extra={"arguments": arguments})

    try:
        result = await execute_tool(name, arguments)
        logger.info(f"Tool succeeded: {name}")
        return result

    except Exception as e:
        logger.error(
            f"Tool failed: {name}",
            extra={"arguments": arguments, "error": str(e)},
            exc_info=True
        )
        raise

Next, we'll build a complete server lab exercise. :::

Quiz

Module 2 Quiz: Building MCP Servers

Take Quiz