Skip to main content
Glama

LocalTides MCP Server

moon-phase-service.ts5.9 kB
import SunCalc from 'suncalc'; import { MoonPhaseParams, MoonPhasesRangeParams, NextMoonPhaseParams } from '../interfaces/moon.js'; import { MoonPhaseName, MoonPhaseInfo } from '../types/moon.js'; /** * Service for moon phase calculations */ export class MoonPhaseService { /** * Get the moon phase for a specific date * @param params Parameters for the request * @returns Moon phase information */ getMoonPhase(params: MoonPhaseParams): MoonPhaseInfo { const date = params.date ? new Date(params.date) : new Date(); // Get moon illumination data const illuminationData = SunCalc.getMoonIllumination(date); // Get moon position data (requires location) const latitude = params.latitude ?? 0; const longitude = params.longitude ?? 0; const positionData = SunCalc.getMoonPosition(date, latitude, longitude); // Calculate moon phase name const phaseName = this.getMoonPhaseName(illuminationData.phase); // Calculate if the moon is waxing (increasing illumination) const isWaxing = illuminationData.phase < 0.5; // Calculate approximate moon age (0-29.53 days) const lunarMonth = 29.53; // days const age = illuminationData.phase * lunarMonth; // Calculate apparent diameter (in degrees) const diameter = 0.5181 * (384400 / positionData.distance); return { date: date.toISOString().split('T')[0], phase: illuminationData.phase, phaseName, illumination: illuminationData.fraction, age, distance: positionData.distance, diameter, isWaxing }; } /** * Get moon phases for a date range * @param params Parameters for the request * @returns Array of moon phase information */ getMoonPhasesRange(params: MoonPhasesRangeParams): MoonPhaseInfo[] { const startDate = new Date(params.start_date); const endDate = new Date(params.end_date); if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) { throw new Error('Invalid date format. Please use YYYY-MM-DD format.'); } if (startDate > endDate) { throw new Error('Start date must be before end date.'); } const result: MoonPhaseInfo[] = []; const currentDate = new Date(startDate); while (currentDate <= endDate) { result.push(this.getMoonPhase({ date: currentDate.toISOString().split('T')[0], latitude: params.latitude, longitude: params.longitude })); // Move to next day currentDate.setDate(currentDate.getDate() + 1); } return result; } /** * Get the next occurrence(s) of a specific moon phase * @param params Parameters for the request * @returns Array of dates for the next occurrences of the specified phase */ getNextMoonPhase(params: NextMoonPhaseParams): { date: string, phase: string }[] { const startDate = params.date ? new Date(params.date) : new Date(); const count = params.count || 1; const targetPhase = params.phase; // Map phase names to their approximate values const phaseValues: Record<string, number> = { [MoonPhaseName.NEW_MOON]: 0, [MoonPhaseName.FIRST_QUARTER]: 0.25, [MoonPhaseName.FULL_MOON]: 0.5, [MoonPhaseName.LAST_QUARTER]: 0.75 }; const targetPhaseValue = phaseValues[targetPhase]; const results: { date: string, phase: string }[] = []; let currentDate = new Date(startDate); // Find the next occurrences while (results.length < count) { // Check every day (could be optimized with better algorithms) const illuminationData = SunCalc.getMoonIllumination(currentDate); const prevDate = new Date(currentDate); prevDate.setDate(prevDate.getDate() - 1); const prevIlluminationData = SunCalc.getMoonIllumination(prevDate); // Check if we've passed the target phase between yesterday and today const prevDiff = Math.abs(prevIlluminationData.phase - targetPhaseValue); const currentDiff = Math.abs(illuminationData.phase - targetPhaseValue); // If we're getting closer to the target phase and then further away, we've passed it // Or if we're very close to the target phase (within 0.01) if ((prevDiff > currentDiff && currentDiff < 0.01) || currentDiff < 0.005) { results.push({ date: currentDate.toISOString().split('T')[0], phase: targetPhase }); } // Move to next day currentDate.setDate(currentDate.getDate() + 1); // Safety check to prevent infinite loops if (results.length === 0 && currentDate.getTime() - startDate.getTime() > 366 * 24 * 60 * 60 * 1000) { throw new Error('Could not find the specified moon phase within a year.'); } } return results; } /** * Get the moon phase name based on the phase value (0-1) * @param phase Phase value (0-1) * @returns Moon phase name */ private getMoonPhaseName(phase: number): MoonPhaseName { // Normalize phase to 0-1 range const normalizedPhase = phase < 0 ? phase + 1 : phase > 1 ? phase - 1 : phase; // Determine moon phase based on the value if (normalizedPhase < 0.0625 || normalizedPhase >= 0.9375) { return MoonPhaseName.NEW_MOON; } else if (normalizedPhase < 0.1875) { return MoonPhaseName.WAXING_CRESCENT; } else if (normalizedPhase < 0.3125) { return MoonPhaseName.FIRST_QUARTER; } else if (normalizedPhase < 0.4375) { return MoonPhaseName.WAXING_GIBBOUS; } else if (normalizedPhase < 0.5625) { return MoonPhaseName.FULL_MOON; } else if (normalizedPhase < 0.6875) { return MoonPhaseName.WANING_GIBBOUS; } else if (normalizedPhase < 0.8125) { return MoonPhaseName.LAST_QUARTER; } else { return MoonPhaseName.WANING_CRESCENT; } } }

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/RyanCardin15/NOAA-TidesAndCurrents-MCP'

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