MCP server: One MCP server.

astronomy-mcp-server

@cyanheadsRun by its publisher: served from their own domain, namespace checked by the registry.MCP server · External

Offline observational astronomy: positions, rise/set, moon phases, eclipses, and seasons.

Space & astronomyv0.4.3Apache-2.0TypeScript
Freeno charge per call

One address for any MCP client. No sign-in needed on this server.

What it does

7 read

Grouped by tool name. At most: only reads or looks up information.

What it costs

Free

Odel charges nothing per call. The publisher may charge for its own service.

Tools

7 · grouped by tool name · checked 22 Oct 2026

Read · 7Look things up

  • astronomy_get_sky_position
  • astronomy_get_rise_set
  • astronomy_get_moon_phase
  • astronomy_find_events
  • astronomy_list_visible
  • astronomy_get_ephemeris
  • astronomy_get_satellite_passes
Try it in the Inspector
README11 min read

@cyanheads/astronomy-mcp-server

What's in the sky, computed offline — planet and moon positions, rise/set, phases, eclipses, and seasons for any place and time via MCP. STDIO or Streamable HTTP.

7 Tools • 1 Resource • 1 Prompt

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

Public Hosted Server: https://astronomy.caseyjhand.com/mcp


Overview

Observational astronomy for any place and time: sky positions, rise/set and twilight times, moon phases, and the next eclipse, equinox, opposition, or conjunction — or just ask what's visible right now. Computed locally by astronomy-engine, with optional JPL Horizons ephemerides and satellite passes.

Tools

ToolDescription
astronomy_get_sky_positionApparent position of one body or named star: RA/Dec, alt/az, ecliptic, magnitude, phase, Sun elongation, constellation
astronomy_get_rise_setRise, set, and transit times for a body, plus civil, nautical, and astronomical twilight for the Sun
astronomy_get_moon_phaseIlluminated fraction, phase name, synodic age, and the next four quarter phases
astronomy_find_eventsNext eclipses, equinoxes, solstices, moon quarters, oppositions, conjunctions, greatest elongations, or apsides
astronomy_list_visibleWhat's up now: naked-eye bodies (and optional bright stars) above the horizon, ranked, with a sky-condition gate
astronomy_get_ephemerisSmall-body or spacecraft ephemeris from JPL Horizons (opt-in)
astronomy_get_satellite_passesVisible satellite passes from a CelesTrak element set propagated with SGP4 (opt-in)

Resources

ResourceDescription
astronomy://body/{body}Reference card for a solar-system body: name, type, mean radius, naked-eye visibility

Tool-only clients get the same type, radius, and naked-eye fields inline from astronomy_get_sky_position.

Prompts

PromptDescription
astronomy_stargazing_planPlan a night of stargazing from a place, chaining the astronomy tools with geocoding and weather steps

Design reference: docs/design.md.

Capability reference

astronomy_get_sky_position tool

  • body (sun, moon, mercury … pluto) or star is required, with star winning when both are set; star resolves against a bundled 32-star catalog by common name or Bayer designation (Sirius, α CMa), and a miss fails as star_not_found listing the catalog
  • Returns equatorial, refraction-corrected horizontal, and ecliptic coordinates plus magnitude, angular_diameter_arcsec, phase_angle_degrees, illuminated_fraction, sun_elongation_degrees, and constellation
  • Solar-system bodies also carry body_metadata, the resource card's type, mean_radius_km, and naked_eye; a star has none

astronomy_get_rise_set tool

  • One body searched forward from start (default now) for the next count cycles, default 1; 31 are served per call, and a count up to 366 returns the first 31 with a notice naming the start and count that continue the series
  • Each cycle carries rise_utc, set_utc, transit_utc, and transit_altitude_degrees; a circumpolar or never-rising body gets null rise/set and a note instead of an error
  • For body: "sun", twilight holds civil, nautical, and astronomical dusk/dawn pairs for the night after each cycle's day. When the first day begins after start, night_in_progress holds the preceding night, including polar nights without a sunset; the first cycle otherwise carries its own night. Each pair stays within that night, and times before start remain included

astronomy_get_moon_phase tool

  • Geocentric: takes only time (default now) and an optional timezone
  • Returns illuminated_fraction, phase_name, age_days, next_quarters, and phase_longitude_degrees (0 new, 180 full), which is not the phase_angle_degrees of astronomy_get_sky_position

