tak-server-mcp-skyfi
Version:
Model Context Protocol (MCP) server for TAK Server integration with geospatial support
162 lines • 6.34 kB
JavaScript
;
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