Skip to main content
Glama

get_moon_info

Retrieve the Moon's phase, illumination, and position data for any specified date and time zone to support stargazing planning and astronomical observations.

Instructions

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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
timeYes
time_zoneYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The MCP tool handler for get_moon_info. Decorated with @mcp.tool(), it validates and parses the time input, localizes to timezone, calls the calculate_moon_info helper in a thread, and formats the response.
    @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)
  • Supporting helper function that performs the actual astronomical calculations for moon phase, illumination, age, elongation, and distance using Astropy.
    def calculate_moon_info(time: Union[Time, datetime]) -> Dict[str, Any]:
        """
        Calculate detailed information about the Moon's phase and position.
        
        Args:
            time: Observation time (Astropy Time or timezone-aware datetime).
            
        Returns:
            Dict containing:
            - illumination: Fraction of the moon illuminated (0.0 to 1.0)
            - phase_name: String description of the phase (e.g. "Waxing Gibbous")
            - age_days: Approximate age of the moon in days (since New Moon)
            - elongation: Angular separation from Sun in degrees
            - earth_distance: Distance from Earth in km
        """
        # Convert local time to UTC if input is datetime
        if isinstance(time, datetime):
            if time.tzinfo is None:
                raise ValueError("Input datetime must be timezone-aware for local time.")
            time = Time(time.astimezone(pytz.UTC))
    
        sun = get_sun(time)
        moon = get_body("moon", time)
    
        # Elongation (angular separation)
        elongation = sun.separation(moon)
        
        # Illumination fraction (0-1)
        # The illuminated fraction k is given by k = (1 - cos(i))/2 where i is phase angle (approx elongation)
        # New Moon (0 deg): (1 - 1)/2 = 0
        # Full Moon (180 deg): (1 - (-1))/2 = 1
        illumination = (1 - np.cos(elongation.rad)) / 2.0
        
        # Phase angle for naming (requires Ecliptic longitude)
        sun_ecl = sun.transform_to(GeocentricTrueEcliptic(obstime=time))
        moon_ecl = moon.transform_to(GeocentricTrueEcliptic(obstime=time))
        
        # Calculate longitude difference (Moon - Sun)
        lon_diff = (moon_ecl.lon.deg - sun_ecl.lon.deg) % 360
        
        # Determine Phase Name
        # New Moon: 0
        # First Quarter: 90
        # Full Moon: 180
        # Last Quarter: 270
        
        if lon_diff < 1 or lon_diff > 359:
            phase_name = "New Moon"
        elif 1 <= lon_diff < 89:
            phase_name = "Waxing Crescent"
        elif 89 <= lon_diff <= 91:
             phase_name = "First Quarter"
        elif 91 < lon_diff < 179:
            phase_name = "Waxing Gibbous"
        elif 179 <= lon_diff <= 181:
            phase_name = "Full Moon"
        elif 181 < lon_diff < 269:
            phase_name = "Waning Gibbous"
        elif 269 <= lon_diff <= 271:
            phase_name = "Last Quarter"
        else:
            phase_name = "Waning Crescent"
    
        # Age in days (approximate)
        # Synodic month is ~29.53 days. Age = (lon_diff / 360) * 29.53
        age_days = (lon_diff / 360.0) * 29.53059
        
        return {
            "illumination": float(illumination),
            "phase_name": phase_name,
            "age_days": float(age_days),
            "elongation": float(elongation.deg),
            "earth_distance": float(moon.distance.to(u.km).value)
        }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions the return structure ('Dict with keys "data", "_meta"') which adds some value, but doesn't cover important aspects like whether this is a read-only operation, error handling, rate limits, or authentication needs. For a tool with zero annotation coverage, this is insufficient.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured with a clear purpose statement followed by parameter and return value sections. Every sentence adds value, though the formatting with separate 'Args:' and 'Returns:' sections could be slightly more integrated for better flow.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity, 2 parameters with 0% schema coverage, and the presence of an output schema, the description does a reasonably complete job. It explains what information is returned ('illumination, phase_name, age_days, etc.') and documents parameter formats, though it could benefit from more behavioral context given the lack of annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage and 2 parameters, the description compensates well by specifying the format for both parameters ('Date string "YYYY-MM-DD HH:MM:SS"' and 'IANA timezone string'). This adds meaningful context beyond the bare schema, though it doesn't explain why both parameters are required or provide examples.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('Get detailed information') and resources ('Moon's phase and position'), making it immediately understandable. However, it doesn't explicitly differentiate from sibling tools like 'get_celestial_pos' which might also provide positional data, so it doesn't reach the highest score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'get_celestial_pos' or 'get_constellation'. It lacks context about prerequisites, exclusions, or comparative use cases, leaving the agent to infer usage from the purpose alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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