Skip to main content
Glama

N2YO Satellite Tracker MCP Server

space-catalog.ts13.4 kB
export interface TleData { line1: string; line2: string; epoch: string; } export interface SpaceObject { noradId: string; name: string; tle: TleData; objectType: "satellite" | "debris" | "rocket body" | "unknown"; source?: string; classification?: "unclassified" | "cui" | "secret"; createdAt: string; updatedAt: string; lastTleUpdate?: string; } export interface ConjunctionEvent { primaryObject: string; secondaryObject: string; timeOfClosestApproach: string; minimumDistance: number; relativeVelocity: number; probabilityOfCollision?: number; alertLevel: "low" | "medium" | "high" | "critical"; } export interface OrbitalPrediction { noradId: string; predictionTime: string; position: { x: number; y: number; z: number; }; velocity: { x: number; y: number; z: number; }; altitude: number; latitude: number; longitude: number; propagationModel: string; } export interface SpaceFenceObservation { noradId: string; observationTime: string; azimuth: number; elevation: number; range: number; rangeRate: number; radarCrossSection?: number; quality: number; } export interface CatalogStats { totalObjects: number; objectsByType: Record<string, number>; objectsByClassification: Record<string, number>; objectsBySource: Record<string, number>; averageTleAge: number; oldestTle: string; newestTle: string; lastUpdated: string; } export interface SensorStatus { sensorName: string; status: "operational" | "degraded" | "offline"; lastDataReceived: string; observationsToday: number; healthScore: number; } export class SpaceCatalog { private apiHeaders: Record<string, string> = {}; private apiEndpoint: string = "https://unifieddatalibrary.com/api"; setApiHeaders(headers: Record<string, string>): void { this.apiHeaders = headers; } setApiEndpoint(endpoint: string): void { this.apiEndpoint = endpoint; } private async makeApiCall(endpoint: string, params?: Record<string, any>): Promise<any> { const url = new URL(`${this.apiEndpoint}${endpoint}`); if (params) { Object.keys(params).forEach(key => { if (params[key] !== undefined) { url.searchParams.append(key, params[key]); } }); } // Simulate API call for now - in real implementation would use fetch console.log(`API Call: ${url.toString()}`); console.log(`Headers:`, this.apiHeaders); // Return mock data based on endpoint return this.getMockResponse(endpoint, params); } private getMockResponse(endpoint: string, params?: Record<string, any>): any { // Simulate different API responses based on endpoint if (endpoint.includes('/objects/')) { return this.getMockSpaceObject(params?.noradId || '25544'); } else if (endpoint.includes('/objects')) { return this.getMockSearchResults(params); } else if (endpoint.includes('/conjunctions')) { return this.getMockConjunctions(params); } else if (endpoint.includes('/predictions')) { return this.getMockPrediction(params); } else if (endpoint.includes('/spacefence')) { return this.getMockSpaceFenceData(params); } else if (endpoint.includes('/catalog/stats')) { return this.getMockCatalogStats(); } else if (endpoint.includes('/sensors/status')) { return this.getMockSensorStatus(); } return {}; } private getMockSpaceObject(noradId: string): SpaceObject { return { noradId, name: noradId === '25544' ? 'ISS (ZARYA)' : `OBJECT-${noradId}`, tle: { line1: `1 ${noradId.padStart(5)}U 98067A 24001.50000000 .00002182 00000-0 40201-4 0 9990`, line2: `2 ${noradId.padStart(5)} 51.6461 339.7939 0001397 83.2918 276.8626 15.48919103123456`, epoch: "2024-01-01T12:00:00.000Z" }, objectType: noradId === '25544' ? 'satellite' : 'unknown', source: '18SDS', classification: 'unclassified', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: new Date().toISOString(), lastTleUpdate: new Date().toISOString(), }; } private getMockSearchResults(params?: Record<string, any>): SpaceObject[] { const results = []; const count = Math.min(params?.limit || 10, 50); for (let i = 0; i < count; i++) { const noradId = (25544 + i).toString(); results.push(this.getMockSpaceObject(noradId)); } return results; } private getMockConjunctions(params?: Record<string, any>): ConjunctionEvent[] { const conjunctions = []; const count = Math.floor(Math.random() * 5) + 1; for (let i = 0; i < count; i++) { conjunctions.push({ primaryObject: params?.primaryObject || '25544', secondaryObject: (25545 + i).toString(), timeOfClosestApproach: new Date(Date.now() + i * 3600000).toISOString(), minimumDistance: Math.random() * 5, relativeVelocity: Math.random() * 15000 + 5000, probabilityOfCollision: Math.random() * 0.001, alertLevel: this.getRandomAlertLevel(), }); } return conjunctions; } private getMockPrediction(params?: Record<string, any>): OrbitalPrediction { return { noradId: params?.noradId || '25544', predictionTime: params?.predictionTime || new Date().toISOString(), position: { x: Math.random() * 13000 - 6500, y: Math.random() * 13000 - 6500, z: Math.random() * 13000 - 6500, }, velocity: { x: Math.random() * 16 - 8, y: Math.random() * 16 - 8, z: Math.random() * 16 - 8, }, altitude: Math.random() * 2000 + 200, latitude: Math.random() * 180 - 90, longitude: Math.random() * 360 - 180, propagationModel: params?.propagationModel || 'SGP4', }; } private getMockSpaceFenceData(params?: Record<string, any>): SpaceFenceObservation[] { const observations = []; const count = Math.floor(Math.random() * 10) + 1; for (let i = 0; i < count; i++) { observations.push({ noradId: params?.noradId || '25544', observationTime: new Date(Date.now() - i * 3600000).toISOString(), azimuth: Math.random() * 360, elevation: Math.random() * 90, range: Math.random() * 2000 + 200, rangeRate: Math.random() * 20 - 10, radarCrossSection: Math.random() * 10, quality: Math.random() * 100, }); } return observations; } private getMockCatalogStats(): CatalogStats { return { totalObjects: 47832, objectsByType: { satellite: 8234, debris: 35421, "rocket body": 4177, }, objectsByClassification: { unclassified: 42000, cui: 4832, secret: 1000, }, objectsBySource: { "18SDS": 30000, "Commercial": 12000, "International": 5832, }, averageTleAge: 2.3, oldestTle: "2024-01-01T00:00:00.000Z", newestTle: new Date().toISOString(), lastUpdated: new Date().toISOString(), }; } private getMockSensorStatus(): SensorStatus[] { return [ { sensorName: "Space Fence (Kwajalein)", status: "operational", lastDataReceived: new Date(Date.now() - 300000).toISOString(), observationsToday: 15420, healthScore: 98.5, }, { sensorName: "Eglin AFB Radar", status: "operational", lastDataReceived: new Date(Date.now() - 180000).toISOString(), observationsToday: 8760, healthScore: 95.2, }, { sensorName: "Commercial SSA Provider 1", status: "operational", lastDataReceived: new Date(Date.now() - 120000).toISOString(), observationsToday: 45200, healthScore: 92.7, }, ]; } private getRandomAlertLevel(): "low" | "medium" | "high" | "critical" { const levels = ["low", "medium", "high", "critical"]; return levels[Math.floor(Math.random() * levels.length)] as any; } // Public API methods that call the UDL API async getObject(noradId: string): Promise<SpaceObject | null> { try { const response = await this.makeApiCall(`/catalog/objects/${noradId}`); return response; } catch (error) { console.error(`Failed to get object ${noradId}:`, error); return null; } } async searchObjects(criteria: any): Promise<SpaceObject[]> { try { const params: Record<string, any> = {}; if (criteria.name) params.name = criteria.name; if (criteria.objectType) params.type = criteria.objectType; if (criteria.source) params.source = criteria.source; if (criteria.classification) params.classification = criteria.classification; if (criteria.epochRange) { params.epoch_start = criteria.epochRange.start; params.epoch_end = criteria.epochRange.end; } const response = await this.makeApiCall('/catalog/objects', params); return Array.isArray(response) ? response : []; } catch (error) { console.error('Failed to search objects:', error); return []; } } async getConjunctions( primaryObject: string, timeWindow: { start: string; end: string }, threshold: number ): Promise<ConjunctionEvent[]> { try { const params = { primary: primaryObject, start_time: timeWindow.start, end_time: timeWindow.end, threshold: threshold.toString(), }; const response = await this.makeApiCall('/analysis/conjunctions', params); return Array.isArray(response) ? response : []; } catch (error) { console.error('Failed to get conjunctions:', error); return []; } } async predictOrbit( noradId: string, predictionTime: string, propagationModel: string ): Promise<OrbitalPrediction> { try { const params = { norad_id: noradId, prediction_time: predictionTime, model: propagationModel, }; const response = await this.makeApiCall('/analysis/predictions', params); return response; } catch (error) { console.error('Failed to get orbital prediction:', error); throw error; } } async getSpaceFenceObservations( noradId: string, timeRange?: { start: string; end: string } ): Promise<SpaceFenceObservation[]> { try { const params: Record<string, any> = { norad_id: noradId }; if (timeRange) { params.start_time = timeRange.start; params.end_time = timeRange.end; } const response = await this.makeApiCall('/sensors/spacefence/observations', params); return Array.isArray(response) ? response : []; } catch (error) { console.error('Failed to get Space Fence data:', error); return []; } } async getCatalogStats(): Promise<CatalogStats> { try { const response = await this.makeApiCall('/catalog/statistics'); return response; } catch (error) { console.error('Failed to get catalog stats:', error); throw error; } } async getSpaceObjectSchema(): Promise<any> { return { version: "1.0.0", objectStructure: { required: ["noradId", "name", "tle", "objectType", "createdAt", "updatedAt"], optional: ["source", "classification", "lastTleUpdate"], }, tleStructure: { required: ["line1", "line2", "epoch"], format: "NORAD Two-Line Element Set", }, enums: { objectType: ["satellite", "debris", "rocket body", "unknown"], classification: ["unclassified", "cui", "secret"], }, }; } async getActiveConjunctions(): Promise<ConjunctionEvent[]> { try { const now = new Date(); const next24Hours = new Date(now.getTime() + 24 * 60 * 60 * 1000); const params = { start_time: now.toISOString(), end_time: next24Hours.toISOString(), active_only: 'true', }; const response = await this.makeApiCall('/analysis/conjunctions/active', params); return Array.isArray(response) ? response : []; } catch (error) { console.error('Failed to get active conjunctions:', error); return []; } } async getSensorStatus(): Promise<SensorStatus[]> { try { const response = await this.makeApiCall('/sensors/status'); return Array.isArray(response) ? response : this.getMockSensorStatus(); } catch (error) { console.error('Failed to get sensor status:', error); return this.getMockSensorStatus(); } } async getTleAgeReport(): Promise<any> { try { const response = await this.makeApiCall('/catalog/tle-age-report'); return response; } catch (error) { console.error('Failed to get TLE age report:', error); // Return mock data return { ageDistribution: { "0-1 days": 12000, "1-3 days": 15000, "3-7 days": 10000, "7-14 days": 7000, "14+ days": 3832, }, staleTles: [ { noradId: "12345", name: "OLD SATELLITE", ageInDays: 45.2 }, { noradId: "23456", name: "DEBRIS PIECE", ageInDays: 38.7 }, ], totalObjects: 47832, generatedAt: new Date().toISOString(), }; } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MaxwellCalkin/N2YO-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server