Skip to main content
Glama
cameronsjo

MCP Server Template

by cameronsjo
problem-details.ts5.6 kB
/** * Problem Details for HTTP APIs (RFC 9457) * * Provides standardized error response format for better debugging * and client error handling. * * @see https://www.rfc-editor.org/rfc/rfc9457 */ import { ErrorCode } from '../types/index.js'; /** * Problem Details response structure per RFC 9457 */ export interface ProblemDetails { /** URI reference identifying the problem type */ type: string; /** Short, human-readable summary */ title: string; /** HTTP status code */ status: number; /** Human-readable explanation specific to this occurrence */ detail?: string; /** URI reference identifying the specific occurrence */ instance?: string; /** Additional properties */ [key: string]: unknown; } /** * Problem type registry */ const PROBLEM_TYPES: Record<ErrorCode, { type: string; title: string; status: number }> = { NOT_FOUND: { type: 'https://problems.example.com/not-found', title: 'Resource Not Found', status: 404, }, VALIDATION_ERROR: { type: 'https://problems.example.com/validation-error', title: 'Validation Error', status: 400, }, RATE_LIMITED: { type: 'https://problems.example.com/rate-limited', title: 'Rate Limit Exceeded', status: 429, }, UNAUTHORIZED: { type: 'https://problems.example.com/unauthorized', title: 'Unauthorized', status: 401, }, FORBIDDEN: { type: 'https://problems.example.com/forbidden', title: 'Forbidden', status: 403, }, INTERNAL_ERROR: { type: 'https://problems.example.com/internal-error', title: 'Internal Server Error', status: 500, }, TIMEOUT: { type: 'https://problems.example.com/timeout', title: 'Request Timeout', status: 504, }, CACHE_ERROR: { type: 'https://problems.example.com/cache-error', title: 'Cache Error', status: 500, }, EXTERNAL_API_ERROR: { type: 'https://problems.example.com/external-api-error', title: 'External API Error', status: 502, }, }; /** * Create a Problem Details response from an error code */ export function createProblemDetails( code: ErrorCode, detail?: string, extensions?: Record<string, unknown> ): ProblemDetails { const base = PROBLEM_TYPES[code] ?? PROBLEM_TYPES.INTERNAL_ERROR; return { type: base.type, title: base.title, status: base.status, detail, ...extensions, }; } /** * Create a validation error with field-level details */ export function createValidationProblem( errors: Array<{ field: string; message: string; code?: string }> ): ProblemDetails { return { type: 'https://problems.example.com/validation-error', title: 'Validation Error', status: 400, detail: 'One or more fields failed validation', errors, }; } /** * Create a rate limit error with retry information */ export function createRateLimitProblem(retryAfterSeconds: number): ProblemDetails { return { type: 'https://problems.example.com/rate-limited', title: 'Rate Limit Exceeded', status: 429, detail: `Rate limit exceeded. Retry after ${retryAfterSeconds} seconds.`, retryAfter: retryAfterSeconds, }; } /** * Create an authentication error */ export function createAuthProblem(detail?: string): ProblemDetails { return { type: 'https://problems.example.com/unauthorized', title: 'Unauthorized', status: 401, detail: detail ?? 'Authentication required', }; } /** * Create a not found error */ export function createNotFoundProblem(resource: string, identifier?: string): ProblemDetails { return { type: 'https://problems.example.com/not-found', title: 'Resource Not Found', status: 404, detail: identifier ? `${resource} not found: ${identifier}` : `${resource} not found`, resource, identifier, }; } /** * Create an external API error */ export function createExternalApiProblem( service: string, detail: string, upstreamStatus?: number ): ProblemDetails { return { type: 'https://problems.example.com/external-api-error', title: 'External API Error', status: 502, detail: `${service}: ${detail}`, service, upstreamStatus, }; } /** * Convert Problem Details to MCP error format */ export function problemToMcpError(problem: ProblemDetails): { error: string; code: string; details?: Record<string, unknown>; } { const { type, title, status, detail, ...rest } = problem; return { error: detail ?? title, code: type.split('/').pop()?.toUpperCase().replace(/-/g, '_') ?? 'INTERNAL_ERROR', details: Object.keys(rest).length > 0 ? rest : undefined, }; } /** * Check if an object is a Problem Details response */ export function isProblemDetails(obj: unknown): obj is ProblemDetails { if (typeof obj !== 'object' || obj === null) { return false; } const problem = obj as Record<string, unknown>; return ( typeof problem['type'] === 'string' && typeof problem['title'] === 'string' && typeof problem['status'] === 'number' ); } /** * Express-compatible error handler that returns Problem Details */ export function problemDetailsMiddleware() { return ( err: Error & { code?: ErrorCode; status?: number }, _req: unknown, res: { status: (code: number) => { json: (body: unknown) => void }; set: (header: string, value: string) => void }, _next: unknown ): void => { const code = err.code ?? 'INTERNAL_ERROR'; const problem = createProblemDetails(code as ErrorCode, err.message); res.set('Content-Type', 'application/problem+json'); res.status(problem.status).json(problem); }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cameronsjo/mcp-server-template'

If you have feedback or need assistance with the MCP directory API, please join our Discord server