UNPKG

@veris-ai/sdk

Version:
235 lines 9.78 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.VerisMCPServer = void 0; exports.createMCPServer = createMCPServer; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js"); const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js"); const types_js_1 = require("@modelcontextprotocol/sdk/types.js"); const express_1 = __importDefault(require("express")); const zod_1 = require("zod"); const zod_to_json_schema_1 = require("zod-to-json-schema"); const toolMock_1 = require("./toolMock"); const node_crypto_1 = require("node:crypto"); class VerisMCPServer { server; registeredFunctions = new Map(); name; description; transport; requireAuth; port; app; httpServer; transports = { streamable: {}, sse: {} }; constructor(options) { this.name = options.name; this.description = options.description; this.transport = options.transport || 'http'; this.requireAuth = options.requireAuth || false; this.port = options.port; } createMcpServer(authInfo) { const server = new mcp_js_1.McpServer({ name: this.name, version: '1.0.0' }); // Register all tools with the server using registerTool for (const [name, registered] of this.registeredFunctions) { const parametersSchema = registered.parametersSchema instanceof zod_1.z.ZodObject ? registered.parametersSchema.shape : {}; server.registerTool(name, { title: name, description: registered.description, inputSchema: parametersSchema, }, async (args) => { try { // Set session ID from auth token if available const token = authInfo?.token; if (token) { toolMock_1.veris.parseToken(token); } // Execute the function const result = await registered.func(args); // Clear session ID after execution toolMock_1.veris.clearContext(); return { content: [{ type: 'text', text: typeof result === 'string' ? result : JSON.stringify(result) }] }; } catch (error) { toolMock_1.veris.clearContext(); const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(errorMessage); } }); } return server; } registerFunction(func, options) { this.registeredFunctions.set(options.name, { func, ...options }); } getTools() { const tools = []; for (const [name, registered] of this.registeredFunctions) { const inputSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(registered.parametersSchema); tools.push({ name, description: registered.description, inputSchema: { type: 'object', properties: (inputSchema.properties || {}), required: (inputSchema.required || []) } }); } return tools; } extractBearerToken(authHeader) { if (!authHeader) return null; const parts = authHeader.split(' '); if (parts.length === 2 && parts[0] === 'Bearer') { return parts[1]; } return null; } setupExpressApp() { const app = (0, express_1.default)(); app.use(express_1.default.json()); if (this.transport === 'http') { // Handle POST requests for streamable HTTP transport app.post('/mcp', async (req, res) => { const sessionId = req.headers['mcp-session-id']; const authHeader = req.headers.authorization; let transport; // Extract bearer token for session ID if present const bearerToken = this.extractBearerToken(authHeader); if (sessionId && this.transports.streamable[sessionId]) { transport = this.transports.streamable[sessionId]; } else if (!sessionId && (0, types_js_1.isInitializeRequest)(req.body)) { transport = new streamableHttp_js_1.StreamableHTTPServerTransport({ sessionIdGenerator: () => (0, node_crypto_1.randomUUID)(), onsessioninitialized: (sessionId) => { this.transports.streamable[sessionId] = transport; } }); transport.onclose = () => { if (transport.sessionId) { delete this.transports.streamable[transport.sessionId]; } }; // Create MCP server with auth info if bearer token present const authInfo = bearerToken ? { token: bearerToken } : undefined; const server = this.createMcpServer(authInfo); await server.connect(transport); } else { res.status(400).json({ jsonrpc: '2.0', error: { code: -32000, message: 'Bad Request: No valid session ID provided', }, id: null, }); return; } await transport.handleRequest(req, res, req.body); }); // Handle GET and DELETE requests for session management const handleSessionRequest = async (req, res) => { const sessionId = req.headers['mcp-session-id']; if (!sessionId || !this.transports.streamable[sessionId]) { res.status(400).send('Invalid or missing session ID'); return; } const transport = this.transports.streamable[sessionId]; await transport.handleRequest(req, res); }; app.get('/mcp', handleSessionRequest); app.delete('/mcp', handleSessionRequest); } else if (this.transport === 'sse') { // SSE transport setup with custom auth middleware const sseMiddleware = (req, res, next) => { const token = this.extractBearerToken(req.headers.authorization); if (this.requireAuth && !token) { return res.status(401).json({ error: 'Unauthorized' }); } if (token) { req.authInfo = { token }; } next(); }; app.get('/sse', sseMiddleware, async (req, res) => { // Create SSE transport const transport = new sse_js_1.SSEServerTransport('/messages', res); this.transports.sse[transport.sessionId] = transport; res.on('close', () => { delete this.transports.sse[transport.sessionId]; }); // Create MCP server with auth info const authInfo = req.authInfo; const server = this.createMcpServer(authInfo); await server.connect(transport); }); // Handle messages for SSE transport app.post('/messages', sseMiddleware, async (req, res) => { const sessionId = req.query.sessionId; const transport = this.transports.sse[sessionId]; if (transport) { await transport.handlePostMessage(req, res, req.body); } else { res.status(400).send('No transport found for sessionId'); } }); } return app; } async listen(port) { if (this.transport === 'stdio') { // For stdio transport, use the standard MCP SDK transport const transport = new stdio_js_1.StdioServerTransport(); const server = this.createMcpServer(); await server.connect(transport); return {}; // Return dummy server for consistency } // For HTTP/SSE transport, use Express this.app = this.setupExpressApp(); this.httpServer = this.app.listen(port); console.log(`${this.name} MCP server listening on port ${port} (${this.transport} transport)`); return this.httpServer; } async start() { if (!this.port) { throw new Error('Port must be specified for standalone server'); } return this.listen(this.port); } static createStandalone(options) { return new VerisMCPServer(options); } } exports.VerisMCPServer = VerisMCPServer; // Export a function to create and configure an MCP server with decorated functions function createMCPServer(options) { return new VerisMCPServer(options); } //# sourceMappingURL=mcpServer.js.map