UNPKG

tak-server-mcp-skyfi

Version:

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

211 lines 8.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.spatialQueryTool = void 0; const turf = __importStar(require("@turf/turf")); const mgrs_1 = __importDefault(require("mgrs")); const h3 = __importStar(require("h3-js")); exports.spatialQueryTool = { name: 'tak_spatial_query', description: 'Query TAK entities within a geographic area using point-radius or polygon', category: 'geospatial', requiresAuth: true, requiresWrite: false, inputSchema: { type: 'object', properties: { center: { type: 'array', items: { type: 'number' }, minItems: 2, maxItems: 2, description: 'Center point [lat, lon] for radius search' }, radius: { type: 'number', minimum: 0, description: 'Search radius in meters (required if using center point)' }, polygon: { type: 'array', items: { type: 'array', items: { type: 'number' }, minItems: 2, maxItems: 2 }, minItems: 3, description: 'Polygon coordinates [[lat, lon], ...] for polygon search' }, types: { type: 'array', items: { type: 'string' }, description: 'Filter by entity types' }, timeWindow: { type: 'object', properties: { start: { type: 'string', format: 'date-time' }, end: { type: 'string', format: 'date-time' } }, description: 'Time window for historical queries' }, includeStale: { type: 'boolean', default: false, description: 'Include stale entities in results' } }, oneOf: [ { required: ['center', 'radius'] }, { required: ['polygon'] } ] }, async handler(context) { const { takClient, params, logger } = context; logger.info('Performing spatial query', { params }); try { // Perform the spatial query const events = await takClient.spatialQuery({ center: params.center, radius: params.radius, polygon: params.polygon, types: params.types, timeWindow: params.timeWindow ? { start: new Date(params.timeWindow.start), end: new Date(params.timeWindow.end) } : undefined }); // Filter out stale events if requested const now = new Date(); const filteredEvents = params.includeStale ? events : events.filter(event => new Date(event.stale) > now); // Calculate additional geospatial properties const enrichedResults = filteredEvents.map(event => { const point = turf.point([event.point.lon, event.point.lat]); let distanceFromCenter = null; let bearing = null; if (params.center && params.radius) { const centerPoint = turf.point([params.center[1], params.center[0]]); distanceFromCenter = turf.distance(centerPoint, point, { units: 'meters' }); bearing = turf.bearing(centerPoint, point); } return { uid: event.uid, type: event.type, callsign: event.detail?.contact?.callsign, location: { lat: event.point.lat, lon: event.point.lon, alt: event.point.hae, mgrs: convertToMGRS(event.point.lat, event.point.lon), // Placeholder h3Index: getH3Index(event.point.lat, event.point.lon, 9) // Placeholder }, time: event.time, stale: event.stale, distanceFromCenter, bearing, team: event.detail?.group?.name, role: event.detail?.group?.role, status: event.detail?.status }; }); // Group results by type const groupedByType = enrichedResults.reduce((acc, event) => { const type = event.type || 'unknown'; if (!acc[type]) acc[type] = []; acc[type].push(event); return acc; }, {}); return { success: true, totalCount: enrichedResults.length, results: enrichedResults, summary: { byType: Object.entries(groupedByType).map(([type, items]) => ({ type, count: items.length })), bounds: calculateBounds(enrichedResults), timeRange: { earliest: enrichedResults.reduce((min, e) => new Date(e.time) < new Date(min) ? e.time : min, enrichedResults[0]?.time), latest: enrichedResults.reduce((max, e) => new Date(e.time) > new Date(max) ? e.time : max, enrichedResults[0]?.time) } } }; } catch (error) { logger.error('Spatial query failed', error); throw error; } } }; // Helper functions with real implementations function convertToMGRS(lat, lon) { try { return mgrs_1.default.forward([lon, lat]); } catch (error) { // Fallback if MGRS conversion fails return `${Math.abs(lat).toFixed(6)}${lat >= 0 ? 'N' : 'S'} ${Math.abs(lon).toFixed(6)}${lon >= 0 ? 'E' : 'W'}`; } } function getH3Index(lat, lon, resolution) { try { return h3.latLngToCell(lat, lon, resolution); } catch (error) { // Fallback if H3 conversion fails return `${lat.toFixed(2)}_${lon.toFixed(2)}_r${resolution}`; } } function calculateBounds(events) { if (events.length === 0) return null; const lats = events.map(e => e.location.lat); const lons = events.map(e => e.location.lon); return [ Math.min(...lons), Math.min(...lats), Math.max(...lons), Math.max(...lats) ]; } //# sourceMappingURL=spatial-query.js.map