Skip to main content
Glama

N2YO Satellite Tracker MCP Server

location-time-parser.ts16.6 kB
export interface LocationCoordinates { latitude: number; longitude: number; name: string; country?: string; } export interface ParsedDateTime { timestamp: number; timezone?: string; description: string; } export class LocationTimeParser { // Major countries and cities with their approximate center coordinates private static readonly LOCATION_DATABASE: Record<string, LocationCoordinates> = { // Countries "france": { latitude: 46.2276, longitude: 2.2137, name: "France", country: "France" }, "germany": { latitude: 51.1657, longitude: 10.4515, name: "Germany", country: "Germany" }, "spain": { latitude: 40.4637, longitude: -3.7492, name: "Spain", country: "Spain" }, "italy": { latitude: 41.8719, longitude: 12.5674, name: "Italy", country: "Italy" }, "united kingdom": { latitude: 55.3781, longitude: -3.4360, name: "United Kingdom", country: "UK" }, "uk": { latitude: 55.3781, longitude: -3.4360, name: "United Kingdom", country: "UK" }, "england": { latitude: 52.3555, longitude: -1.1743, name: "England", country: "UK" }, "usa": { latitude: 39.8283, longitude: -98.5795, name: "United States", country: "USA" }, "united states": { latitude: 39.8283, longitude: -98.5795, name: "United States", country: "USA" }, "america": { latitude: 39.8283, longitude: -98.5795, name: "United States", country: "USA" }, "canada": { latitude: 56.1304, longitude: -106.3468, name: "Canada", country: "Canada" }, "china": { latitude: 35.8617, longitude: 104.1954, name: "China", country: "China" }, "russia": { latitude: 61.5240, longitude: 105.3188, name: "Russia", country: "Russia" }, "japan": { latitude: 36.2048, longitude: 138.2529, name: "Japan", country: "Japan" }, "india": { latitude: 20.5937, longitude: 78.9629, name: "India", country: "India" }, "australia": { latitude: -25.2744, longitude: 133.7751, name: "Australia", country: "Australia" }, "brazil": { latitude: -14.2350, longitude: -51.9253, name: "Brazil", country: "Brazil" }, "mexico": { latitude: 23.6345, longitude: -102.5528, name: "Mexico", country: "Mexico" }, "south korea": { latitude: 35.9078, longitude: 127.7669, name: "South Korea", country: "South Korea" }, "netherlands": { latitude: 52.1326, longitude: 5.2913, name: "Netherlands", country: "Netherlands" }, "poland": { latitude: 51.9194, longitude: 19.1451, name: "Poland", country: "Poland" }, "ukraine": { latitude: 48.3794, longitude: 31.1656, name: "Ukraine", country: "Ukraine" }, "turkey": { latitude: 38.9637, longitude: 35.2433, name: "Turkey", country: "Turkey" }, "israel": { latitude: 31.0461, longitude: 34.8516, name: "Israel", country: "Israel" }, "saudi arabia": { latitude: 23.8859, longitude: 45.0792, name: "Saudi Arabia", country: "Saudi Arabia" }, "iran": { latitude: 32.4279, longitude: 53.6880, name: "Iran", country: "Iran" }, "egypt": { latitude: 26.0975, longitude: 30.0444, name: "Egypt", country: "Egypt" }, "south africa": { latitude: -30.5595, longitude: 22.9375, name: "South Africa", country: "South Africa" }, "nigeria": { latitude: 9.0820, longitude: 8.6753, name: "Nigeria", country: "Nigeria" }, "argentina": { latitude: -38.4161, longitude: -63.6167, name: "Argentina", country: "Argentina" }, "chile": { latitude: -35.6751, longitude: -71.5430, name: "Chile", country: "Chile" }, "colombia": { latitude: 4.5709, longitude: -74.2973, name: "Colombia", country: "Colombia" }, "peru": { latitude: -9.1900, longitude: -75.0152, name: "Peru", country: "Peru" }, "venezuela": { latitude: 6.4238, longitude: -66.5897, name: "Venezuela", country: "Venezuela" }, "ecuador": { latitude: -1.8312, longitude: -78.1834, name: "Ecuador", country: "Ecuador" }, "bolivia": { latitude: -16.2902, longitude: -63.5887, name: "Bolivia", country: "Bolivia" }, "uruguay": { latitude: -32.5228, longitude: -55.7658, name: "Uruguay", country: "Uruguay" }, "paraguay": { latitude: -23.4425, longitude: -58.4438, name: "Paraguay", country: "Paraguay" }, // Major cities "paris": { latitude: 48.8566, longitude: 2.3522, name: "Paris", country: "France" }, "london": { latitude: 51.5074, longitude: -0.1278, name: "London", country: "UK" }, "berlin": { latitude: 52.5200, longitude: 13.4050, name: "Berlin", country: "Germany" }, "madrid": { latitude: 40.4168, longitude: -3.7038, name: "Madrid", country: "Spain" }, "rome": { latitude: 41.9028, longitude: 12.4964, name: "Rome", country: "Italy" }, "new york": { latitude: 40.7128, longitude: -74.0060, name: "New York", country: "USA" }, "los angeles": { latitude: 34.0522, longitude: -118.2437, name: "Los Angeles", country: "USA" }, "chicago": { latitude: 41.8781, longitude: -87.6298, name: "Chicago", country: "USA" }, "houston": { latitude: 29.7604, longitude: -95.3698, name: "Houston", country: "USA" }, "toronto": { latitude: 43.6532, longitude: -79.3832, name: "Toronto", country: "Canada" }, "vancouver": { latitude: 49.2827, longitude: -123.1207, name: "Vancouver", country: "Canada" }, "tokyo": { latitude: 35.6762, longitude: 139.6503, name: "Tokyo", country: "Japan" }, "beijing": { latitude: 39.9042, longitude: 116.4074, name: "Beijing", country: "China" }, "shanghai": { latitude: 31.2304, longitude: 121.4737, name: "Shanghai", country: "China" }, "moscow": { latitude: 55.7558, longitude: 37.6176, name: "Moscow", country: "Russia" }, "mumbai": { latitude: 19.0760, longitude: 72.8777, name: "Mumbai", country: "India" }, "delhi": { latitude: 28.7041, longitude: 77.1025, name: "Delhi", country: "India" }, "sydney": { latitude: -33.8688, longitude: 151.2093, name: "Sydney", country: "Australia" }, "melbourne": { latitude: -37.8136, longitude: 144.9631, name: "Melbourne", country: "Australia" }, "sao paulo": { latitude: -23.5505, longitude: -46.6333, name: "São Paulo", country: "Brazil" }, "rio de janeiro": { latitude: -22.9068, longitude: -43.1729, name: "Rio de Janeiro", country: "Brazil" }, "mexico city": { latitude: 19.4326, longitude: -99.1332, name: "Mexico City", country: "Mexico" }, "amsterdam": { latitude: 52.3676, longitude: 4.9041, name: "Amsterdam", country: "Netherlands" }, "brussels": { latitude: 50.8503, longitude: 4.3517, name: "Brussels", country: "Belgium" }, "zurich": { latitude: 47.3769, longitude: 8.5417, name: "Zurich", country: "Switzerland" }, "vienna": { latitude: 48.2082, longitude: 16.3738, name: "Vienna", country: "Austria" }, "stockholm": { latitude: 59.3293, longitude: 18.0686, name: "Stockholm", country: "Sweden" }, "copenhagen": { latitude: 55.6761, longitude: 12.5683, name: "Copenhagen", country: "Denmark" }, "oslo": { latitude: 59.9139, longitude: 10.7522, name: "Oslo", country: "Norway" }, "helsinki": { latitude: 60.1699, longitude: 24.9384, name: "Helsinki", country: "Finland" }, "warsaw": { latitude: 52.2297, longitude: 21.0122, name: "Warsaw", country: "Poland" }, "prague": { latitude: 50.0755, longitude: 14.4378, name: "Prague", country: "Czech Republic" }, "budapest": { latitude: 47.4979, longitude: 19.0402, name: "Budapest", country: "Hungary" }, "bucharest": { latitude: 44.4268, longitude: 26.1025, name: "Bucharest", country: "Romania" }, "athens": { latitude: 37.9838, longitude: 23.7275, name: "Athens", country: "Greece" }, "istanbul": { latitude: 41.0082, longitude: 28.9784, name: "Istanbul", country: "Turkey" }, "tel aviv": { latitude: 32.0853, longitude: 34.7818, name: "Tel Aviv", country: "Israel" }, "jerusalem": { latitude: 31.7683, longitude: 35.2137, name: "Jerusalem", country: "Israel" }, "dubai": { latitude: 25.2048, longitude: 55.2708, name: "Dubai", country: "UAE" }, "riyadh": { latitude: 24.7136, longitude: 46.6753, name: "Riyadh", country: "Saudi Arabia" }, "cairo": { latitude: 30.0444, longitude: 31.2357, name: "Cairo", country: "Egypt" }, "cape town": { latitude: -33.9249, longitude: 18.4241, name: "Cape Town", country: "South Africa" }, "johannesburg": { latitude: -26.2041, longitude: 28.0473, name: "Johannesburg", country: "South Africa" }, "lagos": { latitude: 6.5244, longitude: 3.3792, name: "Lagos", country: "Nigeria" }, "buenos aires": { latitude: -34.6118, longitude: -58.3960, name: "Buenos Aires", country: "Argentina" }, "santiago": { latitude: -33.4489, longitude: -70.6693, name: "Santiago", country: "Chile" }, "bogota": { latitude: 4.7110, longitude: -74.0721, name: "Bogotá", country: "Colombia" }, "lima": { latitude: -12.0464, longitude: -77.0428, name: "Lima", country: "Peru" }, "caracas": { latitude: 10.4806, longitude: -66.9036, name: "Caracas", country: "Venezuela" }, "quito": { latitude: -0.1807, longitude: -78.4678, name: "Quito", country: "Ecuador" }, "la paz": { latitude: -16.5000, longitude: -68.1193, name: "La Paz", country: "Bolivia" }, "montevideo": { latitude: -34.9011, longitude: -56.1645, name: "Montevideo", country: "Uruguay" }, "asuncion": { latitude: -25.2637, longitude: -57.5759, name: "Asunción", country: "Paraguay" }, }; static parseLocation(locationText: string): LocationCoordinates | null { const normalized = locationText.toLowerCase().trim(); // Direct lookup if (this.LOCATION_DATABASE[normalized]) { return this.LOCATION_DATABASE[normalized]; } // Try partial matches for (const [key, coords] of Object.entries(this.LOCATION_DATABASE)) { if (key.includes(normalized) || normalized.includes(key)) { return coords; } } // Try with common variations const variations = [ normalized.replace(/\s+/g, ""), normalized.replace(/\s+/g, " "), normalized.replace(/^the\s+/, ""), ]; for (const variation of variations) { if (this.LOCATION_DATABASE[variation]) { return this.LOCATION_DATABASE[variation]; } } return null; } static parseDateTime(timeText: string, referenceLocation?: LocationCoordinates): ParsedDateTime | null { const now = new Date(); const normalized = timeText.toLowerCase().trim(); // Handle relative times like "tonight", "6:00 tonight", "tomorrow morning", etc. if (normalized.includes("tonight") || normalized.includes("this evening")) { const today = new Date(now); today.setHours(21, 0, 0, 0); // Default to 9 PM // Look for specific time like "6:00 tonight" const timeMatch = normalized.match(/(\d{1,2}):?(\d{2})?\s*(?:pm|am)?/); if (timeMatch) { let hours = parseInt(timeMatch[1]); const minutes = parseInt(timeMatch[2] || "0"); // If no AM/PM specified and it's a reasonable evening time, assume PM if (!normalized.includes("am") && !normalized.includes("pm") && hours <= 12) { if (hours < 6) hours += 12; // 6:00 tonight = 6 PM, not 6 AM } else if (normalized.includes("pm") && hours < 12) { hours += 12; } today.setHours(hours, minutes, 0, 0); } return { timestamp: Math.floor(today.getTime() / 1000), description: `Tonight at ${today.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })}`, }; } if (normalized.includes("tomorrow")) { const tomorrow = new Date(now); tomorrow.setDate(tomorrow.getDate() + 1); if (normalized.includes("morning")) { tomorrow.setHours(9, 0, 0, 0); } else if (normalized.includes("afternoon")) { tomorrow.setHours(15, 0, 0, 0); } else if (normalized.includes("evening") || normalized.includes("night")) { tomorrow.setHours(21, 0, 0, 0); } else { // Look for specific time const timeMatch = normalized.match(/(\d{1,2}):?(\d{2})?\s*(?:pm|am)?/); if (timeMatch) { let hours = parseInt(timeMatch[1]); const minutes = parseInt(timeMatch[2] || "0"); if (normalized.includes("pm") && hours < 12) { hours += 12; } else if (normalized.includes("am") && hours === 12) { hours = 0; } tomorrow.setHours(hours, minutes, 0, 0); } else { tomorrow.setHours(12, 0, 0, 0); // Default to noon } } return { timestamp: Math.floor(tomorrow.getTime() / 1000), description: `Tomorrow at ${tomorrow.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })}`, }; } if (normalized.includes("now") || normalized.includes("currently") || normalized.includes("right now")) { return { timestamp: Math.floor(now.getTime() / 1000), description: "Right now", }; } // Handle "in X hours/minutes" const inTimeMatch = normalized.match(/in\s+(\d+)\s+(hour|minute)s?/); if (inTimeMatch) { const amount = parseInt(inTimeMatch[1]); const unit = inTimeMatch[2]; const future = new Date(now); if (unit === "hour") { future.setHours(future.getHours() + amount); } else { future.setMinutes(future.getMinutes() + amount); } return { timestamp: Math.floor(future.getTime() / 1000), description: `In ${amount} ${unit}${amount > 1 ? 's' : ''}`, }; } // Handle specific times like "6:00 PM", "18:00", "6 PM" const timeMatch = normalized.match(/(\d{1,2}):?(\d{2})?\s*(pm|am)?/); if (timeMatch) { const today = new Date(now); let hours = parseInt(timeMatch[1]); const minutes = parseInt(timeMatch[2] || "0"); const period = timeMatch[3]; if (period === "pm" && hours < 12) { hours += 12; } else if (period === "am" && hours === 12) { hours = 0; } else if (!period && hours <= 12) { // No AM/PM specified, guess based on current time and reasonableness const currentHour = now.getHours(); if (hours < 6 && currentHour >= 12) { hours += 12; // Assume PM for times before 6 if it's already afternoon } } today.setHours(hours, minutes, 0, 0); // If the time has already passed today, assume tomorrow if (today < now) { today.setDate(today.getDate() + 1); } return { timestamp: Math.floor(today.getTime() / 1000), description: `Today at ${today.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true })}`, }; } return null; } static extractLocationAndTime(query: string): { location?: LocationCoordinates; time?: ParsedDateTime; cleanQuery: string; } { const normalized = query.toLowerCase(); // Extract location patterns const locationPatterns = [ /over\s+([a-zA-Z\s]+?)(?:\s+at|\s+tonight|\s+tomorrow|\s+in\s+\d+|\s+now|\s*$)/, /in\s+([a-zA-Z\s]+?)(?:\s+at|\s+tonight|\s+tomorrow|\s+in\s+\d+|\s+now|\s*$)/, /above\s+([a-zA-Z\s]+?)(?:\s+at|\s+tonight|\s+tomorrow|\s+in\s+\d+|\s+now|\s*$)/, ]; let location: LocationCoordinates | undefined; let locationMatch = ""; for (const pattern of locationPatterns) { const match = normalized.match(pattern); if (match) { locationMatch = match[1].trim(); location = this.parseLocation(locationMatch) || undefined; if (location) break; } } // Extract time patterns const timePatterns = [ /at\s+([^,]+?)(?:\s+over|\s+in|\s+above|\s*$)/, /(tonight|tomorrow|now|currently|right now)/, /(in\s+\d+\s+(?:hour|minute)s?)/, /(\d{1,2}:?\d{0,2}\s*(?:pm|am)?)/, ]; let time: ParsedDateTime | undefined; let timeMatch = ""; for (const pattern of timePatterns) { const match = normalized.match(pattern); if (match) { timeMatch = match[1].trim(); time = this.parseDateTime(timeMatch, location) || undefined; if (time) break; } } // Create clean query by removing location and time references let cleanQuery = query; if (locationMatch) { cleanQuery = cleanQuery.replace(new RegExp(`\\b(?:over|in|above)\\s+${locationMatch}\\b`, 'gi'), ''); } if (timeMatch) { cleanQuery = cleanQuery.replace(new RegExp(`\\b(?:at\\s+)?${timeMatch}\\b`, 'gi'), ''); } cleanQuery = cleanQuery.replace(/\s+/g, ' ').trim(); return { location, time, cleanQuery }; } static getAllSupportedLocations(): LocationCoordinates[] { return Object.values(this.LOCATION_DATABASE); } }

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