Skip to main content
Glama
impl.py6.02 kB
from typing import Dict, Optional, Any import asyncio from datetime import datetime import pytz from src.server_instance import mcp from src.celestial import celestial_pos, celestial_rise_set, calculate_moon_info, get_visible_planets, get_constellation_center, calculate_nightly_forecast from src.utils import process_location_and_time from src.response import format_response @mcp.tool() async def get_celestial_pos( celestial_object: str, lon: float, lat: float, time: str, time_zone: str ) -> Dict[str, Any]: """Calculate the altitude and azimuth angles of a celestial object. Args: celestial_object: Name of object (e.g. "sun", "moon", "andromeda") lon: Observer longitude in degrees lat: Observer latitude in degrees time: Observation time string "YYYY-MM-DD HH:MM:SS" time_zone: IANA timezone string Returns: Dict with keys "data", "_meta". "data" contains "altitude" and "azimuth" (degrees). """ location, time_info = process_location_and_time(lon, lat, time, time_zone) # Run synchronous celestial calculations in a separate thread to avoid blocking the event loop alt, az = await asyncio.to_thread(celestial_pos, celestial_object, location, time_info) return format_response({ "altitude": alt, "azimuth": az }) @mcp.tool() async def get_celestial_rise_set( celestial_object: str, lon: float, lat: float, time: str, time_zone: str ) -> Dict[str, Any]: """Calculate the rise and set times of a celestial object. Args: celestial_object: Name of object (e.g. "sun", "moon", "andromeda") lon: Observer longitude in degrees lat: Observer latitude in degrees time: Date string "YYYY-MM-DD HH:MM:SS" time_zone: IANA timezone string Returns: Dict with keys "data", "_meta". "data" contains "rise_time" and "set_time". """ location, time_info = process_location_and_time(lon, lat, time, time_zone) # Run synchronous celestial calculations in a separate thread rise_time, set_time = await asyncio.to_thread(celestial_rise_set, celestial_object, location, time_info) return format_response({ "rise_time": rise_time.isoformat() if rise_time else None, "set_time": set_time.isoformat() if set_time else None }) @mcp.tool() async def get_moon_info( time: str, time_zone: str ) -> Dict[str, Any]: """Get detailed information about the Moon's phase and position. Args: time: Date string "YYYY-MM-DD HH:MM:SS" time_zone: IANA timezone string Returns: Dict with keys "data", "_meta". "data" contains illumination, phase_name, age_days, etc. """ try: # Try standard format first dt = datetime.strptime(time, "%Y-%m-%d %H:%M:%S") except ValueError: try: # Try ISO format dt = datetime.fromisoformat(time) except ValueError: raise ValueError(f"Time string '{time}' matches neither '%Y-%m-%d %H:%M:%S' nor ISO format.") if dt.tzinfo is None: tz = pytz.timezone(time_zone) dt = tz.localize(dt) result = await asyncio.to_thread(calculate_moon_info, dt) return format_response(result) @mcp.tool() async def get_visible_planets( lon: float, lat: float, time: str, time_zone: str ) -> Dict[str, Any]: """Get a list of solar system planets currently visible (above horizon). Args: lon: Observer longitude in degrees lat: Observer latitude in degrees time: Observation time string "YYYY-MM-DD HH:MM:SS" time_zone: IANA timezone string Returns: Dict with keys "data", "_meta". "data" is a list of planet dicts (name, altitude, azimuth). """ location, time_info = process_location_and_time(lon, lat, time, time_zone) # Note: Function name collision with imported function 'get_visible_planets' # Use the imported function from src.celestial from src.celestial import get_visible_planets as calc_visible_planets planets = await asyncio.to_thread(calc_visible_planets, location, time_info) return format_response(planets) @mcp.tool() async def get_constellation( constellation_name: str, lon: float, lat: float, time: str, time_zone: str ) -> Dict[str, Any]: """Get the position (altitude/azimuth) of the center of a constellation. Args: constellation_name: Name of constellation (e.g. "Orion", "Ursa Major") lon: Observer longitude in degrees lat: Observer latitude in degrees time: Observation time string "YYYY-MM-DD HH:MM:SS" time_zone: IANA timezone string Returns: Dict with keys "data", "_meta". "data" contains name, altitude, azimuth. """ location, time_info = process_location_and_time(lon, lat, time, time_zone) result = await asyncio.to_thread(get_constellation_center, constellation_name, location, time_info) return format_response(result) @mcp.tool() async def get_nightly_forecast( lon: float, lat: float, time: str, time_zone: str, limit: int = 20 ) -> Dict[str, Any]: """Get a curated list of best objects to view for the night. Args: lon: Observer longitude in degrees lat: Observer latitude in degrees time: Date string "YYYY-MM-DD HH:MM:SS" (Time of observation, or just date) time_zone: IANA timezone string limit: Max number of deep-sky objects to return (default 20) Returns: Dict with keys: - moon_phase: Moon details - planets: List of visible planets - deep_sky: Sorted list of deep sky objects (Messier/NGC) """ location, time_info = process_location_and_time(lon, lat, time, time_zone) # Run in thread result = await asyncio.to_thread(calculate_nightly_forecast, location, time_info, limit) return format_response(result)

Latest Blog Posts

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/StarGazer1995/mcp-stargazing'

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