UNPKG

@l4t/mcp-ai

Version:

A set of tools for making integration and aggregation of MCP servers extremely easy.

119 lines 4.44 kB
import express from 'express'; import cors from 'cors'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { McpClientConfigs } from '../../common/types.js'; import { create as createFeatures } from '../features.js'; const HTTP_ERROR = 500; const BAD_REQUEST = 400; const NOT_FOUND_STATUS = 404; const DEFAULT_PORT = 3000; const create = (config, options) => { // eslint-disable-next-line functional/no-let let server; const transports = {}; const app = express(); app.use(express.json()); app.use(cors()); options?.preRouteMiddleware?.forEach(middleware => app.use(middleware)); const handleSseConnection = async (req, res) => { // eslint-disable-next-line functional/no-try-statements try { const transport = new SSEServerTransport('/messages', res); const sessionId = transport.sessionId; // eslint-disable-next-line functional/immutable-data transports[sessionId] = transport; // eslint-disable-next-line functional/immutable-data transport.onclose = () => { // eslint-disable-next-line functional/immutable-data delete transports[sessionId]; }; if (server) { await server.connect(transport); } } catch { if (!res.headersSent) { res.status(HTTP_ERROR).send('Error establishing SSE stream'); } throw new Error('Failed to establish SSE connection'); } }; const handlePostMessage = async (req, res) => { const sessionId = req.query.sessionId; if (!sessionId || typeof sessionId !== 'string') { res.status(BAD_REQUEST).send('Missing sessionId parameter'); return; } const transport = transports[sessionId]; if (!transport) { res.status(NOT_FOUND_STATUS).send('Session not found'); return; } // eslint-disable-next-line functional/no-try-statements try { await transport.handlePostMessage(req, res, req.body); } catch { if (!res.headersSent) { res.status(HTTP_ERROR).send('Error handling request'); } } }; const setupServer = async (features) => { features.validateConfig(); server = new McpServer(Object.assign({}, McpClientConfigs.aggregator, { capabilities: { tools: config.tools }, name: config.name, version: config.version, })); const formatted = features.getFormattedTools(); formatted.forEach(tool => { //@ts-ignore server.tool(...tool); }); }; const _routeWrapper = async (func) => { if (options?.afterRouteCallback) { return async (req, res) => { await func(req, res); // @ts-ignore await options.afterRouteCallback(req, res); }; } return func; }; const getApp = async () => { const features = await createFeatures(config); await setupServer(features); const path = config.server.path || '/'; const messagesPath = config.server.messagesPath || '/messages'; options?.additionalRoutes?.forEach(route => { app[route.method.toLowerCase()](route.path, route.handler); }); app.get(path, _routeWrapper(handleSseConnection)); app.post(messagesPath, _routeWrapper(handlePostMessage)); // Add catch-all route for non-existent URLs app.use(_routeWrapper((req, res) => { res.status(NOT_FOUND_STATUS).json({ error: 'Not Found', message: `The requested URL ${req.url} was not found on this server`, status: NOT_FOUND_STATUS, }); })); return app; }; return { getApp, start: async () => { const app = await getApp(); app.listen(config.server.connection.port || DEFAULT_PORT); }, stop: async () => { await Promise.all(Object.values(transports).map(transport => transport.close())); await server?.close(); }, }; }; export { create }; //# sourceMappingURL=sse.js.map