Error Handling

October 20, 2025 ยท View on GitHub

The MCP Adapter uses a two-part error handling system that separates error logging from error response creation.

System Overview

The error handling system has two main components:

  • Error Logging: McpErrorHandlerInterface implementations log errors for monitoring
  • Error Response Creation: McpErrorFactory creates standardized JSON-RPC error responses
use WP\MCP\Infrastructure\ErrorHandling\Contracts\McpErrorHandlerInterface;
use WP\MCP\Infrastructure\ErrorHandling\McpErrorFactory;

// Error logging
interface McpErrorHandlerInterface {
    public function log(string $message, array $context = [], string $type = 'error'): void;
}

// Error response creation
class McpErrorFactory {
    public static function tool_not_found(int $id, string $tool): array;
    public static function missing_parameter(int $id, string $parameter): array;
    // ... other error types
}

Error Handler Interface

Error handlers implement McpErrorHandlerInterface:

interface McpErrorHandlerInterface {
    public function log(string $message, array $context = [], string $type = 'error'): void;
}

The log() method receives:

  • $message: Error description
  • $context: Additional data (tool name, user ID, etc.)
  • $type: Log level ('error', 'info', 'debug')

Error Factory

McpErrorFactory creates standardized JSON-RPC error responses:

Common Error Methods

// Standard JSON-RPC errors
McpErrorFactory::parse_error(int $id, string $details = ''): array
McpErrorFactory::invalid_request(int $id, string $details = ''): array
McpErrorFactory::method_not_found(int $id, string $method): array
McpErrorFactory::invalid_params(int $id, string $details = ''): array
McpErrorFactory::internal_error(int $id, string $details = ''): array

// MCP-specific errors
McpErrorFactory::missing_parameter(int $id, string $parameter): array
McpErrorFactory::tool_not_found(int $id, string $tool): array
McpErrorFactory::ability_not_found(int $id, string $ability): array
McpErrorFactory::resource_not_found(int $id, string $resource_uri): array
McpErrorFactory::prompt_not_found(int $id, string $prompt): array
McpErrorFactory::permission_denied(int $id, string $details = ''): array
McpErrorFactory::unauthorized(int $id, string $details = ''): array
McpErrorFactory::mcp_disabled(int $id): array
McpErrorFactory::validation_error(int $id, string $details): array

Error Response Format

All methods return JSON-RPC 2.0 error responses:

$error = McpErrorFactory::tool_not_found(123, 'missing-tool');
// Returns:
[
    'jsonrpc' => '2.0',
    'id' => 123,
    'error' => [
        'code' => -32003,
        'message' => 'Tool not found: missing-tool'
    ]
]

Error Codes

Standard JSON-RPC and MCP-specific error codes:

// Standard JSON-RPC codes
const PARSE_ERROR      = -32700;
const INVALID_REQUEST  = -32600;
const METHOD_NOT_FOUND = -32601;
const INVALID_PARAMS   = -32602;
const INTERNAL_ERROR   = -32603;

// MCP-specific codes (-32000 to -32099)
const SERVER_ERROR       = -32000; // Generic server error (includes MCP disabled)
const TIMEOUT_ERROR      = -32001; // Request timeout
const RESOURCE_NOT_FOUND = -32002; // Resource not found
const TOOL_NOT_FOUND     = -32003; // Tool not found
const PROMPT_NOT_FOUND   = -32004; // Prompt not found
const PERMISSION_DENIED  = -32008; // Access denied
const UNAUTHORIZED       = -32010; // Authentication required

HTTP Status Mapping

The factory includes methods to map JSON-RPC error codes to HTTP status codes:

// Get HTTP status for error response
$http_status = McpErrorFactory::get_http_status_for_error($error_response);

// Direct mapping
$http_status = McpErrorFactory::mcp_error_to_http_status(-32003); // Returns 404

Built-in Error Handlers

ErrorLogMcpErrorHandler

Logs errors to PHP error log with structured context and user information:

$handler = new ErrorLogMcpErrorHandler();
$handler->log('Tool execution failed', ['tool_name' => 'my-tool'], 'error');
// Logs: [ERROR] Tool execution failed | Context: {"tool_name":"my-tool"} | User ID: 123

NullMcpErrorHandler

No-op handler that ignores all errors (useful for testing or when logging is disabled):

$handler = new NullMcpErrorHandler();
$handler->log('This will not be logged', [], 'error'); // Does nothing

Creating Custom Error Handlers

Implement the McpErrorHandlerInterface to create custom error handlers:

File-based Handler

use WP\MCP\Infrastructure\ErrorHandling\Contracts\McpErrorHandlerInterface;

class FileErrorHandler implements McpErrorHandlerInterface {
    public function log(string $message, array $context = [], string $type = 'error'): void {
        $log_entry = sprintf(
            '[%s] %s | Context: %s',
            strtoupper($type),
            $message,
            wp_json_encode($context)
        );
        
        file_put_contents(
            WP_CONTENT_DIR . '/mcp-errors.log',
            $log_entry . "\n",
            FILE_APPEND | LOCK_EX
        );
    }
}

External Service Handler

class ExternalServiceErrorHandler implements McpErrorHandlerInterface {
    public function log(string $message, array $context = [], string $type = 'error'): void {
        wp_remote_post('https://your-monitoring-service.com/api/errors', [
            'body' => wp_json_encode([
                'message' => $message,
                'context' => $context,
                'level' => $type,
                'site' => get_site_url()
            ]),
            'headers' => ['Content-Type' => 'application/json'],
            'timeout' => 5
        ]);
        
        // Fallback to local logging
        error_log("[MCP {$type}] {$message}");
    }
}

Usage in Practice

Handler Helper Trait

Most handlers use the HandlerHelperTrait which provides convenience methods:

use WP\MCP\Handlers\HandlerHelperTrait;

class MyHandler {
    use HandlerHelperTrait;
    
    public function handle_request($request) {
        // Create error responses easily
        if (!$this->validate_params($request)) {
            return $this->missing_parameter_error('required_param', $request['id']);
        }
        
        // Handle other errors
        if (!$this->check_permissions()) {
            return $this->permission_denied_error('resource_access', $request['id']);
        }
    }
}

HTTP Transport Integration

The HTTP transport automatically maps error codes to HTTP status codes:

// In transport handlers
$error_response = McpErrorFactory::tool_not_found(123, 'missing-tool');
$http_status = McpErrorFactory::get_http_status_for_error($error_response); // Returns 404

return new WP_REST_Response($error_response, $http_status);

JSON-RPC Message Validation

The factory includes message validation for proper JSON-RPC structure:

$validation_result = McpErrorFactory::validate_jsonrpc_message($request);
if (is_array($validation_result)) {
    // Validation failed, $validation_result contains error response
    return new WP_REST_Response($validation_result, 400);
}
// Validation passed