UNPKG

tak-server-mcp-skyfi

Version:

Model Context Protocol (MCP) server for TAK Server integration with geospatial support

162 lines 6.34 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpServerTransport = void 0; exports.createHttpTransport = createHttpTransport; const express_1 = __importDefault(require("express")); const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const pino_1 = __importDefault(require("pino")); const logger = (0, pino_1.default)({ level: process.env.LOG_LEVEL || 'info' }); class HttpServerTransport { server; app; config; constructor(server, config) { this.server = server; this.config = config; this.app = (0, express_1.default)(); this.setupMiddleware(); this.setupRoutes(); } setupMiddleware() { this.app.use(express_1.default.json({ limit: '10mb' })); this.app.use(express_1.default.urlencoded({ extended: true })); // CORS this.app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') { return res.sendStatus(200); } next(); }); // Authentication middleware if (this.config.mcp.auth?.enabled) { this.app.use(this.authMiddleware.bind(this)); } // Request logging this.app.use((req, res, next) => { logger.info({ method: req.method, path: req.path }, 'HTTP request'); next(); }); } authMiddleware(req, res, next) { const authHeader = req.headers.authorization; if (!authHeader) { return res.status(401).json({ error: 'No authorization header' }); } const authConfig = this.config.mcp.auth; switch (authConfig.method) { case 'oauth2': const token = authHeader.replace('Bearer ', ''); try { // Verify JWT token (would need proper OAuth2 validation in production) jsonwebtoken_1.default.verify(token, authConfig.oauth.clientSecret); next(); } catch (error) { return res.status(401).json({ error: 'Invalid token' }); } break; case 'apikey': if (authHeader !== `Bearer ${process.env.MCP_API_KEY}`) { return res.status(401).json({ error: 'Invalid API key' }); } next(); break; case 'basic': // Basic auth implementation const base64Credentials = authHeader.split(' ')[1]; const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii'); const [username, password] = credentials.split(':'); if (username !== process.env.MCP_USERNAME || password !== process.env.MCP_PASSWORD) { return res.status(401).json({ error: 'Invalid credentials' }); } next(); break; default: next(); } } setupRoutes() { // Health check this.app.get('/health', (req, res) => { res.json({ status: 'ok', service: 'tak-server-mcp', version: '0.1.0' }); }); // MCP endpoint this.app.post('/mcp', async (req, res) => { try { const request = req.body; logger.debug({ method: request.method }, 'Processing RPC request'); // Forward to MCP server const response = await this.handleRequest(request); res.json(response); } catch (error) { logger.error('Error handling request:', error); res.status(500).json({ jsonrpc: '2.0', id: req.body.id, error: { code: -32603, message: error instanceof Error ? error.message : 'Internal error' } }); } }); // Batch requests this.app.post('/mcp/batch', async (req, res) => { if (!Array.isArray(req.body)) { return res.status(400).json({ error: 'Batch requests must be an array' }); } try { const responses = await Promise.all(req.body.map(request => this.handleRequest(request))); res.json(responses); } catch (error) { logger.error('Error handling batch request:', error); res.status(500).json({ error: 'Batch processing failed' }); } }); } async handleRequest(request) { // The server expects to handle the request directly // We need to simulate the transport layer communication return new Promise((resolve) => { // Create a mock connection that captures the response const mockConnection = { send: (response) => { resolve(response); } }; // Process the request through the server this.server.handleRequest(request, mockConnection); }); } async start() { const port = this.config.mcp.port || 3000; return new Promise((resolve, reject) => { this.app.listen(port, () => { logger.info(`HTTP transport listening on port ${port}`); resolve(); }).on('error', reject); }); } async close() { // Express doesn't provide a direct way to close the server // In production, you'd keep a reference to the server instance logger.info('HTTP transport closing'); } } exports.HttpServerTransport = HttpServerTransport; async function createHttpTransport(server, config) { const transport = new HttpServerTransport(server, config); await transport.start(); return transport; } //# sourceMappingURL=http.js.map