Skip to main content
Glama

Celestial Position MCP Server

by Rkm1999
CelestialDetailsTool.ts7.49 kB
import { MCPTool } from 'mcp-framework'; import { z } from 'zod'; import { OBSERVER_CONFIG } from '../config.js'; import { getEquatorialCoordinates, getObjectDetails, convertToAltAz, // Added this import EquatorialCoordinates } from '../utils/astronomy.js'; import * as Astronomy from 'astronomy-engine'; interface CelestialDetailsInput { objectName: string; // Removed: useSystemTime, dateTime, latitude, longitude, elevation } class CelestialDetailsTool extends MCPTool<CelestialDetailsInput> { name = 'getCelestialDetails'; description = "Retrieves detailed astronomical information for a specified celestial object (e.g., planet, star, Messier object, NGC/IC object). Information includes current equatorial and horizontal (altitude/azimuth) coordinates, visibility status (above/below horizon), rise/transit/set times, and, where applicable, distance, phase illumination, and upcoming moon phases. All calculations are performed for the pre-configured observer location and the current system time. The tool automatically resolves common names (e.g., 'Andromeda Galaxy' to 'M31') and handles various catalog identifiers."; protected schema = { objectName: { type: z.string(), description: "The name or catalog identifier of the celestial object. Examples: 'Jupiter', 'Sirius', 'M31', 'NGC 7000', 'Crab Nebula'. The tool will attempt to resolve common names." } // Removed: useSystemTime, dateTime, latitude, longitude, elevation }; async execute(params: CelestialDetailsInput) { try { // Always use current system time const date = new Date(); // Always use pre-configured observer location const observer = { latitude: OBSERVER_CONFIG.latitude, longitude: OBSERVER_CONFIG.longitude, elevation: OBSERVER_CONFIG.altitude, temperature: OBSERVER_CONFIG.temperature, pressure: OBSERVER_CONFIG.pressure }; // Get equatorial coordinates for the object let equatorialCoords: EquatorialCoordinates; try { equatorialCoords = await getEquatorialCoordinates(params.objectName, date); } catch (error: any) { throw new Error(`Could not find object: ${params.objectName}. ${error.message}`); } // Convert to horizontal (altaz) coordinates (NEW) const altazCoords = convertToAltAz(equatorialCoords, observer, date); // Get detailed information const details = getObjectDetails(params.objectName, date, observer); // Format the location for display const locationName = `Configured (${OBSERVER_CONFIG.latitude.toFixed(4)}°, ${OBSERVER_CONFIG.longitude.toFixed(4)}°)`; // Calculate visibility (NEW) const isAboveHorizon = altazCoords.altitude > 0; const visibility = isAboveHorizon ? altazCoords.altitude > 30 ? "Excellent visibility" : "Above horizon" : "Below horizon (not visible)"; // Format the response const response: any = { object: params.objectName, ...(typeof equatorialCoords.magnitude === 'number' && { apparentMagnitude: equatorialCoords.magnitude }), observationTime: date.toLocaleString() + " (system local time)", location: locationName, coordinates: { equatorial: { rightAscension: equatorialCoords.rightAscension.toFixed(4) + "h", declination: equatorialCoords.declination.toFixed(4) + "°" }, // Added altitude and azimuth to response horizontal: { altitude: altazCoords.altitude.toFixed(4) + "°", azimuth: altazCoords.azimuth.toFixed(4) + "°" } }, // Added these fields aboveHorizon: isAboveHorizon ? "Yes" : "No", visibility: visibility }; // Add rise/set/transit times if available if (details) { const formatTime = (timeObj: Date | Astronomy.AstroTime | null | undefined): string => { if (!timeObj) return "N/A"; // AstroTime objects have a .date property which is a JS Date. // Fixed objects might return JS Date directly for riseTime/setTime. const date = timeObj instanceof Date ? timeObj : new Date((timeObj as Astronomy.AstroTime).date); return date.toLocaleTimeString(); }; let note = ""; if (details.isCircumpolar) { if (details.alwaysAboveHorizon) { note = "This object is circumpolar and remains above the horizon from this location."; } else if (details.alwaysBelowHorizon) { note = "This object is circumpolar and remains below the horizon from this location."; } else { note = "This object is circumpolar from this location."; } } else { // Not circumpolar. Check if it doesn't rise/set and transit is below horizon. if (!details.riseTime && !details.setTime && details.transitTime && details.transitTime.hor && details.transitTime.hor.altitude < 0) { note = "This object does not rise above the horizon on this date from this location."; } } const riseStr = formatTime(details.riseTime); // details.transitTime is an event object like { time: AstroTime | Date, hor: HorizontalCoordinates } const transitStr = formatTime(details.transitTime ? details.transitTime.time : null); const setStr = formatTime(details.setTime); if (riseStr === "N/A" && transitStr === "N/A" && setStr === "N/A" && !note) { note = "Rise, transit, and set times are not available for this object on this date at this location."; } response.visibilityTimes = { rise: riseStr, transit: transitStr, set: setStr }; if (note) response.visibilityTimes.note = note; } else { response.visibilityTimes = { note: "Astronomical details, including rise/set times, could not be determined for this object." }; } // Add distance information if available if (details && details.distance) { response.distance = { astronomicalUnits: details.distance.au.toFixed(6), kilometers: Math.round(details.distance.km).toLocaleString() }; } // Add phase information for solar system objects if (details && details.phaseInfo) { response.phase = { percentIlluminated: details.phaseInfo.phasePercent.toFixed(1) + "%", trend: details.phaseInfo.isWaxing ? "Waxing" : "Waning" }; } // Add moon phase information if this is the Moon if (details && details.moonPhases && params.objectName.toLowerCase() === 'moon') { response.upcomingPhases = { newMoon: new Date(details.moonPhases.nextNewMoon.date).toLocaleDateString(), firstQuarter: new Date(details.moonPhases.nextFirstQuarter.date).toLocaleDateString(), fullMoon: new Date(details.moonPhases.nextFullMoon.date).toLocaleDateString(), lastQuarter: new Date(details.moonPhases.nextLastQuarter.date).toLocaleDateString() }; } return response; } catch (error: any) { throw new Error(`Failed to get celestial details: ${error.message}`); } } } // Export the class directly (not an instance) export default CelestialDetailsTool;

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/Rkm1999/CelestialMCP'

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