Skip to main content

MCP Error Codes

export const MCP_ERROR_CODES = {
  UNAUTHORIZED: -32001,          // Missing credentials
  RESOURCE_NOT_FOUND: -32002,    // Resource not found
  FORBIDDEN: -32003,             // Invalid/insufficient credentials
  INVALID_REQUEST: -32600,       // Invalid request
  METHOD_NOT_FOUND: -32601,      // Method not found
  INVALID_PARAMS: -32602,        // Invalid parameters
  INTERNAL_ERROR: -32603,        // Internal server error
  PARSE_ERROR: -32700,           // Parse error
} as const;

Error Hierarchy

McpError (abstract)
├── PublicMcpError (safe to expose to clients)
│   ├── ToolNotFoundError
│   ├── ResourceNotFoundError
│   ├── InvalidInputError
│   ├── UnauthorizedError
│   ├── RateLimitError
│   └── ...

└── InternalMcpError (hide details from clients)
    ├── ToolExecutionError
    ├── ResourceReadError
    ├── InvalidOutputError
    └── ...

Base Error Classes

McpError (Abstract)

Base class for all MCP-related errors.
abstract class McpError extends Error {
  readonly errorId: string;           // Unique ID for tracking
  abstract isPublic: boolean;         // Expose details to clients?
  abstract statusCode: number;        // HTTP status code
  abstract code: string;              // Error code

  abstract getPublicMessage(): string;
  getInternalMessage(): string;
  toMcpError(isDevelopment?: boolean): McpErrorResponse;
}

PublicMcpError

Errors safe to expose to clients (validation errors, not found, etc.).
class PublicMcpError extends McpError {
  readonly isPublic = true;

  constructor(message: string, code?: string, statusCode?: number);
}

InternalMcpError

Errors that should NOT expose details (server errors, unexpected failures).
class InternalMcpError extends McpError {
  readonly isPublic = false;
  readonly statusCode = 500;

  getPublicMessage(): string {
    return `Internal FrontMCP error. Contact support with ID: ${this.errorId}`;
  }
}

Error Categories

Tool & Prompt Errors

ToolNotFoundError, ToolExecutionError, PromptNotFoundError, PromptExecutionError

Resource Errors

ResourceNotFoundError, ResourceReadError, InvalidResourceUriError

Validation Errors

InvalidInputError, InvalidOutputError, InvalidMethodError, MissingPromptArgumentError

Auth Errors

UnauthorizedError, AuthConfigurationError, SessionMissingError, AuthorizationRequiredError

Agent Errors

AgentNotFoundError, AgentExecutionError, AgentLoopExceededError, AgentTimeoutError

Rate Limit Errors

RateLimitError, QuotaExceededError

Transport Errors

TransportNotConnectedError, UnsupportedContentTypeError

Remote Errors

RemoteConnectionError, RemoteTimeoutError, RemoteToolNotFoundError

Elicitation Errors

ElicitationNotSupportedError, ElicitationTimeoutError, ElicitationFallbackRequired

Provider Errors

ProviderNotRegisteredError, DependencyCycleError, ProviderConstructionError

Registry Errors

RegistryDefinitionNotFoundError, FlowNotRegisteredError, EntryValidationError

Decorator Errors

InvalidDecoratorMetadataError, HookTargetNotDefinedError

Normalization Errors

MissingProvideError, InvalidUseClassError, InvalidEntityError

SDK Errors

FlowExitedWithoutOutputError, ServerNotFoundError, ConfigNotFoundError

Auth Internal Errors

EncryptionContextNotSetError, VaultLoadError, TokenLeakDetectedError

Usage Patterns

Throwing Errors

import { ToolNotFoundError, InvalidInputError } from '@frontmcp/sdk';

@Tool({ name: 'my_tool', inputSchema: { id: z.string() } })
class MyTool extends ToolContext {
  async execute(input: { id: string }) {
    if (!input.id) {
      throw new InvalidInputError('ID is required');
    }

    const item = await this.get(Service).find(input.id);
    if (!item) {
      throw new ToolNotFoundError('my_tool');
    }

    return item;
  }
}

Using fail()

@Tool({ name: 'my_tool', inputSchema: {} })
class MyTool extends ToolContext {
  async execute(input) {
    if (!valid) {
      this.fail(new InvalidInputError('Validation failed'));
      // Never reaches here - fail() throws
    }
  }
}

Error with Details

throw new InvalidInputError('Validation failed', {
  field: 'email',
  reason: 'Invalid format',
});

Custom toJsonRpcError

Some errors override toJsonRpcError() for custom data:
class ResourceNotFoundError extends PublicMcpError {
  toJsonRpcError() {
    return {
      code: MCP_ERROR_CODES.RESOURCE_NOT_FOUND,
      message: this.getPublicMessage(),
      data: { uri: this.uri },
    };
  }
}

Error Properties

PropertyTypeDescription
errorIdstringUnique ID for tracking (auto-generated)
isPublicbooleanWhether to expose message to clients
statusCodenumberHTTP status code equivalent
codestringError code for categorization
mcpErrorCodenumberJSON-RPC error code (optional)

Best Practices

Use Specific Error Classes

// Good
throw new ToolNotFoundError('my_tool');
throw new InvalidInputError('Email is required');

// Avoid
throw new Error('Tool not found');

Preserve Original Errors

try {
  await externalService.call();
} catch (error) {
  throw new ToolExecutionError('my_tool', error);
}

Don’t Expose Internal Details

// InternalMcpError hides message from clients
throw new InternalMcpError('Database connection failed: timeout after 30s');
// Client sees: "Internal FrontMCP error. Contact support with ID: err_abc123"

Use Error IDs for Tracking

try {
  // operation
} catch (error) {
  if (error instanceof McpError) {
    logger.error('Operation failed', {
      errorId: error.errorId,
      code: error.code,
    });
  }
  throw error;
}

Import

import {
  MCP_ERROR_CODES,
  McpError,
  PublicMcpError,
  InternalMcpError,
  ToolNotFoundError,
  ToolExecutionError,
  ResourceNotFoundError,
  ResourceReadError,
  InvalidInputError,
  InvalidOutputError,
  UnauthorizedError,
  RateLimitError,
  // ... more
} from '@frontmcp/sdk';