Skip to main content
Glama

astronomy-mcp-server: get small-body ephemeris

astronomy_get_ephemeris
Read-onlyIdempotent

Fetch a time-series ephemeris for a small body (asteroid or comet) or spacecraft from JPL Horizons — RA/Dec, distance, and apparent magnitude over a span, optionally with observer-relative altitude/azimuth. This covers objects the in-process major-body set cannot. The designation is passed to Horizons verbatim, so it must be in a form Horizons resolves to a single record: a numbered asteroid takes a trailing-semicolon record lookup (e.g. "433;" for Eros, "1;" for Ceres), and a periodic comet takes the DES + closest-apparition form (e.g. "DES=1P;CAP" for Halley). Spacecraft take their negative SPK-ID. Anything else goes through the Horizons name search: a bare name matching nothing (e.g. "433 Eros") or several records (e.g. "1P/Halley") is rejected, but one matching a single object succeeds even when that object is not the one meant — "Eros" resolves to Kerberos, a moon of Pluto — so compare target_name, the object Horizons resolved, with the one intended. A major-body name matching several bodies (e.g. "Mars") is rejected with the ID number of each body it matched — pass one, such as "499" for the planet. start and stop are ISO 8601 UTC dates in the Gregorian calendar, extended before 1582, in years 0100 through 9999; stop must not be before start, and a stop equal to start returns the single row at that instant. step is a count plus a unit of m, h, d, mo, or y, such as "1d", "1h", or "10m". Supplying observer latitude/longitude yields topocentric coordinates and adds alt/az — supply both or neither. This is a gated, network-backed extension (JPL Horizons is keyless but rate-limited and best-effort); large spans truncate inline at 200 rows, and the truncation notice names the exact start to resume from — one step past the last row returned, because Horizons includes the start instant in its output — so re-calling from there continues the series without repeating a sample. Splitting the range into smaller adjacent spans works too; keep the same step either way so no sample is lost.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
stepNoStep size as a positive count up to 2147483647 plus a unit of m (minutes), h (hours), d (days), mo (months), or y (years), e.g. "10m", "1h", "1d". Default "1h".1h
stopNoEphemeris stop as an ISO 8601 UTC string in years 0100 through 9999, at or after start — equal to start returns the single row at that instant. Defaults to 24 hours after start. A value with no zone designator is read as UTC, not the local zone of the server process.
startNoEphemeris start as an ISO 8601 UTC string, e.g. "2024-01-01T00:00:00Z", in years 0100 through 9999 — the calendar years JPL Horizons reads. Dates are Gregorian, extended before 1582. Defaults to now. A value with no zone designator is read as UTC, not the local zone of the server process.
latitudeNoObserver latitude in decimal degrees — supply together with longitude for topocentric coordinates and alt/az. Supplying one without the other is rejected.
elevationNoObserver elevation in meters above sea level. Default 0. With latitude and longitude it must be greater than -20000 and less than 40000000 — the observer altitudes Horizons accepts; a geocentric request ignores it.
longitudeNoObserver longitude in decimal degrees. Supply together with latitude; one without the other is rejected.
designationYesJPL Horizons target, passed verbatim — use a form that resolves to one record. Numbered asteroid: trailing-semicolon record lookup, e.g. "433;" (Eros), "1;" (Ceres). Periodic comet: DES + closest-apparition flag, e.g. "DES=1P;CAP" (Halley), "DES=2P;CAP" (Encke). Spacecraft: negative SPK-ID, e.g. "-48" (Hubble). A bare name like "433 Eros" (no match) or "1P/Halley" (several records) is rejected, and "Mars" (several major bodies) is rejected with their ID numbers listed — the planet is "499" — but one matching a single object succeeds even when it is not the object meant — check `target_name` in the result. Earth ("399") is rejected, being the body the observer is on, and so is a name containing an apostrophe, which the Horizons search cannot match — "6P/d'Arrest" resolves as "DES=6P;CAP" — or any character outside printable ASCII, which Horizons refuses, such as an accent or a typographic apostrophe — "Šteins" resolves as "2867;". Look up designations at ssd.jpl.nasa.gov/tools/sbdb_lookup.html.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
capNoThe inline row cap that was applied.
errorNoPresent when the call failed. Absent on success.
shownNoNumber of ephemeris rows returned.
noticeNoCaveats on the returned series — how to retrieve rows omitted by the cap, and whether any rows were dropped. Absent when the whole span came back intact.
pointsNoTime-series of positions, one per step, in chronological order.
droppedNoRows Horizons returned that carried no usable time, position, or distance and were dropped. Present only when at least one row was dropped, in which case the series has gaps and is shorter than the requested step count.
truncatedNoTrue when the requested span continues past the returned rows; the notice names the start to resume from.
designationNoThe body designation echoed from the request.
target_nameNoThe object JPL Horizons resolved the designation to, from its 'Target body name' header — compare it with the intended object, since a bare name can resolve to a different one. Absent when Horizons' response names no target.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed10 schema fields changed
    • changedInput schema / properties / designation / description
      Previous value: -"JPL Horizons target, passed verbatim — use a form that resolves to one record. Numbered asteroid: trailing-semicolon record lookup, e.g. \"433;\" (Eros), \"1;\" (Ceres). Periodic comet: DES + closest-apparition flag, e.g. \"DES=1P;CAP\" (Halley), \"DES=2P;CAP\" (Encke). Spacecraft: negative SPK-ID, e.g. \"-48\" (Hubble). A bare name like \"433 Eros\" (no match) or \"1P/Halley\" (several records) is rejected, but one matching a single object succeeds even when it is not the object meant — check `target_name` in the result. Look up designations at ssd.jpl.nasa.gov/tools/sbdb_lookup.html."New value: +"JPL Horizons target, passed verbatim — use a form that resolves to one record. Numbered asteroid: trailing-semicolon record lookup, e.g. \"433;\" (Eros), \"1;\" (Ceres). Periodic comet: DES + closest-apparition flag, e.g. \"DES=1P;CAP\" (Halley), \"DES=2P;CAP\" (Encke). Spacecraft: negative SPK-ID, e.g. \"-48\" (Hubble). A bare name like \"433 Eros\" (no match) or \"1P/Halley\" (several records) is rejected, and \"Mars\" (several major bodies) is rejected with their ID numbers listed — the planet is \"499\" — but one matching a single object succeeds even when it is not the object meant — check `target_name` in the result. Earth (\"399\") is rejected, being the body the observer is on, and so is a name containing an apostrophe, which the Horizons search cannot match — \"6P/d'Arrest\" resolves as \"DES=6P;CAP\" — or any character outside printable ASCII, which Horizons refuses, such as an accent or a typographic apostrophe — \"Šteins\" resolves as \"2867;\". Look up designations at ssd.jpl.nasa.gov/tools/sbdb_lookup.html."
    • removedInput schema / properties / designation / minLength
      Removed value: -1
    • addedInput schema / properties / designation / pattern
      Added value: +"\\S"
    • changedInput schema / properties / elevation / description
      Previous value: -"Observer elevation in meters above sea level. Default 0."New value: +"Observer elevation in meters above sea level. Default 0. With latitude and longitude it must be greater than -20000 and less than 40000000 — the observer altitudes Horizons accepts; a geocentric request ignores it."
    • changedInput schema / properties / start / description
      Previous value: -"Ephemeris start as an ISO 8601 UTC string, e.g. \"2024-01-01T00:00:00Z\". Defaults to now. A value with no zone designator is read as UTC, not the local zone of the server process."New value: +"Ephemeris start as an ISO 8601 UTC string, e.g. \"2024-01-01T00:00:00Z\", in years 0100 through 9999 — the calendar years JPL Horizons reads. Dates are Gregorian, extended before 1582. Defaults to now. A value with no zone designator is read as UTC, not the local zone of the server process."
    • changedInput schema / properties / step / description
      Previous value: -"Step size as a positive count plus a unit of m (minutes), h (hours), d (days), mo (months), or y (years), e.g. \"10m\", \"1h\", \"1d\". Default \"1h\"."New value: +"Step size as a positive count up to 2147483647 plus a unit of m (minutes), h (hours), d (days), mo (months), or y (years), e.g. \"10m\", \"1h\", \"1d\". Default \"1h\"."
    • changedInput schema / properties / stop / description
      Previous value: -"Ephemeris stop as an ISO 8601 UTC string, and must be later than start. Defaults to 24 hours after start. A value with no zone designator is read as UTC, not the local zone of the server process."New value: +"Ephemeris stop as an ISO 8601 UTC string in years 0100 through 9999, at or after start — equal to start returns the single row at that instant. Defaults to 24 hours after start. A value with no zone designator is read as UTC, not the local zone of the server process."
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `invalid_time`: The start or stop timestamp is not a parseable ISO 8601 instant, or names a calendar date that does not exist (e.g. 2026-02-30). `invalid_time_range`: The resolved stop instant is at or before the resolved start instant. `incomplete_observer`: Exactly one of latitude and longitude was supplied. `invalid_step`: The step is not a positive count followed by a unit of m, h, d, mo, or y. `time_out_of_range`: The requested span falls outside the target's Horizons data span. The message names the bound Horizons reported, e.g. after A.D. 2599-DEC-31 for Mars. `body_not_found`: JPL Horizons has no match for the designation, or the designation is ambiguous (a bare comet name matches multiple apparition records). `horizons_unavailable`: The JPL Horizons API failed after retries. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `invalid_time`: The start or stop timestamp is not a parseable ISO 8601 instant, names a calendar date that does not exist (e.g. 2026-02-30), or falls outside years 0100 through 9999, the calendar years JPL Horizons reads. `invalid_time_range`: The resolved stop instant is before the resolved start instant. `incomplete_observer`: Exactly one of latitude and longitude was supplied. `invalid_elevation`: An observer (latitude and longitude) was supplied with an elevation of -20000 m or less, or 40000000 m or more — outside the observer altitudes JPL Horizons accepts. `invalid_step`: The step is not a positive count followed by a unit of m, h, d, mo, or y, its count is past 2147483647, or Horizons reports it longer than the span of data it holds for the target. `time_out_of_range`: The requested span falls outside the target's Horizons data span. The message names the bound Horizons reported, e.g. after A.D. 2599-DEC-31 for Mars. `body_not_found`: JPL Horizons has no match for the designation, the designation is ambiguous (a bare comet name matches multiple apparition records), it contains an apostrophe, which the Horizons search cannot match (e.g. \"6P/d'Arrest\"), or it contains a character outside printable ASCII, which Horizons refuses (e.g. \"Šteins\", \"6P/d’Arrest\"). `ambiguous_designation`: A major-body or spacecraft name matches several Horizons bodies (e.g. \"Mars\" matches the planet, its barycenter, and spacecraft). The message lists the candidates with their ID numbers, at most 20, and names the total when there are more. `observer_is_target`: The designation resolves to Earth (e.g. \"399\"), the body every request here observes from. `request_rejected`: JPL Horizons refused a request parameter value with HTTP 400 — the same answer on every attempt. The message names the input that parameter carries. `horizons_unavailable`: The JPL Horizons API failed after retries. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous value: -[
      -  "invalid_time",
      -  "invalid_time_range",
      -  "incomplete_observer",
      -  "invalid_step",
      -  "time_out_of_range",
      -  "body_not_found",
      -  "horizons_unavailable"
      -]New value: +[
      +  "invalid_time",
      +  "invalid_time_range",
      +  "incomplete_observer",
      +  "invalid_elevation",
      +  "invalid_step",
      +  "time_out_of_range",
      +  "body_not_found",
      +  "ambiguous_designation",
      +  "observer_is_target",
      +  "request_rejected",
      +  "horizons_unavailable"
      +]
    • changedOutput schema / properties / truncated / description
      Previous value: -"True when Horizons returned more rows than the inline cap."New value: +"True when the requested span continues past the returned rows; the notice names the start to resume from."
  2. Changed4 schema fields changed
    • changedInput schema / properties / designation / description
      Previous value: -"JPL Horizons target, passed verbatim — use a form that resolves to one record. Numbered asteroid: trailing-semicolon record lookup, e.g. \"433;\" (Eros), \"1;\" (Ceres). Periodic comet: DES + closest-apparition flag, e.g. \"DES=1P;CAP\" (Halley), \"DES=2P;CAP\" (Encke). Spacecraft: negative SPK-ID, e.g. \"-48\" (Hubble). A bare name like \"433 Eros\" or \"1P/Halley\" fails. Look up designations at ssd.jpl.nasa.gov/tools/sbdb_lookup.html."New value: +"JPL Horizons target, passed verbatim — use a form that resolves to one record. Numbered asteroid: trailing-semicolon record lookup, e.g. \"433;\" (Eros), \"1;\" (Ceres). Periodic comet: DES + closest-apparition flag, e.g. \"DES=1P;CAP\" (Halley), \"DES=2P;CAP\" (Encke). Spacecraft: negative SPK-ID, e.g. \"-48\" (Hubble). A bare name like \"433 Eros\" (no match) or \"1P/Halley\" (several records) is rejected, but one matching a single object succeeds even when it is not the object meant — check `target_name` in the result. Look up designations at ssd.jpl.nasa.gov/tools/sbdb_lookup.html."
    • changedOutput schema / properties / error / properties / data / properties / reason / description
      Previous value: -"Machine-readable failure mode. Declared by this tool: `invalid_time`: The start or stop timestamp is not a parseable ISO 8601 instant, or names a calendar date that does not exist (e.g. 2026-02-30). `invalid_time_range`: The resolved stop instant is at or before the resolved start instant. `incomplete_observer`: Exactly one of latitude and longitude was supplied. `invalid_step`: The step is not a positive count followed by a unit of m, h, d, mo, or y. `body_not_found`: JPL Horizons has no match for the designation, or the designation is ambiguous (a bare comet name matches multiple apparition records). `horizons_unavailable`: The JPL Horizons API failed after retries. Other values are possible when a failure originates below the handler."New value: +"Machine-readable failure mode. Declared by this tool: `invalid_time`: The start or stop timestamp is not a parseable ISO 8601 instant, or names a calendar date that does not exist (e.g. 2026-02-30). `invalid_time_range`: The resolved stop instant is at or before the resolved start instant. `incomplete_observer`: Exactly one of latitude and longitude was supplied. `invalid_step`: The step is not a positive count followed by a unit of m, h, d, mo, or y. `time_out_of_range`: The requested span falls outside the target's Horizons data span. The message names the bound Horizons reported, e.g. after A.D. 2599-DEC-31 for Mars. `body_not_found`: JPL Horizons has no match for the designation, or the designation is ambiguous (a bare comet name matches multiple apparition records). `horizons_unavailable`: The JPL Horizons API failed after retries. Other values are possible when a failure originates below the handler."
    • changedOutput schema / properties / error / properties / data / properties / reason / examples
      Previous value: -[
      -  "invalid_time",
      -  "invalid_time_range",
      -  "incomplete_observer",
      -  "invalid_step",
      -  "body_not_found",
      -  "horizons_unavailable"
      -]New value: +[
      +  "invalid_time",
      +  "invalid_time_range",
      +  "incomplete_observer",
      +  "invalid_step",
      +  "time_out_of_range",
      +  "body_not_found",
      +  "horizons_unavailable"
      +]
    • addedOutput schema / properties / target_name
      Added value: +{
      +  "description": "The object JPL Horizons resolved the designation to, from its 'Target body name' header — compare it with the intended object, since a bare name can resolve to a different one. Absent when Horizons' response names no target.",
      +  "type": "string"
      +}
  3. Changed2 schema fields changed
    • removedOutput schema / properties / points / items / properties / magnitude / anyOf
      Removed value: -[
      -  {
      -    "type": "number"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]
    • addedOutput schema / properties / points / items / properties / magnitude / type
      Added value: +[
      +  "number",
      +  "null"
      +]
  4. Changed6 schema fields changed
    • changedInput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedInput schema / additionalProperties
      Added value: +false
    • changedOutput schema / $schema
      Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • addedOutput schema / anyOf
      Added value: +[
      +  {
      +    "not": {
      +      "required": [
      +        "error"
      +      ]
      +    },
      +    "required": [
      +      "designation",
      +      "points",
      +      "truncated",
      +      "shown",
      +      "cap"
      +    ]
      +  },
      +  {
      +    "required": [
      +      "error"
      +    ]
      +  }
      +]
    • addedOutput schema / properties / error
      Added value: +{
      +  "additionalProperties": {},
      +  "description": "Present when the call failed. Absent on success.",
      +  "properties": {
      +    "code": {
      +      "description": "JSON-RPC error code for this failure.",
      +      "maximum": 9007199254740991,
      +      "minimum": -9007199254740991,
      +      "type": "integer"
      +    },
      +    "data": {
      +      "additionalProperties": {},
      +      "properties": {
      +        "reason": {
      +          "description": "Machine-readable failure mode. Declared by this tool: `invalid_time`: The start or stop timestamp is not a parseable ISO 8601 instant, or names a calendar date that does not exist (e.g. 2026-02-30). `invalid_time_range`: The resolved stop instant is at or before the resolved start instant. `incomplete_observer`: Exactly one of latitude and longitude was supplied. `invalid_step`: The step is not a positive count followed by a unit of m, h, d, mo, or y. `body_not_found`: JPL Horizons has no match for the designation, or the designation is ambiguous (a bare comet name matches multiple apparition records). `horizons_unavailable`: The JPL Horizons API failed after retries. Other values are possible when a failure originates below the handler.",
      +          "examples": [
      +            "invalid_time",
      +            "invalid_time_range",
      +            "incomplete_observer",
      +            "invalid_step",
      +            "body_not_found",
      +            "horizons_unavailable"
      +          ],
      +          "type": "string"
      +        },
      +        "recovery": {
      +          "additionalProperties": {},
      +          "description": "Actionable next step for the caller.",
      +          "properties": {
      +            "hint": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "hint"
      +          ],
      +          "type": "object"
      +        },
      +        "retryable": {
      +          "description": "Whether retrying may succeed.",
      +          "type": "boolean"
      +        }
      +      },
      +      "type": "object"
      +    },
      +    "message": {
      +      "description": "Human-readable description of what went wrong.",
      +      "type": "string"
      +    }
      +  },
      +  "required": [
      +    "code",
      +    "message"
      +  ],
      +  "type": "object"
      +}
    • removedOutput schema / required
      Removed value: -[
      -  "designation",
      -  "points",
      -  "truncated",
      -  "shown",
      -  "cap"
      -]
  5. Changed2 schema fields changed
    • changedInput schema / properties / start / description
      Previous value: -"Ephemeris start as an ISO 8601 UTC string, e.g. \"2024-01-01T00:00:00Z\". Defaults to now."New value: +"Ephemeris start as an ISO 8601 UTC string, e.g. \"2024-01-01T00:00:00Z\". Defaults to now. A value with no zone designator is read as UTC, not the local zone of the server process."
    • changedInput schema / properties / stop / description
      Previous value: -"Ephemeris stop as an ISO 8601 UTC string, and must be later than start. Defaults to 24 hours after start."New value: +"Ephemeris stop as an ISO 8601 UTC string, and must be later than start. Defaults to 24 hours after start. A value with no zone designator is read as UTC, not the local zone of the server process."
  6. First observed

TDQS

A4.6/5.0
Behavior5/5

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

Annotations only cover safety (readOnly/openWorld/idempotent), and the description goes well beyond: it discloses the gated, network-backed, keyless-but-rate-limited best-effort nature of Horizons, the 200-row inline truncation and the exact resume semantics (one step past the last row), the both-or-neither rule for observer coordinates, and the subtle name-resolution failure mode where a single unintended match (e.g. 'Eros' → Kerberos) silently succeeds.

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 long but front-loaded: purpose and scope come first, then designation grammar, then temporal parameters, then truncation behavior. Nearly every sentence carries operational value, though the designation discussion is dense enough to border on over-length for a single tool definition.

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

Completeness5/5

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

An output schema exists, so return values need not be explained, and the description still supplies the critical return-side context (compare `target_name`, truncation notice names the resume `start`). For a 7-parameter, network-backed, gated tool with tricky identifier resolution, this is complete enough that an agent could call it correctly on the first attempt.

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?

Schema coverage is already 100%, so the baseline is 3. The description nonetheless adds semantics the schema cannot express: the registry-vs-name-search resolution paths for `designation`, the rejection rules for Earth/apostrophes/non-ASCII, comparing `target_name` against intent, and the truncation/resume behavior tied to `start`/`step`. It mostly reinforces the schema rather than introducing new field-level detail, so 4 rather than 5.

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

Purpose5/5

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

The opening sentence names a specific verb (Fetch), resource (time-series ephemeris), the object classes it covers (small body or spacecraft), the source (JPL Horizons), and the returned quantities (RA/Dec, distance, apparent magnitude, optional alt/az). It also explicitly carves out its niche by noting it 'covers objects the in-process major-body set cannot', distinguishing it from sky-position/rise-set siblings.

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

Usage Guidelines4/5

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

It clearly delimits scope — small bodies and spacecraft that the major-body set cannot handle — which implies when to reach for it over the major-body siblings. However, it never names an alternative tool (e.g. astronomy_get_sky_position) or states a positive 'use this when' for a specific user scenario, so differentiation is contextual rather than explicit.

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

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.