About astronomy-mcp-server
astronomy-mcp-server provides offline observational astronomy calculations for MCP clients, including sky positions, rise and set times, phases, and events. Optional extensions add JPL Horizons ephemerides and satellite passes.
Key features
- 01Positions for solar-system bodies and catalog stars
- 02Rise, set, transit, and twilight times
- 03Moon phase and upcoming astronomical events
- 04Ranked list of currently visible objects
- 05Optional Horizons ephemerides and satellite passes
Use cases
- 01Check a planet's position for an observation
- 02Find twilight and rise/set times for a location
- 03Plan a stargazing session or inspect satellite passes
MCP support
It exposes seven tools, one body-reference resource, and a stargazing-plan prompt to MCP clients. It supports STDIO and Streamable HTTP transports.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://astronomy.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-astronomy https://astronomy.caseyjhand.com/mcp{"mcpServers":{"cyanheads-astronomy":{"type":"http","url":"https://astronomy.caseyjhand.com/mcp"}}}Open in Cursor{"servers":{"cyanheads-astronomy":{"type":"http","url":"https://astronomy.caseyjhand.com/mcp"}}}Open in VS Code{"mcpServers":{"cyanheads-astronomy":{"type":"http","url":"https://astronomy.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-astronomy":{"serverUrl":"https://astronomy.caseyjhand.com/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
Tools
7| Parameter | Type | Required | Description |
|---|---|---|---|
| body | enum | No | Solar-system body to locate. Omit when targeting a named star via `star`.sun | moon | mercury | venus | mars | jupiter | saturn | uranus | neptune | pluto |
| star | string | No | Named star to locate, from a bundled catalog of 32 stars — about 30 of the brightest naked-eye stars plus Polaris, not an arbitrary star. Accepts a common name or Bayer designation, with the Greek letter spelled out or as a symbol and the constellation as its genitive or IAU abbreviation (e.g. "Sirius", "Alpha Centauri", "α CMa"). A name outside the catalog fails with the full list of catalog stars. Takes precedence over `body`. |
| time | string | No | Instant of observation as an ISO 8601 UTC string, e.g. "2024-04-08T18:00:00Z". Defaults to now. A value with no zone designator is read as UTC, not the local zone of the server process. |
| latitude | number | Yes | Observer latitude in decimal degrees, north positive. |
| timezone | string | No | IANA timezone for localized output, e.g. "America/Los_Angeles". When omitted, output is UTC-only. |
| elevation | number | No | Observer elevation in meters above sea level. Default 0. |
| longitude | number | Yes | Observer longitude in decimal degrees, east positive. |
Details
From the project README@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.
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
| Tool | Description |
|---|---|
astronomy_get_sky_position | Apparent position of one body or named star: RA/Dec, alt/az, ecliptic, magnitude, phase, Sun elongation, constellation |
astronomy_get_rise_set | Rise, set, and transit times for a body, plus civil, nautical, and astronomical twilight for the Sun |
astronomy_get_moon_phase | Illuminated fraction, phase name, synodic age, and the next four quarter phases |
astronomy_find_events | Next eclipses, equinoxes, solstices, moon quarters, oppositions, conjunctions, greatest elongations, or apsides |
astronomy_list_visible | What's up now: naked-eye bodies (and optional bright stars) above the horizon, ranked, with a sky-condition gate |
astronomy_get_ephemeris | Small-body or spacecraft ephemeris from JPL Horizons (opt-in) |
astronomy_get_satellite_passes | Visible satellite passes from a CelesTrak element set propagated with SGP4 (opt-in) |
Resources
| Resource | Description |
|---|---|
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
| Prompt | Description |
|---|---|
astronomy_stargazing_plan | Plan 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) orstaris required, withstarwinning when both are set;starresolves against a bundled 32-star catalog by common name or Bayer designation (Sirius,α CMa), and a miss fails asstar_not_foundlisting the catalog- Returns
equatorial, refraction-correctedhorizontal, andeclipticcoordinates plusmagnitude,angular_diameter_arcsec,phase_angle_degrees,illuminated_fraction,sun_elongation_degrees, andconstellation - Solar-system bodies also carry
body_metadata, the resource card'stype,mean_radius_km, andnaked_eye; a star has none
astronomy_get_rise_set tool
- One
bodysearched forward fromstart(default now) for the nextcountcycles, default 1; 31 are served per call, and acountup to 366 returns the first 31 with a notice naming thestartandcountthat continue the series - Each cycle carries
rise_utc,set_utc,transit_utc, andtransit_altitude_degrees; a circumpolar or never-rising body getsnullrise/set and anoteinstead of an error - For
body: "sun",twilightholds civil, nautical, and astronomical dusk/dawn pairs for the night after each cycle's day. When the first day begins afterstart,night_in_progressholds 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 beforestartremain included
astronomy_get_moon_phase tool
- Geocentric: takes only
time(default now) and an optionaltimezone - Returns
illuminated_fraction,phase_name,age_days,next_quarters, andphase_longitude_degrees(0 new, 180 full), which is not thephase_angle_degreesofastronomy_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 nextcountoccurrences, default 1; 20 are served per call, and acountup to 200 returns the first 20 with a notice naming thestartandcountthat 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 asbody_not_supported, andconjunctionwithbody2takes the Moon or a planet different frombody(invalid_body_pairotherwise) and reportsseparation_degrees - Eclipses take an optional observer (
latitudeandlongitudetogether, elseincomplete_observer) that addslocal_visibleandcontact_altitudes_degrees - Eclipses are included when their peak is at or after
start; their contacts may precedestart
astronomy_list_visible tool
- Observer coordinates and one evaluation instant,
time(default now, not a window);min_altitude(default 0) andinclude_stars(default false) shape the list - Returns
sky_condition(daylight,civil_twilight,nautical_twilight,astronomical_twilight,dark),sun_altitude_degrees, andbodiesranked brightest-and-highest first, each with avisibility_notethat says when daylight, civil twilight, or Sun glare within 15° hides or dims it
astronomy_get_ephemeris tool
designationis passed to Horizons verbatim ("433;"for a numbered asteroid,"DES=1P;CAP"for a periodic comet, a negative SPK-ID for a spacecraft);startdefaults to now,stopto 24 h later, andstepto"1h"(a count plusm,h,d,mo, ory);latitudeandlongitudetogether add alt/az- Up to 200 rows per call; when
truncatedis true the notice names thestartto resume from.target_nameis the object Horizons resolved - Fails as
ambiguous_designation,observer_is_target,body_not_found,invalid_step,invalid_elevation,time_out_of_range, orrequest_rejected, each naming the input to correct - Registered only when
ASTRONOMY_ENABLE_HORIZONS=true
astronomy_get_satellite_passes tool
- Exactly one of
norad_idorname(ISS, Hubble/HST, and Tiangong/CSS resolve directly; other names are matched against CelesTrak, and an ambiguous one fails asambiguous_satellite_namelisting candidates);startmust be within about a month of today, anddaysdefaults to 7 - 10 days are searched per call; a
daysup to 30 returns the first 10 with a notice naming thestartanddaysthat continue the search - Returns
norad_id,satellite_name, andpassesthat are visible (sunlit, above 10° elevation, Sun below −6°), each with rise/peak/set times and azimuths,peak_altitude_degrees,duration_seconds,sunlit, andvisible_start_utc/visible_end_utc - Registered only when
ASTRONOMY_ENABLE_SATELLITES=true
astronomy://body/{body} resource
{body}is one ofsun,moon,mercury…pluto, with name completion; anything else fails asunknown_bodyapplication/jsoncard withbody,name,type(star/planet/moon/dwarf),mean_radius_km, andnaked_eye
astronomy_stargazing_plan prompt
- Arguments:
locationrequired;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, andastronomy_list_visiblewithinclude_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 setASTRONOMY_DEFAULT_TIMEZONE) to get*_localfields 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 (
elevationdefaults to 0 m) - The two opt-in extensions each carry their own timeout and retry; an upstream failure surfaces as
horizons_unavailableorcelestrak_unavailableand never falls back to core output
Agent-friendly output:
- Uncertainty preserved:
magnitude,angular_diameter_arcsec,phase_angle_degrees, andilluminated_fractionarenullwhere the engine can't compute them, rendered as "unavailable", never zeroed - Deterministic visibility notes:
visibility_notecomes from real magnitude, altitude, sky condition, and Sun elongation, not a synthetic score - Typed error contracts: each declared failure carries a typed
reasonand 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 acontent[]-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:
claude mcp add --transport http astronomy-mcp-server https://astronomy.caseyjhand.com/mcp
For Cline (cline_mcp_settings.json):
{
"mcpServers": {
"astronomy-mcp-server": {
"type": "streamableHttp",
"url": "https://astronomy.caseyjhand.com/mcp"
}
}
}
For Cursor (mcp.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.
{
"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):
{
"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:
{
"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:
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
- Clone the repository:
git clone https://github.com/cyanheads/astronomy-mcp-server.git
- Navigate into the directory:
cd astronomy-mcp-server
- Install dependencies:
bun install
- Configure environment (optional):
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.
| Variable | Description | Default |
|---|---|---|
ASTRONOMY_ENABLE_HORIZONS | Register astronomy_get_ephemeris (JPL Horizons). | false |
ASTRONOMY_ENABLE_SATELLITES | Register astronomy_get_satellite_passes (CelesTrak + SGP4). | false |
ASTRONOMY_DEFAULT_TIMEZONE | Fallback IANA timezone when a call omits timezone. Unset means UTC-only output. | none |
ASTRONOMY_REQUEST_TIMEOUT_MS | HTTP timeout for Horizons and CelesTrak requests, in ms. | 15000 |
ASTRONOMY_TLE_CACHE_TTL_MS | In-process element-set cache TTL, in ms. The 2 h default follows CelesTrak's refetch guidance. | 7200000 |
ASTRONOMY_HORIZONS_BASE_URL | JPL Horizons API endpoint. | https://ssd.jpl.nasa.gov/api/horizons.api |
ASTRONOMY_CELESTRAK_BASE_URL | CelesTrak GP endpoint. | https://celestrak.org/NORAD/elements/gp.php |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
-
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
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
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools/resources/prompts and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Five core tools plus two opt-in extensions. |
src/mcp-server/resources | Resource definitions (*.resource.ts). Body reference card. |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts). Stargazing plan. |
src/services/ephemeris | Offline compute core — astronomy-engine wrapper, body table, bright-star catalog. |
src/services/horizons | JPL Horizons HTTP client (extension). |
src/services/satellite | CelesTrak 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/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools and resources in the
createApp()arrays insrc/index.ts - Normalize raw engine/API values to the domain type; preserve uncertainty as
nulland never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.