astronomy_find_events tool

  • One event (solar_eclipse, lunar_eclipse, equinox, solstice, moon_quarter, opposition, conjunction, max_elongation, perigee_apogee) and the next count occurrences, default 1; 20 are served per call, and a count up to 200 returns the first 20 with a notice naming the start and count that continue the series; searches stop at the end of 2100
  • Body-relative events require body (body_required); a body the event doesn't apply to fails as body_not_supported, and conjunction with body2 takes the Moon or a planet different from body (invalid_body_pair otherwise) and reports separation_degrees
  • Eclipses take an optional observer (latitude and longitude together, else incomplete_observer) that adds local_visible and contact_altitudes_degrees
  • Eclipses are included when their peak is at or after start; their contacts may precede start

astronomy_list_visible tool

  • Observer coordinates and one evaluation instant, time (default now, not a window); min_altitude (default 0) and include_stars (default false) shape the list
  • Returns sky_condition (daylight, civil_twilight, nautical_twilight, astronomical_twilight, dark), sun_altitude_degrees, and bodies ranked brightest-and-highest first, each with a visibility_note that says when daylight, civil twilight, or Sun glare within 15° hides or dims it

astronomy_get_ephemeris tool

  • designation is passed to Horizons verbatim ("433;" for a numbered asteroid, "DES=1P;CAP" for a periodic comet, a negative SPK-ID for a spacecraft); start defaults to now, stop to 24 h later, and step to "1h" (a count plus m, h, d, mo, or y); latitude and longitude together add alt/az
  • Up to 200 rows per call; when truncated is true the notice names the start to resume from. target_name is the object Horizons resolved
  • Fails as ambiguous_designation, observer_is_target, body_not_found, invalid_step, invalid_elevation, time_out_of_range, or request_rejected, each naming the input to correct
  • Registered only when ASTRONOMY_ENABLE_HORIZONS=true

astronomy_get_satellite_passes tool

  • Exactly one of norad_id or name (ISS, Hubble/HST, and Tiangong/CSS resolve directly; other names are matched against CelesTrak, and an ambiguous one fails as ambiguous_satellite_name listing candidates); start must be within about a month of today, and days defaults to 7
  • 10 days are searched per call; a days up to 30 returns the first 10 with a notice naming the start and days that continue the search
  • Returns norad_id, satellite_name, and passes that are visible (sunlit, above 10° elevation, Sun below −6°), each with rise/peak/set times and azimuths, peak_altitude_degrees, duration_seconds, sunlit, and visible_start_utc/visible_end_utc
  • Registered only when ASTRONOMY_ENABLE_SATELLITES=true

astronomy://body/{body} resource

  • {body} is one of sun, moon, mercury … pluto, with name completion; anything else fails as unknown_body
  • application/json card with body, name, type (star/planet/moon/dwarf), mean_radius_km, and naked_eye

astronomy_stargazing_plan prompt

  • Arguments: location required; date (YYYY-MM-DD) optional, defaults to tonight
  • Returns one user message that chains astronomy_get_rise_set (sun, then moon), astronomy_get_moon_phase, and astronomy_list_visible with include_stars, plus the geocoding and weather steps this server doesn't cover

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

Astronomy-specific:

  • Keyless, offline, deterministic core: the five core tools need no network, API key, or rate limit, and return the same answer for the same body, time, and observer
  • Times outside the engine's high-accuracy span (≈1900–2100) fail as time_out_of_range; the Horizons tool is bounded by each target's own data span instead
  • Times are ISO 8601 UTC, and a value with no zone is read as UTC; pass an IANA timezone (or set ASTRONOMY_DEFAULT_TIMEZONE) to get *_local fields alongside UTC. The server never infers a timezone from coordinates
  • No geocoding: resolve a place name to latitude/longitude and a timezone upstream, then pass the coordinates (elevation defaults to 0 m)
  • The two opt-in extensions each carry their own timeout and retry; an upstream failure surfaces as horizons_unavailable or celestrak_unavailable and never falls back to core output

Agent-friendly output:

  • Uncertainty preserved: magnitude, angular_diameter_arcsec, phase_angle_degrees, and illuminated_fraction are null where the engine can't compute them, rendered as "unavailable", never zeroed
  • Deterministic visibility notes: visibility_note comes from real magnitude, altitude, sky condition, and Sun elongation, not a synthetic score
  • Typed error contracts: each declared failure carries a typed reason and a recovery hint, so callers can correct and retry
  • Exact values behind rounded text: format() pairs each rounded figure with its exact value in brackets (RA 4.4116 h [4.411597993526305]), so a content[]-only client loses no precision

