Skip to main content
Glama
request-handler-factory.ts6.4 kB
/** * Request Handler Factory for MCP WordPress Remote * * Creates standardized request handlers to eliminate repetitive code */ import { logger } from './utils.js'; import { wpRequest } from './wordpress-api.js'; import { isAPIError } from './oauth-types.js'; import { convertAPIErrorToMcpError } from './error-utils.js'; import { prepareRequest, SessionContext } from './session-utils.js'; import { WPRequestParams } from './mcp-types.js'; /** * Configuration for creating a request handler */ export interface HandlerConfig { name: string; method: string; paramMapper: (request: any) => WPRequestParams; } /** * Create a standardized MCP request handler */ export function createRequestHandler(config: HandlerConfig, context: SessionContext) { return async (request: any) => { logger.debug(`Processing ${config.name}Request`, 'MCP'); // Map request parameters to WordPress format const wpParams = config.paramMapper(request); // Prepare request based on transport type const requestData = prepareRequest(wpParams, request, context); // Send request to WordPress const response = await wpRequest(requestData, context.transportType === 'jsonrpc'); return response; }; } /** * Create a request handler with logging and error handling wrapper */ export function createWrappedHandler(config: HandlerConfig, context: SessionContext) { const handler = createRequestHandler(config, context); return async (request: any) => { // Enhanced client message logging logger.info(`📩 Client Message: ${config.name}`, 'CLIENT'); logger.info(`Request ID: ${request.id || 'none'} | Method: ${config.method}`, 'CLIENT'); // Log request parameters in a structured way if (request.params && Object.keys(request.params).length > 0) { logger.info(`Request parameters:`, 'CLIENT', { paramCount: Object.keys(request.params).length, paramKeys: Object.keys(request.params), }); logger.debug(`Full request parameters:`, 'CLIENT', request.params); } else { logger.info(`No request parameters`, 'CLIENT'); } // Log session context logger.debug( `Session context - ID: ${context.sessionId || 'none'}, Transport: ${context.transportType || 'detecting'}, Counter: ${context.requestIdCounter}`, 'CLIENT' ); // Log full request object at debug level for detailed debugging logger.debug(`Complete client request object:`, 'CLIENT', request); const startTime = Date.now(); try { const response = await handler(request); const duration = Date.now() - startTime; logger.info(`✅ Client Response: ${config.name} completed in ${duration}ms`, 'CLIENT'); logger.debug(`Response sent to client:`, 'CLIENT', { hasResponse: !!response, responseKeys: response && typeof response === 'object' ? Object.keys(response) : 'primitive', }); return response; } catch (error) { const duration = Date.now() - startTime; logger.error(`❌ Client Error: ${config.name} failed after ${duration}ms`, 'CLIENT', { error: error instanceof Error ? error.message : String(error), requestId: request.id, }); // Only convert APIError to MCP error format for simple transport // JSON-RPC transport already returns properly formatted JSON-RPC errors if (isAPIError(error) && context.transportType === 'simple') { logger.debug(`Converting APIError to MCP error format for ${config.name} (simple transport)`, 'MCP', { statusCode: error.statusCode, endpoint: error.endpoint, message: error.message, }); return convertAPIErrorToMcpError(error); } // For JSON-RPC transport or non-API errors, re-throw (MCP SDK will handle) throw error; } }; } /** * Predefined handler configurations for all MCP methods */ export const HANDLER_CONFIGS: Record<string, HandlerConfig> = { listTools: { name: 'ListTools', method: 'tools/list', paramMapper: (request) => ({ method: 'tools/list', cursor: request.params?.cursor, }), }, callTool: { name: 'CallTool', method: 'tools/call', paramMapper: (request) => ({ method: 'tools/call', name: request.params.name, arguments: request.params.arguments, }), }, listResources: { name: 'ListResources', method: 'resources/list', paramMapper: (request) => ({ method: 'resources/list', cursor: request.params?.cursor, }), }, listResourceTemplates: { name: 'ListResourceTemplates', method: 'resources/templates/list', paramMapper: (request) => ({ method: 'resources/templates/list', cursor: request.params?.cursor, }), }, readResource: { name: 'ReadResource', method: 'resources/read', paramMapper: (request) => ({ method: 'resources/read', uri: request.params.uri, }), }, subscribe: { name: 'Subscribe', method: 'resources/subscribe', paramMapper: (request) => ({ method: 'resources/subscribe', uri: request.params.uri, }), }, unsubscribe: { name: 'Unsubscribe', method: 'resources/unsubscribe', paramMapper: (request) => ({ method: 'resources/unsubscribe', uri: request.params.uri, }), }, listPrompts: { name: 'ListPrompts', method: 'prompts/list', paramMapper: (request) => ({ method: 'prompts/list', cursor: request.params?.cursor, }), }, getPrompt: { name: 'GetPrompt', method: 'prompts/get', paramMapper: (request) => ({ method: 'prompts/get', name: request.params.name, arguments: request.params.arguments, }), }, setLevel: { name: 'SetLevel', method: 'logging/setLevel', paramMapper: (request) => ({ method: 'logging/setLevel', level: request.params.level, }), }, complete: { name: 'Complete', method: 'completion/complete', paramMapper: (request) => ({ method: 'completion/complete', ref: request.params.ref, argument: request.params.argument, }), }, listRoots: { name: 'ListRoots', method: 'roots/list', paramMapper: (request) => ({ method: 'roots/list', }), }, };

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/Automattic/mcp-wordpress-remote'

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