code-wheel / mcp-error-codes
Standardized error codes and fluent error builders for MCP (Model Context Protocol) servers in PHP
Installs: 348
Dependents: 1
Suggesters: 1
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/code-wheel/mcp-error-codes
Requires
- php: >=8.1
Requires (Dev)
- mcp/sdk: ^0.2
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0 || ^11.0
Suggests
- mcp/sdk: Required for toCallToolResult() and toJsonRpcError() conversion methods
README
Standardized error codes and fluent error builders for MCP (Model Context Protocol) servers in PHP.
Zero dependencies - pure PHP 8.1+.
Installation
composer require code-wheel/mcp-error-codes
Quick Start
Fluent Error Builder
use CodeWheel\McpErrorCodes\McpError; // Simple errors with factory methods $error = McpError::notFound('user', 'user-123'); $error = McpError::accessDenied('delete', 'admin permission required'); $error = McpError::validation('email', 'Invalid format'); $error = McpError::rateLimited('tool_calls'); // Add context and suggestions $error = McpError::notFound('user', 'user-123') ->withSuggestion('Check if user ID is correct') ->withContext(['searched_in' => 'active_users']); // Convert to different formats $array = $error->toArray(); // ['success' => false, 'error' => '...', 'code' => '...'] $result = $error->toToolResult(); // ToolResult with success=false $rpcError = $error->toJsonRpcError(); // JSON-RPC 2.0 Error object // Rate limiting with retry hint $error = McpError::rateLimited('api_calls') ->retryAfter(60);
ErrorBag for Multiple Errors
use CodeWheel\McpErrorCodes\ErrorBag; use CodeWheel\McpErrorCodes\McpError; $errors = new ErrorBag(); // Collect validation errors if (empty($input['email'])) { $errors->addValidation('email', 'Email is required'); } if (!filter_var($input['email'] ?? '', FILTER_VALIDATE_EMAIL)) { $errors->addValidation('email', 'Invalid email format'); } if (strlen($input['name'] ?? '') < 2) { $errors->addValidation('name', 'Name must be at least 2 characters'); } // Check and return if ($errors->hasErrors()) { return $errors->toToolResult(); } // Filter errors by field $emailErrors = $errors->forField('email'); // Merge error bags $errors->merge($otherErrors); // Iterate over errors foreach ($errors as $error) { echo $error->getMessage(); }
Error Code Constants
use CodeWheel\McpErrorCodes\ErrorCode; // Use constants for consistent error responses $response = [ 'success' => false, 'code' => ErrorCode::NOT_FOUND, 'error' => 'User not found', ]; // Check error category $category = ErrorCode::getCategory(ErrorCode::NOT_FOUND); // 'resource' // Check if client should retry $shouldRetry = ErrorCode::isRecoverable(ErrorCode::RATE_LIMIT_EXCEEDED); // true // Map to HTTP status $httpStatus = ErrorCode::getHttpStatus(ErrorCode::NOT_FOUND); // 404 // Map to JSON-RPC 2.0 error codes $rpcCode = ErrorCode::getJsonRpcCode(ErrorCode::VALIDATION_ERROR); // -32602 $rpcCode = ErrorCode::getJsonRpcCode(ErrorCode::NOT_FOUND); // -32002 $rpcCode = ErrorCode::getJsonRpcCode(ErrorCode::INTERNAL_ERROR); // -32603
Available Factory Methods
| Method | Description | Error Code |
|---|---|---|
McpError::notFound($type, $id) |
Entity not found | NOT_FOUND |
McpError::accessDenied($action, $reason) |
Permission denied | ACCESS_DENIED |
McpError::validation($field, $message) |
Input validation error | VALIDATION_ERROR |
McpError::rateLimited($resource) |
Rate limit exceeded | RATE_LIMIT_EXCEEDED |
McpError::alreadyExists($type, $id) |
Duplicate entity | ALREADY_EXISTS |
McpError::insufficientScope($required) |
Missing scope | INSUFFICIENT_SCOPE |
McpError::internalError($message) |
Server error | INTERNAL_ERROR |
McpError::timeout($operation) |
Operation timed out | TIMEOUT |
McpError::invalidInput($field, $message) |
Invalid input | VALIDATION_ERROR |
McpError::missingRequired($field) |
Required field missing | MISSING_REQUIRED |
McpError::operationFailed($message) |
Operation failed | OPERATION_FAILED |
McpError::serviceUnavailable($service) |
External service down | SERVICE_UNAVAILABLE |
McpError::entityProtected($type, $id) |
Cannot modify protected | ENTITY_PROTECTED |
McpError::entityInUse($type, $id) |
Cannot delete in-use | ENTITY_IN_USE |
McpError::confirmationRequired($action) |
Needs confirmation | CONFIRMATION_REQUIRED |
JSON-RPC 2.0 Error Code Mapping
MCP uses JSON-RPC 2.0. This package maps semantic error codes to standard JSON-RPC codes:
| Error Code | JSON-RPC Code | Description |
|---|---|---|
| VALIDATION_ERROR | -32602 | Invalid params |
| INVALID_TOOL | -32601 | Method not found |
| NOT_FOUND | -32002 | Resource not found |
| ACCESS_DENIED | -32003 | Access denied |
| RATE_LIMIT_EXCEEDED | -32004 | Rate limited |
| INTERNAL_ERROR | -32603 | Internal error |
| TIMEOUT | -32001 | Timeout |
Error Categories
| Category | Codes | Description |
|---|---|---|
access |
INSUFFICIENT_SCOPE, ADMIN_REQUIRED, ACCESS_DENIED, RATE_LIMIT_EXCEEDED | Permission/auth errors |
resource |
NOT_FOUND, ALREADY_EXISTS, ENTITY_IN_USE, ENTITY_PROTECTED | Entity/resource state errors |
validation |
VALIDATION_ERROR, INVALID_NAME, INVALID_FILE_TYPE, PAYLOAD_TOO_LARGE, MISSING_REQUIRED | Input validation errors |
operation |
INTERNAL_ERROR, OPERATION_FAILED, TIMEOUT, CONFIRMATION_REQUIRED | Operation execution errors |
domain |
TEMPLATE_NOT_FOUND, CRON_FAILED, MIGRATION_FAILED, etc. | Domain-specific errors |
Recoverable Errors
These errors may resolve on retry:
RATE_LIMIT_EXCEEDED- Wait and retryTIMEOUT- Retry with backoffSERVICE_UNAVAILABLE- External service may recoverINTERNAL_ERROR- Transient server issue
License
MIT