Getting started

Public Hosted Instance

A public instance is available at https://astronomy.caseyjhand.com/mcp — no installation required. It serves Streamable HTTP; each client takes its own config shape.

For Claude Code:

sh
claude mcp add --transport http astronomy-mcp-server https://astronomy.caseyjhand.com/mcp

For Cline (cline_mcp_settings.json):

json
{
  "mcpServers": {
    "astronomy-mcp-server": {
      "type": "streamableHttp",
      "url": "https://astronomy.caseyjhand.com/mcp"
    }
  }
}

For Cursor (mcp.json):

json
{
  "mcpServers": {
    "astronomy-mcp-server": {
      "url": "https://astronomy.caseyjhand.com/mcp"
    }
  }
}

Claude Desktop adds remote servers under Settings → Connectors.

Self-Hosted / Local

Add the following to your MCP client configuration file. The core tools need no configuration; set ASTRONOMY_ENABLE_HORIZONS and/or ASTRONOMY_ENABLE_SATELLITES to true to register the extensions.

json
{
  "mcpServers": {
    "astronomy-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/astronomy-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

json
{
  "mcpServers": {
    "astronomy-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/astronomy-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

json
{
  "mcpServers": {
    "astronomy-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/astronomy-mcp-server:latest"]
    }
  }
}

For Streamable HTTP, set the transport and start the server:

sh
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp

Prerequisites

  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key. The core is offline, and both extensions (JPL Horizons, CelesTrak) are keyless.

Installation

  1. Clone the repository:
sh
git clone https://github.com/cyanheads/astronomy-mcp-server.git
  1. Navigate into the directory:
sh
cd astronomy-mcp-server
  1. Install dependencies:
sh
bun install
  1. Configure environment (optional):
sh
cp .env.example .env
# edit .env only to enable the extensions or override a default

Configuration

Every variable is optional; the core runs with none set.

VariableDescriptionDefault
ASTRONOMY_ENABLE_HORIZONSRegister astronomy_get_ephemeris (JPL Horizons).false
ASTRONOMY_ENABLE_SATELLITESRegister astronomy_get_satellite_passes (CelesTrak + SGP4).false
ASTRONOMY_DEFAULT_TIMEZONEFallback IANA timezone when a call omits timezone. Unset means UTC-only output.none
ASTRONOMY_REQUEST_TIMEOUT_MSHTTP timeout for Horizons and CelesTrak requests, in ms.15000
ASTRONOMY_TLE_CACHE_TTL_MSIn-process element-set cache TTL, in ms. The 2 h default follows CelesTrak's refetch guidance.7200000
ASTRONOMY_HORIZONS_BASE_URLJPL Horizons API endpoint.https://ssd.jpl.nasa.gov/api/horizons.api
ASTRONOMY_CELESTRAK_BASE_URLCelesTrak GP endpoint.https://celestrak.org/NORAD/elements/gp.php
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto.stateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
OTEL_ENABLEDEnable OpenTelemetry.false

See .env.example for the full list of optional overrides.

Running the server

Local development

  • Build and run:

    sh
    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
    
  • Run checks and tests:

    sh
    bun run devcheck   # Lint, format, typecheck, security
    bun run test       # Vitest test suite
    bun run lint:mcp   # Validate MCP definitions against spec
    

Docker

sh
docker build -t astronomy-mcp-server .
docker run --rm -p 3010:3010 astronomy-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/astronomy-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers tools/resources/prompts and inits services.
src/configServer-specific environment variable parsing and validation with Zod.
src/mcp-server/toolsTool definitions (*.tool.ts). Five core tools plus two opt-in extensions.
src/mcp-server/resourcesResource definitions (*.resource.ts). Body reference card.
src/mcp-server/promptsPrompt definitions (*.prompt.ts). Stargazing plan.
src/services/ephemerisOffline compute core — astronomy-engine wrapper, body table, bright-star catalog.
src/services/horizonsJPL Horizons HTTP client (extension).
src/services/satelliteCelesTrak element-set fetch and SGP4 propagation (extension).
tests/Unit and integration tests mirroring src/.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools and resources in the createApp() arrays in src/index.ts
  • Normalize raw engine/API values to the domain type; preserve uncertainty as null and never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.

Similar modules

Browse Space & astronomy