tak-server-mcp-skyfi
Version:
Model Context Protocol (MCP) server for TAK Server integration with geospatial support
167 lines • 5.8 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MCPClient = void 0;
const events_1 = require("events");
const axios_1 = __importDefault(require("axios"));
const ws_1 = __importDefault(require("ws"));
class MCPClient extends events_1.EventEmitter {
config;
ws;
axios;
eventSource;
requestId = 0;
pendingRequests = new Map();
constructor(config) {
super();
this.config = config;
if (config.transport === 'http') {
this.axios = axios_1.default.create({
baseURL: config.url,
headers: this.getAuthHeaders()
});
}
}
getAuthHeaders() {
const headers = {
'Content-Type': 'application/json'
};
if (this.config.auth) {
if (this.config.auth.type === 'bearer') {
headers['Authorization'] = `Bearer ${this.config.auth.credentials}`;
}
else if (this.config.auth.type === 'basic') {
headers['Authorization'] = `Basic ${this.config.auth.credentials}`;
}
}
return headers;
}
async connect() {
switch (this.config.transport) {
case 'ws':
return this.connectWebSocket();
case 'sse':
return this.connectSSE();
case 'http':
// HTTP doesn't need persistent connection
return Promise.resolve();
default:
throw new Error(`Unsupported transport: ${this.config.transport}`);
}
}
connectWebSocket() {
return new Promise((resolve, reject) => {
this.ws = new ws_1.default(this.config.url, {
headers: this.getAuthHeaders()
});
this.ws.on('open', () => {
console.log('MCP WebSocket connected');
resolve();
});
this.ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
// Handle JSON-RPC responses
if (message.id && this.pendingRequests.has(message.id)) {
const { resolve, reject } = this.pendingRequests.get(message.id);
this.pendingRequests.delete(message.id);
if (message.error) {
reject(new Error(message.error.message));
}
else {
resolve(message.result);
}
}
else if (message.method === 'notification') {
// Handle server-initiated notifications
this.emit('notification', message.params);
}
}
catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
});
this.ws.on('error', (error) => {
console.error('WebSocket error:', error);
reject(error);
});
this.ws.on('close', () => {
console.log('WebSocket disconnected');
this.emit('disconnected');
});
});
}
connectSSE() {
// SSE implementation would go here for browser environments
// For Node.js, we'd need a different approach
throw new Error('SSE transport not implemented for Node.js environment');
}
async listTools() {
const response = await this.callMethod('tools/list', {});
return response.tools || [];
}
async callTool(name, args) {
return this.callMethod('tools/call', {
name,
arguments: args
});
}
async callMethod(method, params) {
const id = ++this.requestId;
const request = {
jsonrpc: '2.0',
id,
method,
params
};
switch (this.config.transport) {
case 'http':
return this.httpCall(request);
case 'ws':
return this.wsCall(request);
default:
throw new Error(`Unsupported transport: ${this.config.transport}`);
}
}
async httpCall(request) {
if (!this.axios) {
throw new Error('HTTP client not initialized');
}
const response = await this.axios.post('/mcp', request);
if (response.data.error) {
throw new Error(response.data.error.message);
}
return response.data.result;
}
wsCall(request) {
if (!this.ws || this.ws.readyState !== ws_1.default.OPEN) {
throw new Error('WebSocket not connected');
}
return new Promise((resolve, reject) => {
this.pendingRequests.set(request.id, { resolve, reject });
this.ws.send(JSON.stringify(request));
// Timeout after 30 seconds
setTimeout(() => {
if (this.pendingRequests.has(request.id)) {
this.pendingRequests.delete(request.id);
reject(new Error('Request timeout'));
}
}, 30000);
});
}
async disconnect() {
if (this.ws) {
this.ws.close();
this.ws = undefined;
}
if (this.eventSource) {
this.eventSource.close();
this.eventSource = undefined;
}
this.removeAllListeners();
}
}
exports.MCPClient = MCPClient;
//# sourceMappingURL=mcp-client.js.map