Remote-MCP Server

by ssut
import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, GetPromptRequestSchema, InitializeRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import type { AppRouter } from '@remote-mcp/server'; import { type CreateTRPCClient, createTRPCClient, httpBatchLink, } from '@trpc/client'; export interface RemoteMCPClientOptions { remoteUrl: string; headers?: Record<string, string>; onError?: (method: string, error: Error) => void; } export class RemoteMCPClient { public readonly server: Server; public readonly trpc: CreateTRPCClient<AppRouter>; private readonly options: RemoteMCPClientOptions; constructor(options: RemoteMCPClientOptions) { this.trpc = createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: options.remoteUrl, headers: options.headers, }), ], }); this.server = new Server( { name: 'Remote MCP Client', version: '1.0.0', }, { capabilities: { tools: { listChanged: true, }, resources: { subscribe: true, listChanged: true, }, prompts: { listChanged: true, }, logging: {}, }, }, ); this.options = { onError: (method, error) => { this.server.sendLoggingMessage({ level: 'error', data: `${method}: ${error.message}`, }); }, ...options, }; } /** @TODO implement */ private async setupNotificationHandler() {} private async setupHandlers() { this.server.setRequestHandler(InitializeRequestSchema, async (request) => { try { const response = await this.trpc.initialize.mutate(request); if (response.capabilities && response.capabilities) { // await this.setupNotificationHandler(); } return response; } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }); this.server.setRequestHandler(ListToolsRequestSchema, async (request) => { try { return await this.trpc['tools/list'].query(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }); this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { return await this.trpc['tools/call'].mutate(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }); this.server.setRequestHandler( ListResourcesRequestSchema, async (request) => { try { return await this.trpc['resources/list'].query(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }, ); this.server.setRequestHandler( ReadResourceRequestSchema, async (request) => { try { return await this.trpc['resources/read'].query(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }, ); this.server.setRequestHandler(SubscribeRequestSchema, async (request) => { try { return await this.trpc['resources/subscribe'].mutate(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }); this.server.setRequestHandler(UnsubscribeRequestSchema, async (request) => { try { return await this.trpc['resources/unsubscribe'].mutate(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }); // Prompts handlers this.server.setRequestHandler(ListPromptsRequestSchema, async (request) => { try { return await this.trpc['prompts/list'].query(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }); this.server.setRequestHandler(GetPromptRequestSchema, async (request) => { try { return await this.trpc['prompts/get'].query(request); } catch (error) { this.options.onError?.(request.method, error as Error); throw error; } }); } async start() { this.setupHandlers(); const transport = new StdioServerTransport(); await this.server.connect(transport); } }