Tool reference¶
Every tool the server registers, with the input and output JSON schemas
the MCP wire actually carries. The catalogue below is generated at build
time from the live FastMCP registry — what you see is what an MCP
client receives when it issues tools/list against astrodynamics-mcp.
How to read this page¶
Each tool section carries:
- Description. The string the LLM sees in its tool catalogue. Tuned per the eval suite so the model picks the right tool and binds the right arguments under prompt variation.
- Input schema. The JSON-Schema the SDK validates every call
against. The same schema is emitted by
pydantic's.model_json_schema()for the tool's argument model; fielddescriptionandexamplesare visible to the LLM via the SDK. - Output schema. The JSON-Schema for the tool's response body. Every
numeric field follows the
{value, unit}discipline described under Data sources — there are no barekmorkm/sfloats on the wire.
For the underlying Python types and the docstrings, see the API reference.
Tools¶
access_windows¶
Compute ground-station / observer access intervals for a TLE-defined satellite over a UTC time window. e.g. access_windows(observer={'name': 'madrid'}, target_tle={'line1': '...', 'line2': '...'}, start='2024-01-01T00:00:00Z', end='2024-01-02T00:00:00Z', min_elevation_deg=10) returns the satellite's passes above 10° as seen from Madrid. observer is either a named-station dict ({'name': 'madrid' | 'goldstone' | 'canberra' | 'svalbard' | 'wallops' | 'esrange' | 'gsfc' | 'jpl'}) or explicit geodetic coordinates ({lat, lon, alt}). Epochs are UTC ISO 8601 with a mandatory time component ('2024-01-01T00:00:00Z'). min_elevation_deg is in degrees, not radians; below 5° passes are usually operationally useless thanks to horizon refraction and terrain masking — 10° is the standard amateur threshold, 15° for DSN-style large-dish operations. Optional min_range_km / max_range_km filter on the satellite's range at peak elevation (closest approach during the pass). Frame is implicit: ECEF observer + TEME-derived satellite position, transformed internally via skyfield.
Input schema:
{
"$defs": {
"NamedStation": {
"additionalProperties": false,
"description": "An observer identified by a name from the v0.1 station registry.",
"properties": {
"name": {
"description": "Short name of a known ground station. Resolved to lat/lon/alt inside the consuming tool. The v0.1 registry is intentionally small; pass explicit ObserverCoordinates for anything else.",
"enum": [
"madrid",
"goldstone",
"canberra",
"svalbard",
"wallops",
"esrange",
"gsfc",
"jpl"
],
"examples": [
"madrid",
"goldstone"
],
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "NamedStation",
"type": "object"
},
"ObserverCoordinates": {
"additionalProperties": false,
"description": "An observer specified by explicit geodetic coordinates.",
"properties": {
"lat": {
"$ref": "#/$defs/Quantity",
"description": "Geodetic latitude in degrees.",
"examples": [
{
"unit": "deg",
"value": 40.4168
}
]
},
"lon": {
"$ref": "#/$defs/Quantity",
"description": "Geodetic longitude in degrees (east-positive).",
"examples": [
{
"unit": "deg",
"value": -3.7038
}
]
},
"alt": {
"$ref": "#/$defs/Quantity",
"description": "Altitude above the WGS-84 ellipsoid in km.",
"examples": [
{
"unit": "km",
"value": 0.667
}
]
}
},
"required": [
"lat",
"lon",
"alt"
],
"title": "ObserverCoordinates",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"TleLines": {
"additionalProperties": false,
"description": "Two-line TLE as raw strings.\n\nBoth lines must be exactly 69 characters (the canonical TLE width).\nSub-69-character lines are the most common LLM mistake \u2014 they almost\nalways come from a chat client stripping trailing whitespace.",
"properties": {
"line1": {
"description": "First TLE line (begins with '1 '), exactly 69 characters.",
"examples": [
"1 25544U 98067A 24001.50000000 .00010000 00000-0 18000-3 0 9990"
],
"title": "Line1",
"type": "string"
},
"line2": {
"description": "Second TLE line (begins with '2 '), exactly 69 characters.",
"examples": [
"2 25544 51.6400 90.0000 0001000 90.0000 270.0000 15.50000000000000"
],
"title": "Line2",
"type": "string"
}
},
"required": [
"line1",
"line2"
],
"title": "TleLines",
"type": "object"
},
"TleOmm": {
"additionalProperties": false,
"description": "Parsed OMM (Orbit Mean Elements Message) JSON.\n\nSchema is intentionally loose at v0.1: we accept whatever the upstream\nOMM source (CelesTrak) emitted. Downstream tools that need specific\nfields raise typed errors when those fields are missing.",
"properties": {
"omm": {
"additionalProperties": true,
"description": "Parsed OMM JSON object (CCSDS standard fields like CCSDS_OMM_VERS, EPOCH, MEAN_ELEMENTS, \u2026). Fetched from CelesTrak by the tle_lookup tool or supplied directly.",
"examples": [
{
"CCSDS_OMM_VERS": "2.0",
"EPOCH": "2024-01-01T12:00:00.000000",
"MEAN_MOTION": 15.5
}
],
"title": "Omm",
"type": "object"
}
},
"required": [
"omm"
],
"title": "TleOmm",
"type": "object"
}
},
"properties": {
"observer": {
"anyOf": [
{
"$ref": "#/$defs/NamedStation"
},
{
"$ref": "#/$defs/ObserverCoordinates"
}
],
"description": "Ground station / observer location. Either a NamedStation ({name: ...}) where name is one of 'madrid', 'goldstone', 'canberra', 'svalbard', 'wallops', 'esrange', 'gsfc', 'jpl' \u2014 resolved to lat/lon/alt against the v0.1 registry \u2014 or explicit ObserverCoordinates ({lat_deg, lon_deg, height_km}) for arbitrary sites.",
"title": "Observer"
},
"target_tle": {
"anyOf": [
{
"$ref": "#/$defs/TleLines"
},
{
"$ref": "#/$defs/TleOmm"
}
],
"description": "The satellite to track, supplied as a TLE line pair ({line1, line2}) or as an OMM payload (the CCSDS-standard JSON returned by tle_lookup). Either form is accepted.",
"title": "Target Tle"
},
"start": {
"description": "Start of the search window, UTC ISO 8601 with a time component (e.g. '2024-01-01T00:00:00Z').",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Start",
"type": "string"
},
"end": {
"description": "End of the search window, UTC ISO 8601 with a time component. Must be strictly after `start`.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "End",
"type": "string"
},
"min_elevation_deg": {
"description": "Horizon mask, in degrees (not radians). Passes peaking below this elevation are filtered out. Conventional thresholds: 10\u00b0 for amateur ground stations, 15\u00b0 for DSN-style large-dish ops; values below 5\u00b0 are usually noisy due to refraction and terrain. Must be in [0, 90].",
"title": "Min Elevation Deg",
"type": "number"
},
"min_range_km": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional minimum range from observer to satellite at the pass peak (km). Passes whose closest approach is nearer than this are dropped. Useful for excluding very-low-altitude horizon noise.",
"title": "Min Range Km"
},
"max_range_km": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional maximum range from observer to satellite at the pass peak (km). Passes whose closest approach is farther than this are dropped.",
"title": "Max Range Km"
}
},
"required": [
"observer",
"target_tle",
"start",
"end",
"min_elevation_deg"
],
"title": "access_windowsArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"AccessWindow": {
"additionalProperties": false,
"description": "A single ground-station pass: AOS to LOS with peak-elevation details.",
"properties": {
"aos": {
"description": "Acquisition-of-signal epoch (UTC ISO 8601).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Aos",
"type": "string"
},
"los": {
"description": "Loss-of-signal epoch (UTC ISO 8601).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Los",
"type": "string"
},
"peak_elevation_time": {
"description": "Epoch of maximum elevation during the pass (UTC ISO 8601).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Peak Elevation Time",
"type": "string"
},
"peak_elevation": {
"$ref": "#/$defs/Quantity",
"description": "Maximum elevation above the local horizon during the pass (deg).",
"examples": [
{
"unit": "deg",
"value": 35.0
}
]
},
"range_at_aos": {
"$ref": "#/$defs/Quantity",
"description": "Observer-to-satellite range at AOS (km). Always at the horizon-mask elevation.",
"examples": [
{
"unit": "km",
"value": 1483.0
}
]
},
"range_at_peak": {
"$ref": "#/$defs/Quantity",
"description": "Observer-to-satellite range at peak elevation (km). Typically the closest approach during the pass; used by the `min_range_km` / `max_range_km` filters.",
"examples": [
{
"unit": "km",
"value": 600.0
}
]
},
"range_at_los": {
"$ref": "#/$defs/Quantity",
"description": "Observer-to-satellite range at LOS (km). Always at the horizon-mask elevation.",
"examples": [
{
"unit": "km",
"value": 1505.0
}
]
},
"duration": {
"$ref": "#/$defs/Quantity",
"description": "Time between AOS and LOS, in seconds.",
"examples": [
{
"unit": "s",
"value": 480.0
}
]
}
},
"required": [
"aos",
"los",
"peak_elevation_time",
"peak_elevation",
"range_at_aos",
"range_at_peak",
"range_at_los",
"duration"
],
"title": "AccessWindow",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "List of `AccessWindow`s for the requested observer and time range.",
"properties": {
"windows": {
"description": "Complete (AOS, peak, LOS) passes inside the requested window. Passes that begin before `start` or end after `end` are omitted \u2014 only complete triples are emitted. Range-filtered passes are also omitted.",
"items": {
"$ref": "#/$defs/AccessWindow"
},
"title": "Windows",
"type": "array"
}
},
"required": [
"windows"
],
"title": "AccessWindowsResponse",
"type": "object"
}
bplane_target¶
Compute B-plane coordinates (B·T, B·R), hyperbolic excess velocity v_∞, and asymptote declination for a hyperbolic planetocentric flyby, and optionally return the linearised one-step Δv at the input epoch that drives the B-plane to caller-supplied target coordinates. e.g. bplane_target(state={'r': {'value': [5000, 0, 0], 'unit': 'km'}, 'v': {'value': [0, 5.069, 0], 'unit': 'km/s'}, 'frame': 'ICRF', 'epoch': '2026-12-01T00:00:00Z'}, target_body='mars', target_epoch='2026-12-01T00:00:00Z', target_btr_km=1000.0) returns the current B-plane state plus the Δv that drives B·R toward 1000 km. Read-only mode (both target_btr_km and target_btt_km omitted) skips the targeting step. The input state must be planetocentric — r and v relative to target_body's centre — in an inertial frame (ICRF / GCRS / CIRS etc.); the tool does not transform the state. Epochs are UTC ISO 8601 with a mandatory time component ('2026-12-01T00:00:00Z'). target_epoch is the closest-approach epoch the caller is designing for; the Δv applies at state.epoch and is documented as such. The targeting solver is linearised about the input state; for large required Δv apply the result and re-call to converge, or hand off to a multi-iteration targeter. Non-hyperbolic states (specific energy ≤ 0) raise invalid_input.not_hyperbolic; unknown target_body raises invalid_input.unknown_body.
Input schema:
{
"$defs": {
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"StateVector": {
"additionalProperties": false,
"description": "Cartesian state in a named frame at a named epoch.\n\nThe position and velocity are :class:`QuantityVector` instances so the\nunit (km vs m, km/s vs m/s) is on the wire \u2014 never implicit.",
"properties": {
"r": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position vector [x, y, z]. Unit must be a length (km / m / AU).",
"examples": [
{
"unit": "km",
"value": [
7000.0,
0.0,
0.0
]
}
]
},
"v": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian velocity vector [vx, vy, vz]. Unit must be a velocity (km/s / m/s).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
7.5,
0.0
]
}
]
},
"frame": {
"$ref": "#/$defs/Frame",
"description": "Reference frame in which r and v are expressed.",
"examples": [
"TEME",
"ICRF"
]
},
"epoch": {
"description": "UTC ISO 8601 epoch at which the state is valid.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
}
},
"required": [
"r",
"v",
"frame",
"epoch"
],
"title": "StateVector",
"type": "object"
}
},
"properties": {
"state": {
"$ref": "#/$defs/StateVector",
"description": "Hyperbolic planetocentric state at the maneuver epoch \u2014 position (km) and velocity (km/s) relative to `target_body`'s centre, in an inertial frame (ICRF or GCRS). The tool does not transform the input; pass a state already in body-centred inertial coordinates."
},
"target_body": {
"description": "Body the spacecraft is flying by, lower-case JPL Horizons name ('mercury', 'venus', 'earth', 'moon', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune'). Selects \u03bc and the body radius used in the B-plane geometry.",
"title": "Target Body",
"type": "string"
},
"target_epoch": {
"description": "Reference epoch for the targeting evaluation, UTC ISO 8601 with a time component. Carried in the response for traceability; the linearised solver applies the returned \u0394v at `state.epoch`.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Target Epoch",
"type": "string"
},
"target_btr_km": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional B-plane R-component target (km). When supplied, the tool returns the one-step linearised \u0394v that drives B\u00b7R toward this value. Pass either or both of `target_btr_km` and `target_btt_km` to invoke the targeting solver; omit both to get the B-plane element calculation only.",
"title": "Target Btr Km"
},
"target_btt_km": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional B-plane T-component target (km). Same semantics as `target_btr_km` \u2014 supplying both fully constrains the B-plane intercept; supplying just one solves the 1-DOF target.",
"title": "Target Btt Km"
}
},
"required": [
"state",
"target_body",
"target_epoch"
],
"title": "bplane_targetArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"BplaneResidual": {
"additionalProperties": false,
"description": "Post-\u0394v recompute of the B-plane coords, plus the constraint-space error.\n\n``actual_b_t_after_dv`` and ``actual_b_r_after_dv`` are computed by\napplying the linear \u0394v to the input velocity and recomputing the\nB-plane geometry nonlinearly \u2014 the ground truth the caller will see\nonce the burn is executed. ``magnitude`` is the Euclidean distance\nfrom the requested target in the dimensions the caller constrained\n(1-D if only one target was supplied, 2-D if both).",
"properties": {
"actual_b_t_after_dv": {
"$ref": "#/$defs/Quantity",
"description": "B\u00b7T after applying the returned \u0394v to the input velocity and recomputing the B-plane geometry exactly (no linearisation). Compare against the requested target_btt_km to gauge the linear solver's accuracy.",
"examples": [
{
"unit": "km",
"value": 1995.0
}
]
},
"actual_b_r_after_dv": {
"$ref": "#/$defs/Quantity",
"description": "B\u00b7R after applying the returned \u0394v to the input velocity and recomputing the B-plane geometry exactly (no linearisation).",
"examples": [
{
"unit": "km",
"value": -3.0
}
]
},
"magnitude": {
"$ref": "#/$defs/Quantity",
"description": "Euclidean distance between the post-\u0394v (B\u00b7T, B\u00b7R) and the requested target in the constrained dimensions, km. A small magnitude means the linear solver hit the target; a large magnitude means a follow-up iteration is needed.",
"examples": [
{
"unit": "km",
"value": 5.4
}
]
}
},
"required": [
"actual_b_t_after_dv",
"actual_b_r_after_dv",
"magnitude"
],
"title": "BplaneResidual",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
}
},
"additionalProperties": false,
"description": "Response from :func:`bplane_target`.",
"properties": {
"b_r": {
"$ref": "#/$defs/Quantity",
"description": "B\u00b7R component of the impact-parameter vector at the input state, km.",
"examples": [
{
"unit": "km",
"value": 0.0
}
]
},
"b_t": {
"$ref": "#/$defs/Quantity",
"description": "B\u00b7T component of the impact-parameter vector at the input state, km.",
"examples": [
{
"unit": "km",
"value": -8660.25
}
]
},
"v_infinity": {
"$ref": "#/$defs/QuantityVector",
"description": "Hyperbolic excess velocity vector at the incoming asymptote, km/s, in the input state's reference frame. Magnitude is the scalar v_\u221e; direction is the incoming asymptote unit vector S^.",
"examples": [
{
"unit": "km/s",
"value": [
1.464,
2.535,
0.0
]
}
]
},
"asymptote_declination": {
"$ref": "#/$defs/Quantity",
"description": "Declination of the incoming-asymptote unit vector S^ above the input frame's XY plane, deg. ICRF/GCRS users get an ecliptic-style declination; body-fixed frame users get the body's equatorial declination.",
"examples": [
{
"unit": "deg",
"value": 0.0
}
]
},
"dv_required": {
"anyOf": [
{
"$ref": "#/$defs/QuantityVector"
},
{
"type": "null"
}
],
"default": null,
"description": "Linearised one-step \u0394v at the input state's epoch that drives (B\u00b7T, B\u00b7R) to the requested targets. None when the tool ran in read-only mode (both target_btr_km and target_btt_km omitted).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
0.0006,
0.0
]
}
]
},
"residual": {
"anyOf": [
{
"$ref": "#/$defs/BplaneResidual"
},
{
"type": "null"
}
],
"default": null,
"description": "Nonlinear recompute of the B-plane coords after applying dv_required, plus the constraint-space error. None in read-only mode."
}
},
"required": [
"b_r",
"b_t",
"v_infinity",
"asymptote_declination"
],
"title": "BplaneTargetResponse",
"type": "object"
}
czml_trajectory¶
Convert a trajectory into a CZML document for a Cesium 3D client and return it as an embedded resource, with an inline summary (packet count, time span). e.g. czml_trajectory(trajectory=
Input schema:
{
"$defs": {
"ContactInput": {
"additionalProperties": false,
"description": "A ground-station contact: observer geometry, target object, and access windows.\n\nMaps to a :class:`gmat_czml.Contact`. ``target`` is the rendered object the\nline of sight points to; it defaults to the trajectory's single object\n(``'satellite'``) and need only be set for a different name. ``windows`` are\nUTC; an empty list still places the observer but draws no link.",
"properties": {
"station": {
"$ref": "#/$defs/ContactStationInput",
"description": "The observing ground station (placement and label)."
},
"target": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Rendered object name the line of sight points to. Defaults to the trajectory's object, 'satellite'.",
"examples": [
"satellite"
],
"title": "Target"
},
"windows": {
"description": "UTC access windows during which the line of sight is shown. An empty list places the station but draws no link.",
"items": {
"$ref": "#/$defs/ContactWindowInput"
},
"title": "Windows",
"type": "array"
}
},
"required": [
"station"
],
"title": "ContactInput",
"type": "object"
},
"ContactStationInput": {
"additionalProperties": false,
"description": "A ground station for a contact annotation: identity plus geodetic placement.\n\nThe wrapper builds a :class:`gmat_czml.GroundStation` from this. ``name`` is\nthe observer's CZML entity id and label; ``lat`` / ``lon`` are geodetic\n(east-positive longitude) and ``height`` is above the WGS-84 ellipsoid.",
"properties": {
"name": {
"description": "Observer id and label text, e.g. 'madrid'.",
"examples": [
"madrid"
],
"title": "Name",
"type": "string"
},
"lat": {
"$ref": "#/$defs/Quantity",
"description": "Geodetic latitude, deg.",
"examples": [
{
"unit": "deg",
"value": 40.43
}
]
},
"lon": {
"$ref": "#/$defs/Quantity",
"description": "Geodetic longitude (east-positive), deg.",
"examples": [
{
"unit": "deg",
"value": -4.25
}
]
},
"height": {
"anyOf": [
{
"$ref": "#/$defs/Quantity"
},
{
"type": "null"
}
],
"default": null,
"description": "Height above the WGS-84 ellipsoid, km. Omit for sea level (0 km).",
"examples": [
{
"unit": "km",
"value": 0.8
}
]
}
},
"required": [
"name",
"lat",
"lon"
],
"title": "ContactStationInput",
"type": "object"
},
"ContactWindowInput": {
"additionalProperties": false,
"description": "One access window \u2014 a UTC start/end pair the line of sight is shown during.",
"properties": {
"start": {
"description": "Window start (UTC ISO 8601).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Start",
"type": "string"
},
"end": {
"description": "Window end (UTC ISO 8601).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "End",
"type": "string"
}
},
"required": [
"start",
"end"
],
"title": "ContactWindowInput",
"type": "object"
},
"CzmlTrajectoryState": {
"additionalProperties": false,
"description": "One state of the series ``czml_trajectory`` renders \u2014 like :class:`StateVector`,\nbut velocity is optional.\n\nCZML animates positions, so velocity is not load-bearing for the orbit path:\na position-only series is rendered with the velocity NaN-padded (gmat-czml's\nown contract). ``r`` / ``frame`` / ``epoch`` are required; ``v`` may be\nomitted (for the whole series \u2014 mixing states with and without velocity is\nrejected). The unit envelope matches :class:`StateVector` so an\nsgp4_propagate / gmat-ephemeris series feeds straight in.",
"properties": {
"r": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position vector [x, y, z]. Unit must be a length (km / m / AU).",
"examples": [
{
"unit": "km",
"value": [
7000.0,
0.0,
0.0
]
}
]
},
"v": {
"anyOf": [
{
"$ref": "#/$defs/QuantityVector"
},
{
"type": "null"
}
],
"default": null,
"description": "Cartesian velocity vector [vx, vy, vz]. Unit must be a velocity (km/s / m/s). Optional \u2014 omit for a position-only series (rendered with NaN-padded velocity).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
7.5,
0.0
]
}
]
},
"frame": {
"$ref": "#/$defs/Frame",
"description": "Reference frame in which r and v are expressed.",
"examples": [
"TEME",
"ICRF"
]
},
"epoch": {
"description": "UTC ISO 8601 epoch at which the state is valid.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
}
},
"required": [
"r",
"frame",
"epoch"
],
"title": "CzmlTrajectoryState",
"type": "object"
},
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
}
},
"properties": {
"trajectory": {
"description": "The state SERIES to render, e.g. the `states` from an sgp4_propagate call or a gmat ephemeris. Each entry is a {r, v, frame, epoch}; at least two states are required and all must share one frame (TEME / ICRF / GCRS / ITRS). Velocity is optional \u2014 omit it for a position-only series.",
"items": {
"$ref": "#/$defs/CzmlTrajectoryState"
},
"title": "Trajectory",
"type": "array"
},
"intervals": {
"anyOf": [
{
"items": {
"$ref": "#/$defs/ContactInput"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional ground-station contacts to annotate. Each entry is a {station, target, windows}: the station's name + geodetic lat / lon / height, the rendered object the line of sight points to (defaults to 'satellite'), and the UTC access windows it is shown during. Omit for none. This is NOT a station-less 'shade these time spans' primitive.",
"title": "Intervals"
},
"style": {
"default": "default",
"description": "Visual style preset: 'default' (alias of 'sat-default'), or one of the gmat-czml presets sat-default / sat-red / sat-green / sat-magenta. An unrecognised name returns a typed error.",
"title": "Style",
"type": "string"
}
},
"required": [
"trajectory"
],
"title": "czml_trajectoryArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"CzmlResourceInfo": {
"additionalProperties": false,
"description": "Identity and cardinalities of the attached CZML document.\n\n``packet_count`` / ``object_count`` / ``contact_count`` are document\n*cardinalities* (counts, not physical quantities), so they sit outside the\n``{value, unit}`` envelope and are declared exempt where the unit-discipline\nmeta-test polices the attachment-bearing schemas. The CZML text itself rides\nas a separate ``EmbeddedResource`` block keyed by ``uri``, not in this summary.",
"properties": {
"packet_count": {
"description": "CZML packets emitted (a cardinality): the document preamble, one per object, plus any contact observer / line-of-sight entities.",
"title": "Packet Count",
"type": "integer"
},
"object_count": {
"description": "Rendered trajectory objects (a cardinality).",
"title": "Object Count",
"type": "integer"
},
"contact_count": {
"description": "Ground-station contacts annotated (a cardinality).",
"title": "Contact Count",
"type": "integer"
},
"format": {
"const": "czml",
"default": "czml",
"description": "Attachment document format; always 'czml' for this tool.",
"title": "Format",
"type": "string"
},
"media_type": {
"const": "application/json",
"default": "application/json",
"description": "MIME type the embedded CZML resource carries (CZML is JSON).",
"title": "Media Type",
"type": "string"
},
"uri": {
"description": "Stable URI the embedded CZML resource is keyed by.",
"examples": [
"czml://trajectory/satellite"
],
"title": "Uri",
"type": "string"
}
},
"required": [
"packet_count",
"object_count",
"contact_count",
"uri"
],
"title": "CzmlResourceInfo",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "Structured summary accompanying a ``czml_trajectory`` CZML document.\n\nThe CZML rides as an additive ``EmbeddedResource``; this summary is always\npresent and is what a text-only client reads. ``time_span`` carries its unit;\nthe packet / object / contact counts live on :class:`CzmlResourceInfo`.",
"properties": {
"time_span": {
"$ref": "#/$defs/Quantity",
"description": "Wall-clock span from the first to the last state epoch, hours.",
"examples": [
{
"unit": "hours",
"value": 1.5
}
]
},
"frame": {
"description": "Recognised CZML reference frame the states were tagged with (echo).",
"examples": [
"TEME"
],
"title": "Frame",
"type": "string"
},
"style": {
"description": "Resolved gmat-czml style preset name.",
"examples": [
"sat-default"
],
"title": "Style",
"type": "string"
},
"has_velocity": {
"description": "Whether the input series carried velocity; a position-only series is rendered with the velocity NaN-padded.",
"title": "Has Velocity",
"type": "boolean"
},
"resource": {
"$ref": "#/$defs/CzmlResourceInfo",
"description": "The emitted CZML document's identity and cardinalities."
}
},
"required": [
"time_span",
"frame",
"style",
"has_velocity",
"resource"
],
"title": "CzmlTrajectoryResponse",
"type": "object"
}
frame_transform¶
Transform a Cartesian state vector from its current frame into a different reference frame. e.g. frame_transform(state={r: ..., v: ..., frame: 'TEME', epoch: '2024-01-01T12:00:00Z'}, to_frame='ICRF') re-expresses the state in ICRF. Supported frames: ICRF (barycentric inertial), GCRS (Earth-centred inertial), ITRS (Earth-fixed rotating), TEME (SGP4's native output), CIRS (Earth-rotating intermediate), IAU_EARTH (alias for ITRS — same Earth-body-fixed frame, different naming). The epoch arg defaults to the state's own epoch; pass an override only when you deliberately want to transform 'as if the state were at a different time'. Earth-fixed frames (ITRS / IAU_EARTH) rotate at sidereal rate (~7.292e-5 rad/s); the epoch must match the state's epoch to within a few seconds or the transformed velocity will be off by ~1 km/s — don't pass epoch to override a stale state's own epoch. Epochs are UTC ISO 8601 with a mandatory time component. TIRS is not yet supported as a transform endpoint (use ITRS instead); IAU_MARS and IAU_MOON require SPICE-backed body-fixed rotations not available in this tool.
Input schema:
{
"$defs": {
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"StateVector": {
"additionalProperties": false,
"description": "Cartesian state in a named frame at a named epoch.\n\nThe position and velocity are :class:`QuantityVector` instances so the\nunit (km vs m, km/s vs m/s) is on the wire \u2014 never implicit.",
"properties": {
"r": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position vector [x, y, z]. Unit must be a length (km / m / AU).",
"examples": [
{
"unit": "km",
"value": [
7000.0,
0.0,
0.0
]
}
]
},
"v": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian velocity vector [vx, vy, vz]. Unit must be a velocity (km/s / m/s).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
7.5,
0.0
]
}
]
},
"frame": {
"$ref": "#/$defs/Frame",
"description": "Reference frame in which r and v are expressed.",
"examples": [
"TEME",
"ICRF"
]
},
"epoch": {
"description": "UTC ISO 8601 epoch at which the state is valid.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
}
},
"required": [
"r",
"v",
"frame",
"epoch"
],
"title": "StateVector",
"type": "object"
}
},
"properties": {
"state": {
"$ref": "#/$defs/StateVector",
"description": "Input state vector carrying position (km), velocity (km/s), the source `frame`, and the `epoch` at which it is valid. Supported frames: ICRF, GCRS, ITRS, TEME, CIRS, IAU_EARTH. TIRS, IAU_MARS, and IAU_MOON are deliberately rejected \u2014 astropy does not expose them as transform endpoints at v0.1."
},
"to_frame": {
"$ref": "#/$defs/Frame",
"description": "Target frame for the output state. Same supported-frame list as the input. Identity (to_frame == state.frame) is a valid no-op that still returns a structured response."
},
"epoch": {
"anyOf": [
{
"description": "ISO 8601 UTC timestamp with a mandatory time component. Examples: '2026-05-23T12:00:00Z', '2026-05-23T12:00:00.500+00:00'. A bare date like '2026-05-23' is rejected.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional override for the epoch used in the transform, UTC ISO 8601 with a mandatory time component. When omitted, the tool uses `state.epoch`. Override is useful when re-evaluating an Earth-rotating-frame state at a different time than the state's native validity epoch.",
"title": "Epoch"
}
},
"required": [
"state",
"to_frame"
],
"title": "frame_transformArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"StateVector": {
"additionalProperties": false,
"description": "Cartesian state in a named frame at a named epoch.\n\nThe position and velocity are :class:`QuantityVector` instances so the\nunit (km vs m, km/s vs m/s) is on the wire \u2014 never implicit.",
"properties": {
"r": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position vector [x, y, z]. Unit must be a length (km / m / AU).",
"examples": [
{
"unit": "km",
"value": [
7000.0,
0.0,
0.0
]
}
]
},
"v": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian velocity vector [vx, vy, vz]. Unit must be a velocity (km/s / m/s).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
7.5,
0.0
]
}
]
},
"frame": {
"$ref": "#/$defs/Frame",
"description": "Reference frame in which r and v are expressed.",
"examples": [
"TEME",
"ICRF"
]
},
"epoch": {
"description": "UTC ISO 8601 epoch at which the state is valid.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
}
},
"required": [
"r",
"v",
"frame",
"epoch"
],
"title": "StateVector",
"type": "object"
}
},
"additionalProperties": false,
"description": "Transformed state plus an IERS freshness anchor when EOP data was used.",
"properties": {
"state": {
"$ref": "#/$defs/StateVector",
"description": "Transformed state vector in the requested `to_frame`."
},
"iers_bulletin_a_fetched_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "IERS Bulletin A freshness anchor (ISO 8601 UTC). Non-null whenever the transform path touched an Earth-rotating frame (ITRS, GCRS, CIRS, IAU_EARTH), which depend on Earth-orientation parameters.",
"title": "Iers Bulletin A Fetched At"
}
},
"required": [
"state"
],
"title": "FrameTransformResponse",
"type": "object"
}
gmat_execute_script¶
Escape-hatch raw GMAT script executor — minimal validation, raw text output. Prefer gmat_run_mission whenever your goal is 'run this mission and tell me what happened': it returns a structured snapshot, parsed report rows, and typed convergence flags, all shape-disciplined for small-model contexts. Reach for gmat_execute_script only when the curated surface doesn't fit — e.g. you need the raw ReportFile text verbatim (header line, units row, formatting) instead of parsed rows, or you want to run a script with side-effect-only commands that don't surface through the structured response. e.g. gmat_execute_script(script='/abs/path/to/custom.script') runs the script and returns each ReportFile's raw text plus a list of every other artefact GMAT wrote (ephemerides, contact reports, solver logs). script must be either an absolute path to a .script file or the full inline script text (auto-detected by leading '%' / 'Create' markers); do not pass a Python Mission object. Failures-as-data contract: a GMAT engine failure mid-run returns ok=False with the engine's stderr in stderr rather than raising — read the log to diagnose. Pre-run input failures (bad path, GMAT install missing) still raise typed invalid_input. / upstream. errors. output controls line shaping for ReportFile text: the default 'summary' inlines short reports (<=60 lines) whole and trims longer ones to first/last 20 lines so the response fits small-model input caps; 'full' returns every line of every report. Non-ReportFile artefacts (ephemerides, contact reports, solver logs) are always pointer-only regardless of mode — read the curated tool's response shape or copy the paths before the tool returns; the run's temp directory is cleaned up at function exit.
Input schema:
{
"properties": {
"script": {
"description": "Either the absolute path to a GMAT .script file (e.g. '/abs/path/to/custom.script') or the full inline script text starting with '%' comments or 'Create' resource declarations. Auto-detected by content: a string with newlines or a leading '%' / 'Create ' is inline, anything else is treated as a path. Do not pass a Python Mission object.",
"title": "Script",
"type": "string"
},
"output": {
"default": "summary",
"description": "Line-shaping mode for ReportFile text. The default 'summary' inlines short reports (<=60 lines) whole and trims longer ones to first/last 20 lines so the response fits small-model input caps. 'full' returns every line of every report \u2014 pass only when downstream consumers need the dense data and can absorb the bytes. Non-ReportFile artefacts are pointer-only regardless of mode.",
"enum": [
"summary",
"full"
],
"title": "Output",
"type": "string"
},
"timeout_seconds": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional wall-clock cap for this run, in seconds. GMAT runs out-of-process; if the run exceeds the cap the worker is killed and the tool returns upstream.gmat_run_timeout. Leave null to use the server's configured default. Raise it for a heavy optimizer / long propagation, lower it for a quick check; values are clamped to the server's configured maximum and must be positive.",
"title": "Timeout Seconds"
}
},
"required": [
"script"
],
"title": "gmat_execute_scriptArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"OutputPointer": {
"additionalProperties": false,
"description": "Name + path for an EphemerisFile or ContactLocator output.\n\nEphemerides and contact reports are intrinsically too large to inline \u2014\na 24-hour OEM file is several MB. The pointer carries the resource name\nso an LLM can describe the artefact, plus the absolute path for users\nwho want to inspect the file outside the conversation.",
"properties": {
"name": {
"description": "Resource name as declared in the .script (e.g. 'EphemerisFile1').",
"title": "Name",
"type": "string"
},
"path": {
"description": "Absolute path the file landed at. Lives under a registry-owned temp directory until the run is LRU-evicted (configurable via ASTRODYNAMICS_MCP_RUN_REGISTRY_LIMIT). Read the file directly off disk, or pass the resource name to gmat_read_run_artefact for shape-disciplined text inlining.",
"title": "Path",
"type": "string"
}
},
"required": [
"name",
"path"
],
"title": "OutputPointer",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"RawReportContent": {
"additionalProperties": false,
"description": "Raw text view of one ReportFile output for :func:`gmat_execute_script`.\n\nThe escape hatch returns the report verbatim \u2014 header line, units row,\ndata \u2014 instead of parsing into a DataFrame the way\n:class:`ReportFileShape` does. In ``output=\"summary\"`` mode a short\nreport (``line_count <= 60``) lands inline via ``content``; a longer\nreport populates ``head`` (first 20 lines) and ``tail`` (last 20\nlines) so the response stays under small-model input caps regardless\nof how long the run was. In ``output=\"full\"`` mode ``content`` always\ncarries the entire file. ``truncated`` distinguishes the two cases.",
"properties": {
"name": {
"description": "ReportFile resource name as declared in the .script.",
"title": "Name",
"type": "string"
},
"path": {
"description": "Absolute path the artefact landed at. Lives under a registry-owned temp directory until the run is LRU-evicted, so a follow-up gmat_read_run_artefact call can re-fetch the bytes; the raw text below carries the inlined snapshot for the common case.",
"title": "Path",
"type": "string"
},
"content": {
"default": "",
"description": "Full report text when `truncated` is False, joined with the file's original newlines. Empty when `truncated` is True \u2014 read `head` and `tail` instead.",
"title": "Content",
"type": "string"
},
"head": {
"default": "",
"description": "First 20 lines joined with newlines when `truncated` is True. Empty when the report fit fully inline via `content`.",
"title": "Head",
"type": "string"
},
"tail": {
"default": "",
"description": "Last 20 lines joined with newlines when `truncated` is True. Empty when the report fit fully inline via `content`.",
"title": "Tail",
"type": "string"
},
"line_count": {
"$ref": "#/$defs/Quantity",
"description": "Total line count of the file (dimensionless count, unit '1'). Trailing-newline-only files count their data lines, not an empty terminal line.",
"examples": [
{
"unit": "1",
"value": 1440.0
}
]
},
"byte_count": {
"$ref": "#/$defs/Quantity",
"description": "Total size of the file on disk in bytes (dimensionless count, unit '1'). Captured before any UTF-8 decoding.",
"examples": [
{
"unit": "1",
"value": 87432.0
}
]
},
"truncated": {
"description": "True when the artefact had more than 60 lines under output='summary' and the response carries head + tail rather than the full text. False under output='full' or when the artefact fit fully inline.",
"title": "Truncated",
"type": "boolean"
}
},
"required": [
"name",
"path",
"line_count",
"byte_count",
"truncated"
],
"title": "RawReportContent",
"type": "object"
}
},
"additionalProperties": false,
"description": "Response from :func:`gmat_execute_script`.\n\nMinimal-validation escape hatch: carries the success / failure status,\nGMAT's captured log output, raw text of every ReportFile the run\nwrote, and a pointer-only list of every other artefact that landed in\nthe run's output directory. ``ok=False`` surfaces engine failures as\ndata \u2014 the caller inspects ``stderr`` rather than catching an\nexception \u2014 so an LLM can introspect a failing script the same way a\nhuman reads the GMAT log.",
"properties": {
"run_id": {
"description": "UUID4 hex identifying this run in the server-process registry. Pass to gmat_read_run_artefact along with an artefact's resource name or basename to read the raw bytes in a later tool call. Populated on both ok=True and ok=False so a follow-up read can inspect partial outputs even after an engine failure; for failures the registry usually carries only the GMAT log.",
"title": "Run Id",
"type": "string"
},
"ok": {
"description": "True when the mission sequence completed without raising a GmatRunError. False on a GMAT engine failure mid-run \u2014 read `stderr` for GMAT's diagnostic; `reports` and `artefacts` are empty in that case. Pre-run input failures (path-not-found, GMAT install missing) still raise typed errors rather than returning ok=False.",
"title": "Ok",
"type": "boolean"
},
"stderr": {
"description": "GMAT's captured stdout / stderr log from the run, verbatim. Always populated: on a successful run it carries warnings and the solver iteration trace; on a failed run it carries the engine error and is the primary signal for the caller. Empty string when GMAT wrote nothing to stderr (rare).",
"title": "Stderr",
"type": "string"
},
"wall_clock": {
"$ref": "#/$defs/Quantity",
"description": "Wall-clock duration of mission.run() in seconds (unit 's'). Captured even on failure so the caller can tell a fast crash apart from a long-running run that diverged.",
"examples": [
{
"unit": "s",
"value": 0.42
}
]
},
"reports": {
"description": "One :class:`RawReportContent` per ReportFile the run wrote, in the order GMAT declared them. Empty when ok=False -- the tool short-circuits before parsing reports on engine failure, so if the run wrote partial reports before erroring you can still pull them via gmat_read_run_artefact using the returned run_id.",
"items": {
"$ref": "#/$defs/RawReportContent"
},
"title": "Reports",
"type": "array"
},
"artefacts": {
"description": "Every regular file the run wrote under its output directory, deduplicated and sorted by path. ReportFile / EphemerisFile / ContactLocator / Solver outputs carry their declared resource name; stray files (e.g. the GMAT log if it landed on disk) fall back to their basename. Pointer-only \u2014 read `reports` for ReportFile content inline, or copy the path yourself before the tool returns.",
"items": {
"$ref": "#/$defs/OutputPointer"
},
"title": "Artefacts",
"type": "array"
}
},
"required": [
"run_id",
"ok",
"stderr",
"wall_clock"
],
"title": "GmatExecuteScriptResponse",
"type": "object"
}
gmat_read_run_artefact¶
Read the raw text of one file written by a prior gmat_run_mission, gmat_sweep, or gmat_execute_script call, keyed by the run_id that producer returned. The producer tools shape their inline responses for small-model input caps; reach for this tool when you need the verbatim bytes of an output that was too large to inline (a long EphemerisFile, a ContactLocator report, the GMAT log) or when you want a ReportFile's header / units rows preserved exactly. e.g. after gmat_run_mission returns run_id='abc...' with an OutputPointer for 'EphemerisFile1', call gmat_read_run_artefact(run_id='abc...', name='EphemerisFile1', output='summary') to inspect the first and last 20 lines. name resolves first against the run's declared resource names (ReportFile / EphemerisFile / ContactLocator / Solver), then against plain file basenames directly under the run's output directory (so 'GMAT.log' and solver '.data' files are reachable). output controls line shaping: the default 'summary' inlines short files (<=60 lines) whole and trims longer ones to first/last 20 lines; 'full' returns every line. Read-only — this tool does not run GMAT, mutate files, or extend the run's retention. The registry retains the last N runs per server process (configurable via ASTRODYNAMICS_MCP_RUN_REGISTRY_LIMIT, default 50); evicted runs return invalid_input.unknown_run_id, and an unknown name within a known run returns invalid_input.unknown_artefact_name with the available set in data. After a server restart the index is best-effort: if the run's temp directory still exists the read succeeds, otherwise the tool returns invalid_input.artefact_evicted. Text outputs only -- the tool reads the first 8 KB of each artefact and rejects anything containing a NULL byte as invalid_input.binary_artefact rather than returning decoded gibberish. The text/binary line therefore tracks the standard grep -I / git diff --binary heuristic: GMAT's ASCII formats (ReportFile, OEM / CCSDS-OEM / CCSDS-AEM / STK ephemerides, ContactLocator, solver .data, GMAT.log, sweep manifest.jsonl) all flow through; binary outputs (SPK and GMAT Code-500 ephemerides, sweep .parquet, .mat files) are rejected.
Input schema:
{
"properties": {
"run_id": {
"description": "UUID4 hex returned by an earlier gmat_run_mission, gmat_sweep, or gmat_execute_script call. The producer's response carries this in its `run_id` field; pass it back verbatim. Unknown ids raise invalid_input.unknown_run_id with the known set in `data`.",
"title": "Run Id",
"type": "string"
},
"name": {
"description": "Artefact selector. Resolves first against the run's declared GMAT resource names (e.g. 'ReportFile1', 'EphemerisFile1', 'ContactLocator1', or a Solver name like 'DC'); falls back to a plain file basename directly under the run's output directory (e.g. 'GMAT.log', 'manifest.jsonl', or a stray '*.data'). The lookup is non-recursive \u2014 files in subdirectories (e.g. the per-run sweep artefacts) are reachable only via their declared names or via the sweep manifest. Unknown names raise invalid_input.unknown_artefact_name with the available set in `data`.",
"title": "Name",
"type": "string"
},
"output": {
"default": "summary",
"description": "Line-shaping mode for the artefact text. The default 'summary' inlines short files (<=60 lines) whole and trims longer ones to first/last 20 lines so the response fits small-model input caps. 'full' returns every line \u2014 pass only when downstream consumers need the dense data and can absorb the bytes.",
"enum": [
"summary",
"full"
],
"title": "Output",
"type": "string"
}
},
"required": [
"run_id",
"name"
],
"title": "gmat_read_run_artefactArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "Raw text view of one ReportFile output for :func:`gmat_execute_script`.\n\nThe escape hatch returns the report verbatim \u2014 header line, units row,\ndata \u2014 instead of parsing into a DataFrame the way\n:class:`ReportFileShape` does. In ``output=\"summary\"`` mode a short\nreport (``line_count <= 60``) lands inline via ``content``; a longer\nreport populates ``head`` (first 20 lines) and ``tail`` (last 20\nlines) so the response stays under small-model input caps regardless\nof how long the run was. In ``output=\"full\"`` mode ``content`` always\ncarries the entire file. ``truncated`` distinguishes the two cases.",
"properties": {
"name": {
"description": "ReportFile resource name as declared in the .script.",
"title": "Name",
"type": "string"
},
"path": {
"description": "Absolute path the artefact landed at. Lives under a registry-owned temp directory until the run is LRU-evicted, so a follow-up gmat_read_run_artefact call can re-fetch the bytes; the raw text below carries the inlined snapshot for the common case.",
"title": "Path",
"type": "string"
},
"content": {
"default": "",
"description": "Full report text when `truncated` is False, joined with the file's original newlines. Empty when `truncated` is True \u2014 read `head` and `tail` instead.",
"title": "Content",
"type": "string"
},
"head": {
"default": "",
"description": "First 20 lines joined with newlines when `truncated` is True. Empty when the report fit fully inline via `content`.",
"title": "Head",
"type": "string"
},
"tail": {
"default": "",
"description": "Last 20 lines joined with newlines when `truncated` is True. Empty when the report fit fully inline via `content`.",
"title": "Tail",
"type": "string"
},
"line_count": {
"$ref": "#/$defs/Quantity",
"description": "Total line count of the file (dimensionless count, unit '1'). Trailing-newline-only files count their data lines, not an empty terminal line.",
"examples": [
{
"unit": "1",
"value": 1440.0
}
]
},
"byte_count": {
"$ref": "#/$defs/Quantity",
"description": "Total size of the file on disk in bytes (dimensionless count, unit '1'). Captured before any UTF-8 decoding.",
"examples": [
{
"unit": "1",
"value": 87432.0
}
]
},
"truncated": {
"description": "True when the artefact had more than 60 lines under output='summary' and the response carries head + tail rather than the full text. False under output='full' or when the artefact fit fully inline.",
"title": "Truncated",
"type": "boolean"
}
},
"required": [
"name",
"path",
"line_count",
"byte_count",
"truncated"
],
"title": "RawReportContent",
"type": "object"
}
gmat_run_mission¶
Run a single GMAT mission script end-to-end and return structured results (report tables, ephemeris pointers, convergence flags). e.g. gmat_run_mission(script='/abs/path/to/Ex_HohmannTransfer.script') runs a stock GMAT Hohmann transfer sample and returns the ReportFile data inline plus the DifferentialCorrector's converged flag. script must be either an absolute path to a .script file or the full inline script text (auto-detected by leading '%' / 'Create' markers); do not pass a Python Mission object. overrides apply dotted-path field writes (e.g. {'Sat.SMA': 7000.0}) using the same grammar as the GMAT script — Sat.SMA not Sat['SMA'], and the value's Python type must match the field's GMAT type. select_outputs filters which ReportFile / EphemerisFile / ContactLocator outputs appear in the response; leave it null to return every output. output controls row shaping for ReportFile data: the default 'summary' inlines small reports and trims large ones to first/last five rows so the response fits small-model input caps; 'full' returns every row of every selected report. Engine failures (script parse errors, RunScript errors) surface as upstream.gmat_run_ error codes; invalid override paths surface as invalid_input.gmat_override_.
Input schema:
{
"properties": {
"script": {
"description": "Either the absolute path to a GMAT .script file (e.g. '/abs/path/to/Ex_HohmannTransfer.script') or the full inline script text starting with '%' comments or 'Create' resource declarations. Auto-detected by content: a string with newlines or a leading '%' / 'Create ' is inline, anything else is treated as a path. Do not pass a Python Mission object.",
"title": "Script",
"type": "string"
},
"overrides": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Dotted-path field writes applied to the loaded mission before running. Keys use GMAT's script grammar (e.g. 'Sat.SMA', 'FM.Drag.AtmosphereModel', 'Var1.Value'), not subscript form. Values must match the field's GMAT type \u2014 numbers for real / integer fields, booleans for bool fields, strings for filename / enumeration fields. Leave null to skip overrides.",
"title": "Overrides"
},
"select_outputs": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional list of output resource names to include in the response (e.g. ['ReportFile1', 'EphemerisFile1']). Leave null to return every ReportFile / EphemerisFile / ContactLocator the run produced; supply an explicit list to trim the response for a small-context LLM.",
"title": "Select Outputs"
},
"output": {
"default": "summary",
"description": "Row-shaping mode for ReportFile outputs. The default 'summary' inlines small reports (<=20 rows) whole and trims larger ones to first/last five rows so the response fits small-model input caps. 'full' returns every row of every selected report \u2014 pass only when downstream consumers need the dense data and can absorb the bytes. Ephemerides and ContactLocators are always pointer-only regardless of mode (they are intrinsically too large to inline).",
"enum": [
"summary",
"full"
],
"title": "Output",
"type": "string"
},
"timeout_seconds": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional wall-clock cap for this run, in seconds. GMAT runs out-of-process; if the run exceeds the cap the worker is killed and the tool returns upstream.gmat_run_timeout. Leave null to use the server's configured default. Raise it for a heavy optimizer / long propagation, lower it for a quick check; values are clamped to the server's configured maximum and must be positive.",
"title": "Timeout Seconds"
}
},
"required": [
"script"
],
"title": "gmat_run_missionArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"ChildCommandView": {
"additionalProperties": false,
"description": "One direct child of a branch command in :class:`CommandView`.",
"properties": {
"type_name": {
"description": "GMAT command type \u2014 e.g. 'Propagate', 'Maneuver', 'Vary'.",
"title": "Type Name",
"type": "string"
},
"summary": {
"description": "First non-blank line of the command's generating string, truncated. Empty when GMAT returned nothing useful.",
"title": "Summary",
"type": "string"
}
},
"required": [
"type_name",
"summary"
],
"title": "ChildCommandView",
"type": "object"
},
"CommandView": {
"additionalProperties": false,
"description": "One top-level mission-sequence command in :class:`MissionSummaryView`.",
"properties": {
"type_name": {
"description": "GMAT command type \u2014 e.g. 'Propagate', 'Target', 'If', 'Maneuver'.",
"title": "Type Name",
"type": "string"
},
"summary": {
"description": "First non-blank line of the command's generating string, truncated. Empty when GMAT returned nothing useful.",
"title": "Summary",
"type": "string"
},
"children": {
"description": "Direct children of a branch command (Target / Optimize / If / For / While). Empty for non-branch commands.",
"items": {
"$ref": "#/$defs/ChildCommandView"
},
"title": "Children",
"type": "array"
},
"has_deeper_nesting": {
"default": false,
"description": "True when this branch command nests commands beyond the direct children exposed in `children`. Use this to decide whether to drill into the script.",
"title": "Has Deeper Nesting",
"type": "boolean"
}
},
"required": [
"type_name",
"summary"
],
"title": "CommandView",
"type": "object"
},
"MissionSummaryView": {
"additionalProperties": false,
"description": "Structured snapshot of the loaded mission.\n\nMirrors :class:`gmat_run.summary.MissionSummary`: per-category resource\ngroups plus a top-level mission-sequence outline. Field values are not\nmaterialised \u2014 pass overrides at the tool boundary to inspect specific\nfields, or call the GMAT API directly for advanced surfaces.",
"properties": {
"script_name": {
"description": "File name of the loaded script (``Path.name`` of the path Mission.load resolved). When the caller passes inline script text, this is the temp file's name.",
"title": "Script Name",
"type": "string"
},
"resource_groups": {
"description": "Resources grouped by category in display order \u2014 Spacecraft, ForceModel, Propagator, CoordinateSystem, ImpulsiveBurn, FiniteBurn, ReportFile, EphemerisFile, ContactLocator, Solver, Subscriber, Other. Empty categories are omitted.",
"items": {
"$ref": "#/$defs/ResourceGroupView"
},
"title": "Resource Groups",
"type": "array"
},
"commands": {
"description": "Top-level mission-sequence commands in declaration order. Branch commands (Target, Optimize, If, For, While) carry their direct children; deeper nesting is flagged via `has_deeper_nesting`.",
"items": {
"$ref": "#/$defs/CommandView"
},
"title": "Commands",
"type": "array"
}
},
"required": [
"script_name",
"resource_groups",
"commands"
],
"title": "MissionSummaryView",
"type": "object"
},
"OutputPointer": {
"additionalProperties": false,
"description": "Name + path for an EphemerisFile or ContactLocator output.\n\nEphemerides and contact reports are intrinsically too large to inline \u2014\na 24-hour OEM file is several MB. The pointer carries the resource name\nso an LLM can describe the artefact, plus the absolute path for users\nwho want to inspect the file outside the conversation.",
"properties": {
"name": {
"description": "Resource name as declared in the .script (e.g. 'EphemerisFile1').",
"title": "Name",
"type": "string"
},
"path": {
"description": "Absolute path the file landed at. Lives under a registry-owned temp directory until the run is LRU-evicted (configurable via ASTRODYNAMICS_MCP_RUN_REGISTRY_LIMIT). Read the file directly off disk, or pass the resource name to gmat_read_run_artefact for shape-disciplined text inlining.",
"title": "Path",
"type": "string"
}
},
"required": [
"name",
"path"
],
"title": "OutputPointer",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"ReportFileShape": {
"additionalProperties": false,
"description": "Shape-disciplined inline view of one ReportFile output.\n\nIn the default ``output=\"summary\"`` mode, small reports (``row_count\n<= 20``) come back fully inline via ``rows`` while larger reports\npopulate ``head`` (first five rows) and ``tail`` (last five rows) so\nthe response fits small-model input caps regardless of how long the\nrun was. In ``output=\"full\"`` mode the response always carries every\nrow regardless of size; ``head`` / ``tail`` are unused there.\n``truncated`` distinguishes the two cases at read time.",
"properties": {
"name": {
"description": "ReportFile resource name as declared in the .script.",
"title": "Name",
"type": "string"
},
"path": {
"description": "Absolute path the report landed at. Lives under a registry-owned temp directory whose lifetime extends until the run is LRU-evicted, so a follow-up gmat_read_run_artefact call can re-read the file by name; the inline rows below carry the parsed data either way.",
"title": "Path",
"type": "string"
},
"columns": {
"description": "Column headers in the order GMAT wrote them \u2014 e.g. ['Sat.UTCGregorian', 'Sat.X', 'Sat.Y', 'Sat.Z']. The unit per column is carried in the GMAT parameter name itself.",
"items": {
"type": "string"
},
"title": "Columns",
"type": "array"
},
"row_count": {
"$ref": "#/$defs/Quantity",
"description": "Total row count in the report (dimensionless count, unit '1'). When `truncated` is False this equals `len(rows)`; when True it equals `len(head) + len(tail)` plus the dropped middle.",
"examples": [
{
"unit": "1",
"value": 1440.0
}
]
},
"rows": {
"description": "Full report content when row_count <= 20. One dict per row keyed by column name; string for non-numeric columns (e.g. UTCGregorian), float otherwise. Empty when `truncated` is True.",
"items": {
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"type": "object"
},
"title": "Rows",
"type": "array"
},
"head": {
"description": "First five rows when `truncated` is True. Same row shape as `rows`. Empty when the report fit fully inline.",
"items": {
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"type": "object"
},
"title": "Head",
"type": "array"
},
"tail": {
"description": "Last five rows when `truncated` is True. Same row shape as `rows`. Empty when the report fit fully inline.",
"items": {
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"type": "object"
},
"title": "Tail",
"type": "array"
},
"truncated": {
"description": "True when the report had more than 20 rows and the response carries head + tail rather than the full data. The LLM should note the dropped middle window when interpreting trajectory shape.",
"title": "Truncated",
"type": "boolean"
}
},
"required": [
"name",
"path",
"columns",
"row_count",
"truncated"
],
"title": "ReportFileShape",
"type": "object"
},
"ResourceGroupView": {
"additionalProperties": false,
"description": "One resource category in :class:`MissionSummaryView`.\n\nMirrors :class:`gmat_run.summary.ResourceGroup`. Names appear in the\ndeclaration order GMAT returned them from the configuration walk.",
"properties": {
"category": {
"description": "Resource type \u2014 e.g. 'Spacecraft', 'ForceModel', 'Propagator', 'ReportFile'.",
"title": "Category",
"type": "string"
},
"names": {
"description": "Resource names in script-declaration order.",
"items": {
"type": "string"
},
"title": "Names",
"type": "array"
}
},
"required": [
"category",
"names"
],
"title": "ResourceGroupView",
"type": "object"
}
},
"additionalProperties": false,
"description": "Response from :func:`gmat_run_mission`.\n\nCarries a structured mission snapshot, the wall-clock cost of the run,\nshape-disciplined inline content for each selected ReportFile, pointers\nfor the larger ephemeris and contact artefacts, and the per-solver\nconvergence flags GMAT reported.",
"properties": {
"run_id": {
"description": "UUID4 hex identifying this run in the server-process registry. Pass to gmat_read_run_artefact along with an output's resource name to read the raw bytes in a later tool call \u2014 useful when an ephemeris or contact report was too large to inline here. The registry retains the last N runs per process (configurable via ASTRODYNAMICS_MCP_RUN_REGISTRY_LIMIT, default 50); once evicted the id resolves to invalid_input.unknown_run_id.",
"title": "Run Id",
"type": "string"
},
"summary": {
"$ref": "#/$defs/MissionSummaryView",
"description": "Resource and mission-sequence snapshot of the loaded script. Derived from gmat_run.Mission.summary() so the same structure backs both the Python API's __repr__ and this response."
},
"wall_clock": {
"$ref": "#/$defs/Quantity",
"description": "Wall-clock duration of mission.run() in seconds (unit 's'). Does not include script-load time.",
"examples": [
{
"unit": "s",
"value": 0.42
}
]
},
"reports": {
"description": "One :class:`ReportFileShape` per selected ReportFile output. Filtered by the caller's `select_outputs` if provided; otherwise every ReportFile the run wrote is included.",
"items": {
"$ref": "#/$defs/ReportFileShape"
},
"title": "Reports",
"type": "array"
},
"ephemerides": {
"description": "One pointer per selected EphemerisFile output. No inline data \u2014 ephemerides are too large to fit a single tool response.",
"items": {
"$ref": "#/$defs/OutputPointer"
},
"title": "Ephemerides",
"type": "array"
},
"contacts": {
"description": "One pointer per selected ContactLocator output. No inline data \u2014 the contact report formats vary widely and are best inspected on disk.",
"items": {
"$ref": "#/$defs/OutputPointer"
},
"title": "Contacts",
"type": "array"
},
"converged": {
"additionalProperties": {
"type": "boolean"
},
"description": "Per-solver convergence flag keyed by solver resource name (e.g. {'DC': True}). Empty when the mission declares no solvers.",
"title": "Converged",
"type": "object"
}
},
"required": [
"run_id",
"summary",
"wall_clock",
"reports",
"ephemerides",
"contacts",
"converged"
],
"title": "GmatRunMissionResponse",
"type": "object"
}
gmat_sweep¶
Run a parameter sweep, explicit-sample replay, Monte Carlo, or Latin hypercube over a GMAT mission script via the gmat-sweep backend. mode selects which family runs and which payload fields are read. e.g. gmat_sweep(script='/abs/path/to/hohmann.script', mode='grid', grid={'Sat.SMA': [7000, 7100, 7200], 'Sat.INC': [28.5, 51.6]}) runs a 6-point full factorial; gmat_sweep(script=..., mode='samples', samples=[{'Sat.SMA': 7000, 'Sat.INC': 28.5}, {'Sat.SMA': 7100, 'Sat.INC': 51.6}]) runs one mission per explicit row; gmat_sweep(script=..., mode='monte_carlo', perturb={'Sat.SMA': ['normal', 7000, 5.0]}, n=20, seed=42) runs 20 normally-dispersed runs; mode='latin_hypercube' takes the same perturb / n / seed and uses a stratified design. script is the same shape as gmat_run_mission: an absolute .script path or full inline script text. Samples are JSON lists of dotted-path -> value dicts with a stable column order taken from the first row (every row must share the same keys). Perturb values are JSON lists of the form [distribution_name, params] -- ['normal', mu, sigma], ['uniform', lo, hi], or ['lognormal', mu, sigma]; do not pass plain numbers or scipy distributions. For Monte Carlo / Latin hypercube, seed is required for reproducibility -- omitting it falls back to OS entropy and two calls with the same arguments will give different draws. max_workers defaults to 1 to keep the cost ceiling tight; raise it explicitly to parallelise across cores. Output shaping: the default output='summary' returns per-column mean / std / min / max plus the head + tail five rows of the result frame so the response fits small-model input caps. 'full' adds every row in rows. manifest_path and output_dir point at the on-disk sweep artefacts for a follow-up re-load. Engine failures surface as upstream.gmat_sweep_failed; config / payload violations surface as invalid_input.gmat_sweep_.
Input schema:
{
"properties": {
"script": {
"description": "Either the absolute path to a GMAT .script file (e.g. '/abs/path/to/Ex_HohmannTransfer.script') or the full inline script text starting with '%' comments or 'Create' resource declarations. Auto-detected by content \u2014 same shape as the `script` argument of gmat_run_mission.",
"title": "Script",
"type": "string"
},
"mode": {
"description": "Which sweep backend to dispatch and which payload fields to read. 'grid' takes `grid`; 'samples' takes `samples`; 'monte_carlo' and 'latin_hypercube' both take `perturb`, `n`, `seed`. Passing fields not associated with the chosen mode raises invalid_input.gmat_sweep_mode_payload_conflict.",
"enum": [
"grid",
"samples",
"monte_carlo",
"latin_hypercube"
],
"title": "Mode",
"type": "string"
},
"grid": {
"anyOf": [
{
"additionalProperties": {
"items": {},
"type": "array"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Full-factorial sweep parameters, used only when mode='grid'. Keys are dotted-path field names (e.g. 'Sat.SMA', 'FM.Drag.AtmosphereModel'); values are the list of values to sweep on that axis. The run set is the cartesian product across every key. Leave null in other modes.",
"title": "Grid"
},
"samples": {
"anyOf": [
{
"items": {
"additionalProperties": true,
"type": "object"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Explicit-row sweep parameters, used only when mode='samples'. Each list element is one run: a dict from dotted-path field name to value. Every row must carry the same keys; column order is taken from the first row. Leave null in other modes.",
"title": "Samples"
},
"perturb": {
"anyOf": [
{
"additionalProperties": {
"items": {},
"type": "array"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Per-parameter distribution specs, used only when mode='monte_carlo' or 'latin_hypercube'. Each value is a list of the form [distribution_name, *params] \u2014 ['normal', mu, sigma], ['uniform', lo, hi], or ['lognormal', mu, sigma]; plain numbers and scipy distributions are not accepted across the MCP boundary. Leave null in other modes.",
"title": "Perturb"
},
"n": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Number of stochastic runs, used only when mode='monte_carlo' or 'latin_hypercube'. Must be >= 1. Leave null in other modes; the grid mode's run count is derived from the cartesian product of `grid`, and the samples mode uses len(samples).",
"title": "N"
},
"seed": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Integer parent seed, required when mode='monte_carlo' or 'latin_hypercube' so the per-run draws can be reproduced from the response alone. Leave null in other modes.",
"title": "Seed"
},
"max_workers": {
"default": 1,
"description": "Worker count for the local joblib backend. Defaults to 1 to keep the cost ceiling tight; raise it explicitly to parallelise across cores. Custom backends (Dask, Ray, MPI) are not exposed at this MCP boundary \u2014 call gmat-sweep directly for those.",
"title": "Max Workers",
"type": "integer"
},
"output": {
"default": "summary",
"description": "Row-shaping mode for the result frame. The default 'summary' returns per-column mean / std / min / max plus the head + tail five rows of the result frame so the response fits small-model input caps. 'full' adds every row in `rows` \u2014 pass only when downstream consumers need the dense data and can absorb the bytes. `head`, `tail`, and `summary_stats` are always populated regardless of mode.",
"enum": [
"summary",
"full"
],
"title": "Output",
"type": "string"
}
},
"required": [
"script",
"mode"
],
"title": "gmat_sweepArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"SweepColumnStats": {
"additionalProperties": false,
"description": "Per-column summary statistics over ``ok`` rows of a sweep result frame.",
"properties": {
"column": {
"description": "Result-frame column name as written by the per-run ReportFile \u2014 e.g. 'Sat.X', 'Sat.SMA'. The unit per column is carried in the GMAT parameter name itself.",
"title": "Column",
"type": "string"
},
"count": {
"$ref": "#/$defs/Quantity",
"description": "Number of finite values used to compute the stats (dimensionless count, unit '1'). Rows from failed / skipped runs and any NaN cells are excluded.",
"examples": [
{
"unit": "1",
"value": 60.0
}
]
},
"mean": {
"description": "Arithmetic mean over finite values.",
"title": "Mean",
"type": "number"
},
"std": {
"description": "Sample standard deviation (ddof=1) over finite values. NaN when `count` < 2 \u2014 single-row sweeps have no spread to report.",
"title": "Std",
"type": "number"
},
"min": {
"description": "Minimum finite value.",
"title": "Min",
"type": "number"
},
"max": {
"description": "Maximum finite value.",
"title": "Max",
"type": "number"
}
},
"required": [
"column",
"count",
"mean",
"std",
"min",
"max"
],
"title": "SweepColumnStats",
"type": "object"
},
"SweepStatusCounts": {
"additionalProperties": false,
"description": "Run-status tally derived from the result frame's ``__status`` column.",
"properties": {
"ok": {
"$ref": "#/$defs/Quantity",
"description": "Number of runs that completed successfully (count, unit '1').",
"examples": [
{
"unit": "1",
"value": 10.0
}
]
},
"failed": {
"$ref": "#/$defs/Quantity",
"description": "Number of runs the worker reported as failed (count, unit '1'). Each contributes one NaN-filled row to the result frame.",
"examples": [
{
"unit": "1",
"value": 0.0
}
]
},
"skipped": {
"$ref": "#/$defs/Quantity",
"description": "Number of runs the worker reported as skipped (count, unit '1'). Same single-row NaN representation as failed runs.",
"examples": [
{
"unit": "1",
"value": 0.0
}
]
}
},
"required": [
"ok",
"failed",
"skipped"
],
"title": "SweepStatusCounts",
"type": "object"
}
},
"additionalProperties": false,
"description": "Response from :func:`gmat_sweep`.\n\nCarries the ``mode`` echo, sweep-level metadata, per-column summary\nstatistics computed over ``ok`` rows, a status tally, the head + tail\nof the ``(run_id, time)``-MultiIndexed result frame, and pointers to\nthe on-disk manifest and output directory so a follow-up tool call\ncan re-load the full sweep.\n\nIn the default ``output=\"summary\"`` mode ``rows`` is empty and the\nresponse carries only ``head`` + ``tail`` (first / last five rows\neach); ``output=\"full\"`` populates ``rows`` with every result row.\n``truncated`` distinguishes the two cases at read time.",
"properties": {
"run_id": {
"description": "UUID4 hex identifying this sweep in the server-process registry. Pass to gmat_read_run_artefact along with 'manifest.jsonl' or a per-run artefact basename to read the raw bytes in a later tool call. The same retention cap (ASTRODYNAMICS_MCP_RUN_REGISTRY_LIMIT, default 50) covers single-mission runs and sweeps.",
"title": "Run Id",
"type": "string"
},
"mode": {
"description": "Echo of the sweep backend that ran \u2014 matches the `mode` input arg. Stable across the response so a caller can switch on it without re-tracking the request.",
"enum": [
"grid",
"samples",
"monte_carlo",
"latin_hypercube"
],
"title": "Mode",
"type": "string"
},
"script_name": {
"description": "File name of the loaded script (``Path.name`` of the input). When the caller passed inline script text, this is the temp file's name.",
"title": "Script Name",
"type": "string"
},
"run_count": {
"$ref": "#/$defs/Quantity",
"description": "Total runs the sweep dispatched (count, unit '1'). Equals ok + failed + skipped from `status_counts`.",
"examples": [
{
"unit": "1",
"value": 10.0
}
]
},
"wall_clock": {
"$ref": "#/$defs/Quantity",
"description": "Wall-clock duration of the sweep dispatch in seconds (unit 's'). Includes per-run worker overhead but excludes the time to write the response.",
"examples": [
{
"unit": "s",
"value": 12.4
}
]
},
"columns": {
"description": "Non-status data columns of the result frame, in the order GMAT wrote them \u2014 e.g. ['Sat.X', 'Sat.Y', 'Sat.Z', 'Sat.SMA']. The `__status` column is excluded; it is summarised in `status_counts` instead.",
"items": {
"type": "string"
},
"title": "Columns",
"type": "array"
},
"status_counts": {
"$ref": "#/$defs/SweepStatusCounts",
"description": "Per-status run tally (ok / failed / skipped). Failed and skipped runs land as one NaN-filled row apiece in the result frame and are excluded from `summary_stats`."
},
"summary_stats": {
"description": "One :class:`SweepColumnStats` per numeric column in `columns`, computed over the finite cells of ``ok`` rows only. Non-numeric columns (string epochs, categorical fields) are omitted \u2014 they appear in `head` / `tail` / `rows` but not here.",
"items": {
"$ref": "#/$defs/SweepColumnStats"
},
"title": "Summary Stats",
"type": "array"
},
"head": {
"description": "First five rows of the ``(run_id, time)``-indexed result frame, sorted by (run_id, time). Each dict carries 'run_id', 'time', and one entry per column in `columns`; NaN cells become the string 'nan'. Always populated regardless of `output` mode.",
"items": {
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"type": "object"
},
"title": "Head",
"type": "array"
},
"tail": {
"description": "Last five rows of the result frame, sorted the same way. Same row shape as `head`. Always populated regardless of `output` mode; will overlap `head` when the frame has \u2264 10 rows.",
"items": {
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"type": "object"
},
"title": "Tail",
"type": "array"
},
"rows": {
"description": "Every row of the result frame when ``output='full'``. Same row shape as `head` / `tail`. Empty in the default ``output='summary'`` mode \u2014 use `head` + `tail` there.",
"items": {
"additionalProperties": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
]
},
"type": "object"
},
"title": "Rows",
"type": "array"
},
"truncated": {
"description": "True when ``output='summary'`` and the result frame has more than ten rows so `rows` is intentionally empty. False when the full frame fits in `head` + `tail` or when ``output='full'`` populated `rows`.",
"title": "Truncated",
"type": "boolean"
},
"manifest_path": {
"description": "Absolute path to the JSON Lines manifest the sweep wrote. A follow-up call can re-load it via :func:`gmat_sweep.Manifest.load` to walk the per-run outputs that this response summarises.",
"title": "Manifest Path",
"type": "string"
},
"output_dir": {
"description": "Absolute path to the sweep's output directory (parent of `manifest_path` and of every per-run Parquet). Lives for the server process's lifetime; safe to read after the tool call returns.",
"title": "Output Dir",
"type": "string"
}
},
"required": [
"run_id",
"mode",
"script_name",
"run_count",
"wall_clock",
"columns",
"status_counts",
"summary_stats",
"head",
"tail",
"truncated",
"manifest_path",
"output_dir"
],
"title": "GmatSweepResponse",
"type": "object"
}
gmat_validate_script¶
Parse-validate a GMAT mission script without running the mission sequence: load it through GMAT's interpreter, capture any errors and warnings GMAT itself surfaces, and return them alongside the parsed resource and command inventory. Intended for a self-correction loop where an LLM iterates on its script — call gmat_validate_script, fix what GMAT flags, then call gmat_run_mission. e.g. gmat_validate_script(script='/abs/path/to/Ex_HohmannTransfer.script') returns ok=True with a summary listing the declared Spacecraft, ForceModel, Propagator, ReportFile resources and the mission-sequence commands. script must be either an absolute path to a .script file or the full inline script text (auto-detected by leading '%' / 'Create' markers); do not pass a Python Mission object. Common-mistake notes: validate confirms the script parses, not that the mission runs end-to-end — solver convergence and runtime errors still need gmat_run_mission. GMAT resource names are case-sensitive: 'Spacecraft sat' and 'Spacecraft Sat' declare different objects (keyword fields are more forgiving but should match the script grammar). Missing statement terminators (semicolons) are a known false-negative — GMAT's interpreter accepts them silently, so ok=True does not guarantee a syntactically pristine script, only that the interpreter could build the object graph. Output fields carry no physical units (the tool is structural, not numeric).
Input schema:
{
"properties": {
"script": {
"description": "Either the absolute path to a GMAT .script file (e.g. '/abs/path/to/Ex_HohmannTransfer.script') or the full inline script text starting with '%' comments or 'Create' resource declarations. Auto-detected by content \u2014 same shape as the `script` argument of gmat_run_mission.",
"title": "Script",
"type": "string"
}
},
"required": [
"script"
],
"title": "gmat_validate_scriptArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"ChildCommandView": {
"additionalProperties": false,
"description": "One direct child of a branch command in :class:`CommandView`.",
"properties": {
"type_name": {
"description": "GMAT command type \u2014 e.g. 'Propagate', 'Maneuver', 'Vary'.",
"title": "Type Name",
"type": "string"
},
"summary": {
"description": "First non-blank line of the command's generating string, truncated. Empty when GMAT returned nothing useful.",
"title": "Summary",
"type": "string"
}
},
"required": [
"type_name",
"summary"
],
"title": "ChildCommandView",
"type": "object"
},
"CommandView": {
"additionalProperties": false,
"description": "One top-level mission-sequence command in :class:`MissionSummaryView`.",
"properties": {
"type_name": {
"description": "GMAT command type \u2014 e.g. 'Propagate', 'Target', 'If', 'Maneuver'.",
"title": "Type Name",
"type": "string"
},
"summary": {
"description": "First non-blank line of the command's generating string, truncated. Empty when GMAT returned nothing useful.",
"title": "Summary",
"type": "string"
},
"children": {
"description": "Direct children of a branch command (Target / Optimize / If / For / While). Empty for non-branch commands.",
"items": {
"$ref": "#/$defs/ChildCommandView"
},
"title": "Children",
"type": "array"
},
"has_deeper_nesting": {
"default": false,
"description": "True when this branch command nests commands beyond the direct children exposed in `children`. Use this to decide whether to drill into the script.",
"title": "Has Deeper Nesting",
"type": "boolean"
}
},
"required": [
"type_name",
"summary"
],
"title": "CommandView",
"type": "object"
},
"MissionSummaryView": {
"additionalProperties": false,
"description": "Structured snapshot of the loaded mission.\n\nMirrors :class:`gmat_run.summary.MissionSummary`: per-category resource\ngroups plus a top-level mission-sequence outline. Field values are not\nmaterialised \u2014 pass overrides at the tool boundary to inspect specific\nfields, or call the GMAT API directly for advanced surfaces.",
"properties": {
"script_name": {
"description": "File name of the loaded script (``Path.name`` of the path Mission.load resolved). When the caller passes inline script text, this is the temp file's name.",
"title": "Script Name",
"type": "string"
},
"resource_groups": {
"description": "Resources grouped by category in display order \u2014 Spacecraft, ForceModel, Propagator, CoordinateSystem, ImpulsiveBurn, FiniteBurn, ReportFile, EphemerisFile, ContactLocator, Solver, Subscriber, Other. Empty categories are omitted.",
"items": {
"$ref": "#/$defs/ResourceGroupView"
},
"title": "Resource Groups",
"type": "array"
},
"commands": {
"description": "Top-level mission-sequence commands in declaration order. Branch commands (Target, Optimize, If, For, While) carry their direct children; deeper nesting is flagged via `has_deeper_nesting`.",
"items": {
"$ref": "#/$defs/CommandView"
},
"title": "Commands",
"type": "array"
}
},
"required": [
"script_name",
"resource_groups",
"commands"
],
"title": "MissionSummaryView",
"type": "object"
},
"ParseDiagnostic": {
"additionalProperties": false,
"description": "One error or warning extracted from GMAT's load-time log.\n\nShared shape across :attr:`GmatValidateScriptResponse.errors` and\n:attr:`GmatValidateScriptResponse.warnings` \u2014 both surfaces carry the\nsame ``{line, message, raw}`` triple, classified by which GMAT marker\nproduced them (``**** ERROR **** Interpreter Exception:`` \u2192 error;\n``*** WARNING ***`` \u2192 warning).",
"properties": {
"line": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"description": "Line number in the source script GMAT attributed the diagnostic to. Populated when GMAT's message carries an ``in line: \"<N>: ...\"`` trailing context; null for cross-resource reference errors and warnings that don't pin a single line.",
"title": "Line"
},
"message": {
"description": "Cleaned diagnostic text \u2014 the substantive portion of GMAT's message with the script-path prefix and the ``in line:`` continuation stripped. Action-targetable; e.g. 'The field name \"WidgetCount\" on object \"Sat\" is not permitted'.",
"title": "Message",
"type": "string"
},
"raw": {
"description": "Original log line GMAT emitted, verbatim. Fallback when the scraper trims information the caller needs (e.g. the full multi-marker line for non-ASCII errors).",
"title": "Raw",
"type": "string"
}
},
"required": [
"line",
"message",
"raw"
],
"title": "ParseDiagnostic",
"type": "object"
},
"ResourceGroupView": {
"additionalProperties": false,
"description": "One resource category in :class:`MissionSummaryView`.\n\nMirrors :class:`gmat_run.summary.ResourceGroup`. Names appear in the\ndeclaration order GMAT returned them from the configuration walk.",
"properties": {
"category": {
"description": "Resource type \u2014 e.g. 'Spacecraft', 'ForceModel', 'Propagator', 'ReportFile'.",
"title": "Category",
"type": "string"
},
"names": {
"description": "Resource names in script-declaration order.",
"items": {
"type": "string"
},
"title": "Names",
"type": "array"
}
},
"required": [
"category",
"names"
],
"title": "ResourceGroupView",
"type": "object"
}
},
"additionalProperties": false,
"description": "Response from :func:`gmat_validate_script`.\n\nCarries GMAT's view of the script: a parse-success boolean, error and\nwarning lists scraped from the engine's log, the resource and command\ninventory the parser built on success, and the captured log verbatim\nas a fallback. No numeric fields \u2014 the tool is structural, not\nphysical, so the response is deliberately exempt from the cross-tool\n``{value, unit}`` unit discipline.",
"properties": {
"ok": {
"description": "True when GMAT's interpreter loaded the script (LoadScript returned True) and the post-load Spacecraft initialisation didn't raise. False on any parse-time error. Known false-negative: GMAT's interpreter silently accepts missing statement terminators (semicolons), so ok=True means the engine built an object graph, not that the script is syntactically pristine.",
"title": "Ok",
"type": "boolean"
},
"errors": {
"description": "Parse-time errors GMAT surfaced \u2014 ``**** ERROR **** Interpreter Exception:`` lines plus any Spacecraft-initialisation APIException. Empty when ok=True.",
"items": {
"$ref": "#/$defs/ParseDiagnostic"
},
"title": "Errors",
"type": "array"
},
"warnings": {
"description": "Parse-time warnings GMAT surfaced \u2014 ``*** WARNING ***`` lines. Independent of `ok`: GMAT can warn (e.g. missing BeginMissionSequence) while still loading the script successfully.",
"items": {
"$ref": "#/$defs/ParseDiagnostic"
},
"title": "Warnings",
"type": "array"
},
"summary": {
"anyOf": [
{
"$ref": "#/$defs/MissionSummaryView"
},
{
"type": "null"
}
],
"description": "Structured snapshot of what GMAT parsed \u2014 resource categories and the mission-sequence command outline. Populated on ok=True so the caller can confirm the parser saw what it intended to declare. Null on ok=False, since the moderator's state after a failed load is indeterminate."
},
"raw_log": {
"description": "Verbatim GMAT log captured across the LoadScript call. Includes build-date headers, ephemeris-source notices, and the error / warning lines the structured fields are scraped from. Read this when the structured fields miss something \u2014 the scraper trades robustness for tidiness.",
"title": "Raw Log",
"type": "string"
}
},
"required": [
"ok",
"errors",
"warnings",
"summary",
"raw_log"
],
"title": "GmatValidateScriptResponse",
"type": "object"
}
lambert_solve¶
Solve Lambert's problem — find the orbital transfer between two position vectors r1 and r2 in a given time-of-flight tof. e.g. lambert_solve(r1=[5000, 10000, 2100], r2=[-14600, 2500, 7000], tof=3600, mu='earth') returns the initial and final inertial velocities on the transfer arc plus the arc's classical orbital elements. mu selects the central body and is REQUIRED — there is no default, because the wrong choice silently produces garbage. r1 and r2 are heliocentric km when mu='sun'; geocentric km when mu='earth'; planetocentric km for other named bodies. If your problem is interplanetary (e.g. Earth-to-Mars transfer), use mu='sun' and pull body positions via the porkchop tool. Algorithm: izzo is fast and robust for the common case; switch to gooding if izzo fails on near-degenerate geometries. For revs > 0 the response enumerates the multi-rev solutions in all_solutions (low + high path per rev count), and the top-level v1/v2/dv then describe the highest-rev arc you asked for, not the direct transfer — read the M=0 entry in all_solutions for the direct transfer. Supply depart_velocity AND arrive_velocity together to get the two-impulse Δv. Degenerate geometries (r1 == r2, infeasible tof, no convergence) surface as upstream.lambert_no_solution.
Input schema:
{
"properties": {
"r1": {
"description": "Departure position vector (km), 3-component [x, y, z] in the inertial frame of the central body identified by `mu`.",
"items": {
"type": "number"
},
"title": "R1",
"type": "array"
},
"r2": {
"description": "Arrival position vector (km), same frame and units as `r1`. The solver finds the transfer arc connecting `r1` to `r2` in `tof` seconds.",
"items": {
"type": "number"
},
"title": "R2",
"type": "array"
},
"tof": {
"description": "Time of flight from `r1` to `r2`, in seconds. Must be strictly positive and finite.",
"title": "Tof",
"type": "number"
},
"mu": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
],
"description": "Gravitational parameter of the central body \u2014 REQUIRED, no default. Pass a body name ('sun', 'mercury', 'venus', 'earth', 'moon', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune') to use the JPL-published \u03bc, or a raw number in km\u00b3/s\u00b2 for a custom value. Must match the frame `r1`/`r2` are expressed in: heliocentric \u2192 'sun', geocentric \u2192 'earth', planetocentric \u2192 that planet.",
"title": "Mu"
},
"direction": {
"default": "prograde",
"description": "Sense of motion along the transfer arc. 'prograde' is the common case (eastward in Earth orbit, counter-clockwise viewed from the +Z pole); 'retrograde' flips the direction.",
"enum": [
"prograde",
"retrograde"
],
"title": "Direction",
"type": "string"
},
"revs": {
"default": 0,
"description": "Maximum number of complete revolutions to enumerate. 0 (default) returns only the zero-rev / direct-transfer solution. For revs \u2265 1 both low_path branches are enumerated; the primary v1/v2 echo the (revs, low_path=True) solution \u2014 i.e. the highest-rev arc, NOT the direct transfer \u2014 and all_solutions lists every feasible alternative (the direct transfer is the M=0 entry there).",
"title": "Revs",
"type": "integer"
},
"algorithm": {
"default": "izzo",
"description": "Lambert solver. 'izzo' (default) has the broadest convergence basin; 'izzo_revisited', 'gooding', and 'battin' are alternative implementations from `lamberthub` exposed for cross-validation.",
"enum": [
"izzo",
"izzo_revisited",
"gooding",
"battin"
],
"title": "Algorithm",
"type": "string"
},
"depart_velocity": {
"anyOf": [
{
"items": {
"type": "number"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional departure-state velocity (km/s), 3-component, in the same frame as `r1`. When supplied alongside `arrive_velocity`, the tool also returns the two-impulse \u0394v |v1 - depart_velocity| + |v2 - arrive_velocity|. Pass both or neither.",
"title": "Depart Velocity"
},
"arrive_velocity": {
"anyOf": [
{
"items": {
"type": "number"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional arrival-state velocity (km/s), 3-component, in the same frame as `r2`. Required together with `depart_velocity` to compute the two-impulse \u0394v; omit both to skip \u0394v computation.",
"title": "Arrive Velocity"
}
},
"required": [
"r1",
"r2",
"tof",
"mu"
],
"title": "lambert_solveArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"KeplerianElements": {
"additionalProperties": false,
"description": "Classical Keplerian orbital elements (a, e, i, RAAN, argp, nu).\n\nShared across any tool that emits an orbit's elements \u2014 Lambert solve\n(transfer arc), porkchop (transfer-elements grid), bplane (hyperbolic\napproach orbit). True anomaly ``nu`` is at the reference epoch (e.g.\nthe start of the Lambert transfer for ``lambert_solve``).\n\nSemi-major axis ``a`` is negative for hyperbolic orbits and undefined\nfor parabolic ones \u2014 callers that need to handle the parabolic edge\nshould branch on eccentricity rather than ``a``.",
"properties": {
"a": {
"$ref": "#/$defs/Quantity",
"description": "Semi-major axis. Length unit (km / m / AU).",
"examples": [
{
"unit": "km",
"value": 24371.0
}
]
},
"e": {
"$ref": "#/$defs/Quantity",
"description": "Eccentricity (dimensionless; unit must be the dimensionless '1').",
"examples": [
{
"unit": "1",
"value": 0.7
}
]
},
"i": {
"$ref": "#/$defs/Quantity",
"description": "Inclination, deg.",
"examples": [
{
"unit": "deg",
"value": 28.5
}
]
},
"raan": {
"$ref": "#/$defs/Quantity",
"description": "Right ascension of the ascending node, deg.",
"examples": [
{
"unit": "deg",
"value": 45.0
}
]
},
"argp": {
"$ref": "#/$defs/Quantity",
"description": "Argument of periapsis, deg.",
"examples": [
{
"unit": "deg",
"value": 90.0
}
]
},
"nu": {
"$ref": "#/$defs/Quantity",
"description": "True anomaly at the reference epoch, deg.",
"examples": [
{
"unit": "deg",
"value": 30.0
}
]
}
},
"required": [
"a",
"e",
"i",
"raan",
"argp",
"nu"
],
"title": "KeplerianElements",
"type": "object"
},
"LambertSolution": {
"additionalProperties": false,
"description": "One Lambert solution row inside :class:`LambertSolveResponse.all_solutions`.",
"properties": {
"v1": {
"$ref": "#/$defs/QuantityVector",
"description": "Initial velocity vector on the transfer arc (km/s)."
},
"v2": {
"$ref": "#/$defs/QuantityVector",
"description": "Final velocity vector on the transfer arc (km/s)."
},
"transfer_elements": {
"$ref": "#/$defs/KeplerianElements",
"description": "Classical orbital elements of the transfer arc."
},
"revs": {
"$ref": "#/$defs/Quantity",
"description": "Revolution count M, dimensionless. e.g. {value: 2, unit: '1'}.",
"examples": [
{
"unit": "1",
"value": 0
},
{
"unit": "1",
"value": 2
}
]
},
"low_path": {
"description": "Which of the two multi-rev branches this solution is. True for the low-path branch (always True for the degenerate M=0 case).",
"title": "Low Path",
"type": "boolean"
}
},
"required": [
"v1",
"v2",
"transfer_elements",
"revs",
"low_path"
],
"title": "LambertSolution",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
}
},
"additionalProperties": false,
"description": "Response from :func:`lambert_solve`.\n\nTop-level ``v1`` / ``v2`` / ``transfer_elements`` / ``dv`` echo the\nuser-requested ``(revs, low_path=True)`` solution for ergonomics \u2014\ni.e. the ``M = revs`` arc. With the default ``revs=0`` that is the direct\ntransfer; for ``revs > 0`` it is the highest-rev arc (usually a much\nlarger \u0394v), and the direct transfer is the ``M=0`` entry in\n``all_solutions``, which lists every feasible (M, low_path) pair from M=0\nup to the requested revolution count.",
"properties": {
"v1": {
"$ref": "#/$defs/QuantityVector",
"description": "Initial velocity for the primary solution (km/s)."
},
"v2": {
"$ref": "#/$defs/QuantityVector",
"description": "Final velocity for the primary solution (km/s)."
},
"transfer_elements": {
"$ref": "#/$defs/KeplerianElements",
"description": "Orbital elements of the primary solution's transfer arc."
},
"dv": {
"anyOf": [
{
"$ref": "#/$defs/Quantity"
},
{
"type": "null"
}
],
"default": null,
"description": "Two-impulse \u0394v (km/s) when both `depart_velocity` and `arrive_velocity` were supplied. None otherwise."
},
"all_solutions": {
"description": "Every feasible (M, low_path) Lambert solution from M=0 to the requested revolution count. Infeasible alternatives are skipped, not surfaced as errors.",
"items": {
"$ref": "#/$defs/LambertSolution"
},
"title": "All Solutions",
"type": "array"
}
},
"required": [
"v1",
"v2",
"transfer_elements",
"all_solutions"
],
"title": "LambertSolveResponse",
"type": "object"
}
plot_ground_track¶
Render a satellite's sub-satellite ground track as a PNG over a lon/lat graticule, with an inline summary (revolutions, lat / lon extent) carried alongside the image. e.g. plot_ground_track(states=
Input schema:
{
"$defs": {
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"NamedStation": {
"additionalProperties": false,
"description": "An observer identified by a name from the v0.1 station registry.",
"properties": {
"name": {
"description": "Short name of a known ground station. Resolved to lat/lon/alt inside the consuming tool. The v0.1 registry is intentionally small; pass explicit ObserverCoordinates for anything else.",
"enum": [
"madrid",
"goldstone",
"canberra",
"svalbard",
"wallops",
"esrange",
"gsfc",
"jpl"
],
"examples": [
"madrid",
"goldstone"
],
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "NamedStation",
"type": "object"
},
"ObserverCoordinates": {
"additionalProperties": false,
"description": "An observer specified by explicit geodetic coordinates.",
"properties": {
"lat": {
"$ref": "#/$defs/Quantity",
"description": "Geodetic latitude in degrees.",
"examples": [
{
"unit": "deg",
"value": 40.4168
}
]
},
"lon": {
"$ref": "#/$defs/Quantity",
"description": "Geodetic longitude in degrees (east-positive).",
"examples": [
{
"unit": "deg",
"value": -3.7038
}
]
},
"alt": {
"$ref": "#/$defs/Quantity",
"description": "Altitude above the WGS-84 ellipsoid in km.",
"examples": [
{
"unit": "km",
"value": 0.667
}
]
}
},
"required": [
"lat",
"lon",
"alt"
],
"title": "ObserverCoordinates",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"StateVector": {
"additionalProperties": false,
"description": "Cartesian state in a named frame at a named epoch.\n\nThe position and velocity are :class:`QuantityVector` instances so the\nunit (km vs m, km/s vs m/s) is on the wire \u2014 never implicit.",
"properties": {
"r": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position vector [x, y, z]. Unit must be a length (km / m / AU).",
"examples": [
{
"unit": "km",
"value": [
7000.0,
0.0,
0.0
]
}
]
},
"v": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian velocity vector [vx, vy, vz]. Unit must be a velocity (km/s / m/s).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
7.5,
0.0
]
}
]
},
"frame": {
"$ref": "#/$defs/Frame",
"description": "Reference frame in which r and v are expressed.",
"examples": [
"TEME",
"ICRF"
]
},
"epoch": {
"description": "UTC ISO 8601 epoch at which the state is valid.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
}
},
"required": [
"r",
"v",
"frame",
"epoch"
],
"title": "StateVector",
"type": "object"
}
},
"properties": {
"states": {
"description": "The state SERIES to trace, e.g. the `states` list from an sgp4_propagate(frame='ITRS', output='full') call. Each entry is a {r, v, frame, epoch}; the track needs many epochs (at least two). Earth frames only (TEME / ICRF / GCRS / CIRS / ITRS).",
"items": {
"$ref": "#/$defs/StateVector"
},
"title": "States",
"type": "array"
},
"stations": {
"anyOf": [
{
"items": {
"anyOf": [
{
"$ref": "#/$defs/NamedStation"
},
{
"$ref": "#/$defs/ObserverCoordinates"
}
],
"description": "Observer location. Either a named station (`{\"name\": \"madrid\"}`) or explicit coordinates (`{\"lat\": {...}, \"lon\": {...}, \"alt\": {...}}`)."
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional ground stations to overlay as markers \u2014 either named stations ({name: 'madrid'}) or explicit coordinates ({lat, lon, alt}). Omit for none.",
"title": "Stations"
}
},
"required": [
"states"
],
"title": "plot_ground_trackArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"PngImageInfo": {
"additionalProperties": false,
"description": "Pixel dimensions of the attached PNG.\n\nThese are rendering *cardinalities*, not physical quantities, so they sit\noutside the ``{value, unit}`` envelope and are declared exempt where the\nunit-discipline meta-test polices the attachment-bearing schemas. The image\nbytes themselves ride as a separate ``ImageContent`` block, not in this\nsummary.",
"properties": {
"width_px": {
"description": "PNG width in pixels (a rendering cardinality).",
"title": "Width Px",
"type": "integer"
},
"height_px": {
"description": "PNG height in pixels (a rendering cardinality).",
"title": "Height Px",
"type": "integer"
},
"format": {
"const": "png",
"default": "png",
"description": "Attachment image format; always 'png' for the static-plot tools.",
"title": "Format",
"type": "string"
}
},
"required": [
"width_px",
"height_px"
],
"title": "PngImageInfo",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "Structured summary accompanying a ``plot_ground_track`` PNG.\n\nThe PNG is additive: this summary is always present and is what a text-only\nclient reads. Every physical field carries an explicit unit; ``revolutions``\nis dimensionless (unit ``\"1\"``).",
"properties": {
"revolutions": {
"$ref": "#/$defs/Quantity",
"description": "Number of orbital revolutions the track spans, derived as equator crossings / 2. Dimensionless (unit '1').",
"examples": [
{
"unit": "1",
"value": 15.5
}
]
},
"lat_min": {
"$ref": "#/$defs/Quantity",
"description": "Minimum sub-satellite geodetic latitude, deg.",
"examples": [
{
"unit": "deg",
"value": -51.6
}
]
},
"lat_max": {
"$ref": "#/$defs/Quantity",
"description": "Maximum sub-satellite geodetic latitude, deg.",
"examples": [
{
"unit": "deg",
"value": 51.6
}
]
},
"lon_min": {
"$ref": "#/$defs/Quantity",
"description": "Minimum sub-satellite longitude (east-positive), deg.",
"examples": [
{
"unit": "deg",
"value": -179.4
}
]
},
"lon_max": {
"$ref": "#/$defs/Quantity",
"description": "Maximum sub-satellite longitude (east-positive), deg.",
"examples": [
{
"unit": "deg",
"value": 178.9
}
]
},
"image": {
"$ref": "#/$defs/PngImageInfo",
"description": "Pixel dimensions of the attached PNG."
}
},
"required": [
"revolutions",
"lat_min",
"lat_max",
"lon_min",
"lon_max",
"image"
],
"title": "GroundTrackResponse",
"type": "object"
}
plot_porkchop¶
Render a porkchop C3 contour as a PNG from an existing porkchop grid result, reusing the computed grid with no recompute, and keep the inline grid summary (best cell C3 / total delta-v / epochs). e.g. plot_porkchop(porkchop_result=
Input schema:
{
"$defs": {
"PorkchopCell": {
"additionalProperties": false,
"description": "One (depart_epoch, arrive_epoch) cell of the porkchop grid.",
"properties": {
"depart_epoch": {
"description": "UTC ISO 8601 departure epoch for this cell.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Depart Epoch",
"type": "string"
},
"arrive_epoch": {
"description": "UTC ISO 8601 arrival epoch for this cell.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Arrive Epoch",
"type": "string"
},
"tof": {
"$ref": "#/$defs/Quantity",
"description": "Time of flight, days.",
"examples": [
{
"unit": "days",
"value": 180.0
}
]
},
"c3": {
"$ref": "#/$defs/Quantity",
"description": "Departure characteristic energy, km^2/s^2. C3 = |v_inf_dep|^2; the launch vehicle's required excess energy above the departure body's escape velocity.",
"examples": [
{
"unit": "km^2/s^2",
"value": 12.5
}
]
},
"v_inf_arrival": {
"$ref": "#/$defs/Quantity",
"description": "Magnitude of arrival V-infinity (hyperbolic excess speed at arrival), km/s.",
"examples": [
{
"unit": "km/s",
"value": 3.0
}
]
},
"dec_dep_asymptote": {
"$ref": "#/$defs/Quantity",
"description": "Declination of the departure asymptote (DLA) in the ICRF equatorial frame, deg \u2014 the v_infinity direction rotated from the ecliptic working frame by the J2000 obliquity. This is the equatorial declination launch vehicles target, not ecliptic latitude.",
"examples": [
{
"unit": "deg",
"value": -12.0
}
]
},
"total_dv": {
"$ref": "#/$defs/Quantity",
"description": "Two-impulse \u0394v proxy = |v_inf_dep| + |v_inf_arr|, km/s. Both legs treated as hyperbolic injections; mission-specific maneuver design lives downstream.",
"examples": [
{
"unit": "km/s",
"value": 6.4
}
]
}
},
"required": [
"depart_epoch",
"arrive_epoch",
"tof",
"c3",
"v_inf_arrival",
"dec_dep_asymptote",
"total_dv"
],
"title": "PorkchopCell",
"type": "object"
},
"PorkchopResponse": {
"additionalProperties": false,
"description": "Response from :func:`porkchop`.\n\nThe shape is the same regardless of ``output`` \u2014 the parameter only\nselects how much of the grid travels back to the caller:\n\n- ``output=\"summary\"`` (default): ``grid`` is empty; ``top_cells``\n carries up to five lowest-``total_dv`` cells so the response fits\n small-model input caps.\n- ``output=\"full\"``: ``grid`` carries every feasible cell in row-major\n order (outer: arrive-epoch, inner: depart-epoch); ``top_cells`` is\n still populated for the canonical \"show me the alternatives\" case.\n\nInfeasible cells (``tof <= 0``, Lambert no-solution) are skipped\nsilently rather than carried as NaN \u2014 the ASCII summary marks them\nwith a space glyph for the visual.",
"properties": {
"best": {
"$ref": "#/$defs/PorkchopCell",
"description": "The feasible cell with the minimum total_dv across the scan."
},
"top_cells": {
"description": "Up to five lowest-total_dv cells, sorted ascending. Always includes `best` as the first entry; size is capped to keep the default response under small-model input limits.",
"items": {
"$ref": "#/$defs/PorkchopCell"
},
"title": "Top Cells",
"type": "array"
},
"grid": {
"description": "Every feasible cell in row-major order (outer: arrive-epoch, inner: depart-epoch). Populated only when the caller passes output='full'; empty otherwise.",
"items": {
"$ref": "#/$defs/PorkchopCell"
},
"title": "Grid",
"type": "array"
},
"ascii_summary": {
"description": "Compact text contour of C3 over the grid. samples_per_axis rows x samples_per_axis columns; each glyph is one of `.:-+*#@X` binned by C3 decile, infeasible cells rendered as a space. Rows are arrive-epoch indices (top = earliest), columns depart-epoch indices (left = earliest).",
"title": "Ascii Summary",
"type": "string"
}
},
"required": [
"best",
"top_cells",
"ascii_summary"
],
"title": "PorkchopResponse",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"properties": {
"porkchop_result": {
"$ref": "#/$defs/PorkchopResponse",
"description": "A porkchop tool result carrying the FULL grid \u2014 call porkchop with output='full' and pass that object back here. The grid's feasible cells (each tagged with depart / arrive epoch and C3) are contoured; the 'best' cell is marked. A summary-only result (empty grid) is rejected with a typed error."
}
},
"required": [
"porkchop_result"
],
"title": "plot_porkchopArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"PngImageInfo": {
"additionalProperties": false,
"description": "Pixel dimensions of the attached PNG.\n\nThese are rendering *cardinalities*, not physical quantities, so they sit\noutside the ``{value, unit}`` envelope and are declared exempt where the\nunit-discipline meta-test polices the attachment-bearing schemas. The image\nbytes themselves ride as a separate ``ImageContent`` block, not in this\nsummary.",
"properties": {
"width_px": {
"description": "PNG width in pixels (a rendering cardinality).",
"title": "Width Px",
"type": "integer"
},
"height_px": {
"description": "PNG height in pixels (a rendering cardinality).",
"title": "Height Px",
"type": "integer"
},
"format": {
"const": "png",
"default": "png",
"description": "Attachment image format; always 'png' for the static-plot tools.",
"title": "Format",
"type": "string"
}
},
"required": [
"width_px",
"height_px"
],
"title": "PngImageInfo",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "Structured summary accompanying a ``plot_porkchop`` PNG.\n\nEchoes the minimum-\u0394v 'best' cell of the supplied porkchop grid and the\ngrid's shape. ``feasible_cells`` / ``n_depart_samples`` / ``n_arrive_samples``\nare grid cardinalities (counts, not physical quantities) and sit outside the\n``{value, unit}`` envelope.",
"properties": {
"best_c3": {
"$ref": "#/$defs/Quantity",
"description": "Departure C3 of the minimum-total_dv cell, km^2/s^2.",
"examples": [
{
"unit": "km^2/s^2",
"value": 12.5
}
]
},
"best_total_dv": {
"$ref": "#/$defs/Quantity",
"description": "Two-impulse total-\u0394v proxy of the best cell, km/s.",
"examples": [
{
"unit": "km/s",
"value": 6.4
}
]
},
"best_tof": {
"$ref": "#/$defs/Quantity",
"description": "Time of flight of the best cell, days.",
"examples": [
{
"unit": "days",
"value": 210.0
}
]
},
"best_depart_epoch": {
"description": "Departure epoch of the best cell (UTC ISO 8601).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Best Depart Epoch",
"type": "string"
},
"best_arrive_epoch": {
"description": "Arrival epoch of the best cell (UTC ISO 8601).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Best Arrive Epoch",
"type": "string"
},
"feasible_cells": {
"description": "Number of feasible cells in the supplied grid (a cardinality).",
"title": "Feasible Cells",
"type": "integer"
},
"n_depart_samples": {
"description": "Distinct departure epochs across the grid (a cardinality).",
"title": "N Depart Samples",
"type": "integer"
},
"n_arrive_samples": {
"description": "Distinct arrival epochs across the grid (a cardinality).",
"title": "N Arrive Samples",
"type": "integer"
},
"image": {
"$ref": "#/$defs/PngImageInfo",
"description": "Pixel dimensions of the attached PNG."
}
},
"required": [
"best_c3",
"best_total_dv",
"best_tof",
"best_depart_epoch",
"best_arrive_epoch",
"feasible_cells",
"n_depart_samples",
"n_arrive_samples",
"image"
],
"title": "PorkchopPlotResponse",
"type": "object"
}
plot_trajectory¶
Render an orbit or transfer trajectory as a 2D or 3D PNG about a central body, with an inline summary (arc length, periapsis / apoapsis, time span) carried alongside the image. e.g. plot_trajectory(states=
Input schema:
{
"$defs": {
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"StateVector": {
"additionalProperties": false,
"description": "Cartesian state in a named frame at a named epoch.\n\nThe position and velocity are :class:`QuantityVector` instances so the\nunit (km vs m, km/s vs m/s) is on the wire \u2014 never implicit.",
"properties": {
"r": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position vector [x, y, z]. Unit must be a length (km / m / AU).",
"examples": [
{
"unit": "km",
"value": [
7000.0,
0.0,
0.0
]
}
]
},
"v": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian velocity vector [vx, vy, vz]. Unit must be a velocity (km/s / m/s).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
7.5,
0.0
]
}
]
},
"frame": {
"$ref": "#/$defs/Frame",
"description": "Reference frame in which r and v are expressed.",
"examples": [
"TEME",
"ICRF"
]
},
"epoch": {
"description": "UTC ISO 8601 epoch at which the state is valid.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
}
},
"required": [
"r",
"v",
"frame",
"epoch"
],
"title": "StateVector",
"type": "object"
}
},
"properties": {
"states": {
"description": "The state SERIES forming the orbit / transfer arc, e.g. the `states` from an sgp4_propagate or lambert/porkchop-derived propagation. Each entry is a {r, v, frame, epoch}; at least two states are required. Positions are plotted in the states' frame.",
"items": {
"$ref": "#/$defs/StateVector"
},
"title": "States",
"type": "array"
},
"projection": {
"default": "2D",
"description": "Plot projection: '2D' (x-y plane, default) or '3D' (x-y-z with a fixed viewing angle).",
"enum": [
"2D",
"3D"
],
"title": "Projection",
"type": "string"
},
"central_body": {
"default": "earth",
"description": "Central body drawn at the origin, e.g. 'earth' (default), 'mars', 'moon', 'sun'. In a 2D plot known bodies are drawn to scale; in 3D the origin is marked (no to-scale sphere). An unknown name still plots, with the origin marked.",
"title": "Central Body",
"type": "string"
}
},
"required": [
"states"
],
"title": "plot_trajectoryArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"PngImageInfo": {
"additionalProperties": false,
"description": "Pixel dimensions of the attached PNG.\n\nThese are rendering *cardinalities*, not physical quantities, so they sit\noutside the ``{value, unit}`` envelope and are declared exempt where the\nunit-discipline meta-test polices the attachment-bearing schemas. The image\nbytes themselves ride as a separate ``ImageContent`` block, not in this\nsummary.",
"properties": {
"width_px": {
"description": "PNG width in pixels (a rendering cardinality).",
"title": "Width Px",
"type": "integer"
},
"height_px": {
"description": "PNG height in pixels (a rendering cardinality).",
"title": "Height Px",
"type": "integer"
},
"format": {
"const": "png",
"default": "png",
"description": "Attachment image format; always 'png' for the static-plot tools.",
"title": "Format",
"type": "string"
}
},
"required": [
"width_px",
"height_px"
],
"title": "PngImageInfo",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "Structured summary accompanying a ``plot_trajectory`` PNG.",
"properties": {
"arc_length": {
"$ref": "#/$defs/Quantity",
"description": "Path length of the plotted state series, summed over straight segments between consecutive states, km.",
"examples": [
{
"unit": "km",
"value": 41600.0
}
]
},
"periapsis_radius": {
"$ref": "#/$defs/Quantity",
"description": "Minimum position magnitude |r| over the series, km.",
"examples": [
{
"unit": "km",
"value": 6678.0
}
]
},
"apoapsis_radius": {
"$ref": "#/$defs/Quantity",
"description": "Maximum position magnitude |r| over the series, km.",
"examples": [
{
"unit": "km",
"value": 42164.0
}
]
},
"time_span": {
"$ref": "#/$defs/Quantity",
"description": "Wall-clock span from the first to the last state epoch, hours.",
"examples": [
{
"unit": "hours",
"value": 5.27
}
]
},
"projection": {
"description": "Projection the PNG was rendered with.",
"enum": [
"2D",
"3D"
],
"title": "Projection",
"type": "string"
},
"central_body": {
"description": "Central body the trajectory was drawn about (echo of the input).",
"title": "Central Body",
"type": "string"
},
"image": {
"$ref": "#/$defs/PngImageInfo",
"description": "Pixel dimensions of the attached PNG."
}
},
"required": [
"arc_length",
"periapsis_radius",
"apoapsis_radius",
"time_span",
"projection",
"central_body",
"image"
],
"title": "TrajectoryResponse",
"type": "object"
}
porkchop¶
Generate an interplanetary porkchop scan — the (depart_epoch x arrive_epoch) grid of C3, arrival V-infinity, declination of the departure asymptote, and total-Δv proxy for a heliocentric transfer. e.g. porkchop(departure_body='earth', arrival_body='mars', depart_window=['2026-10-01T00:00:00Z','2026-12-01T00:00:00Z'], arrive_window=['2027-04-01T00:00:00Z','2027-10-01T00:00:00Z'], samples_per_axis=20) returns the minimum-total_dv 'best' cell, the five lowest-total_dv cells, and a compact ASCII contour for inline LLM display. Both windows are UTC ISO 8601 pairs [start, end]; the grid is samples_per_axis x samples_per_axis linspace-sampled across each window. Output shaping: the default output='summary' trims the response to best/top_cells/ascii_summary so it fits small-model context windows; pass output='full' to receive every feasible cell in grid (a 30x30 scan is ~250 KB and will overflow tight input caps). Body ephemerides come from JPL Horizons — the first call after a cold cache takes minutes (Horizons is slow), subsequent calls within the 7-day TTL are local. For broad exploratory scans, bring samples_per_axis down to 15-20 before launching a 50x50 grid. mu='sun' is the only v0.1 mu — heliocentric Lambert in ICRF ecliptic km / km/s. Misordered windows (arrive entirely before depart) raise invalid_input.porkchop_window_order. Horizons unreachable mid-grid raises data_source.horizons_unreachable with no partial results.
Input schema:
{
"properties": {
"departure_body": {
"description": "Body the spacecraft departs from. One of the JPL Horizons major-body names: 'mercury', 'venus', 'earth', 'mars', 'jupiter', 'saturn', 'uranus', 'neptune'. Must differ from `arrival_body`.",
"title": "Departure Body",
"type": "string"
},
"arrival_body": {
"description": "Body the spacecraft arrives at. Same body-name enum as `departure_body` and must be different from it.",
"title": "Arrival Body",
"type": "string"
},
"depart_window": {
"description": "Departure-epoch range as a two-element [start, end] list of UTC ISO 8601 strings, e.g. ['2028-03-01T00:00:00Z', '2028-06-01T00:00:00Z']. The axis is sampled uniformly across this range.",
"items": {
"type": "string"
},
"title": "Depart Window",
"type": "array"
},
"arrive_window": {
"description": "Arrival-epoch range as a two-element [start, end] list of UTC ISO 8601 strings, sampled uniformly along the other grid axis. Must end strictly after `depart_window` starts so at least one positive-time-of-flight cell exists.",
"items": {
"type": "string"
},
"title": "Arrive Window",
"type": "array"
},
"mu": {
"const": "sun",
"default": "sun",
"description": "Central-body gravitational parameter for the heliocentric Lambert solve. Only 'sun' is supported at v0.1; barycentric \u03bc is used.",
"title": "Mu",
"type": "string"
},
"samples_per_axis": {
"default": 30,
"description": "Grid resolution per axis \u2014 the full grid has samples_per_axis\u00b2 cells. Default 30 gives a 900-cell grid; higher resolves fine structure but costs more Horizons calls and Lambert solves. Capped at the upper end to keep the response size sane.",
"title": "Samples Per Axis",
"type": "integer"
},
"output": {
"default": "summary",
"description": "'summary' (default) returns the minimum-\u0394v 'best' cell, the ASCII C3 contour, and grid metadata only \u2014 the MCP-payload-friendly form. 'full' adds the per-cell grid; only request this when downstream really needs every cell.",
"enum": [
"summary",
"full"
],
"title": "Output",
"type": "string"
}
},
"required": [
"departure_body",
"arrival_body",
"depart_window",
"arrive_window"
],
"title": "porkchopArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"PorkchopCell": {
"additionalProperties": false,
"description": "One (depart_epoch, arrive_epoch) cell of the porkchop grid.",
"properties": {
"depart_epoch": {
"description": "UTC ISO 8601 departure epoch for this cell.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Depart Epoch",
"type": "string"
},
"arrive_epoch": {
"description": "UTC ISO 8601 arrival epoch for this cell.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Arrive Epoch",
"type": "string"
},
"tof": {
"$ref": "#/$defs/Quantity",
"description": "Time of flight, days.",
"examples": [
{
"unit": "days",
"value": 180.0
}
]
},
"c3": {
"$ref": "#/$defs/Quantity",
"description": "Departure characteristic energy, km^2/s^2. C3 = |v_inf_dep|^2; the launch vehicle's required excess energy above the departure body's escape velocity.",
"examples": [
{
"unit": "km^2/s^2",
"value": 12.5
}
]
},
"v_inf_arrival": {
"$ref": "#/$defs/Quantity",
"description": "Magnitude of arrival V-infinity (hyperbolic excess speed at arrival), km/s.",
"examples": [
{
"unit": "km/s",
"value": 3.0
}
]
},
"dec_dep_asymptote": {
"$ref": "#/$defs/Quantity",
"description": "Declination of the departure asymptote (DLA) in the ICRF equatorial frame, deg \u2014 the v_infinity direction rotated from the ecliptic working frame by the J2000 obliquity. This is the equatorial declination launch vehicles target, not ecliptic latitude.",
"examples": [
{
"unit": "deg",
"value": -12.0
}
]
},
"total_dv": {
"$ref": "#/$defs/Quantity",
"description": "Two-impulse \u0394v proxy = |v_inf_dep| + |v_inf_arr|, km/s. Both legs treated as hyperbolic injections; mission-specific maneuver design lives downstream.",
"examples": [
{
"unit": "km/s",
"value": 6.4
}
]
}
},
"required": [
"depart_epoch",
"arrive_epoch",
"tof",
"c3",
"v_inf_arrival",
"dec_dep_asymptote",
"total_dv"
],
"title": "PorkchopCell",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "Response from :func:`porkchop`.\n\nThe shape is the same regardless of ``output`` \u2014 the parameter only\nselects how much of the grid travels back to the caller:\n\n- ``output=\"summary\"`` (default): ``grid`` is empty; ``top_cells``\n carries up to five lowest-``total_dv`` cells so the response fits\n small-model input caps.\n- ``output=\"full\"``: ``grid`` carries every feasible cell in row-major\n order (outer: arrive-epoch, inner: depart-epoch); ``top_cells`` is\n still populated for the canonical \"show me the alternatives\" case.\n\nInfeasible cells (``tof <= 0``, Lambert no-solution) are skipped\nsilently rather than carried as NaN \u2014 the ASCII summary marks them\nwith a space glyph for the visual.",
"properties": {
"best": {
"$ref": "#/$defs/PorkchopCell",
"description": "The feasible cell with the minimum total_dv across the scan."
},
"top_cells": {
"description": "Up to five lowest-total_dv cells, sorted ascending. Always includes `best` as the first entry; size is capped to keep the default response under small-model input limits.",
"items": {
"$ref": "#/$defs/PorkchopCell"
},
"title": "Top Cells",
"type": "array"
},
"grid": {
"description": "Every feasible cell in row-major order (outer: arrive-epoch, inner: depart-epoch). Populated only when the caller passes output='full'; empty otherwise.",
"items": {
"$ref": "#/$defs/PorkchopCell"
},
"title": "Grid",
"type": "array"
},
"ascii_summary": {
"description": "Compact text contour of C3 over the grid. samples_per_axis rows x samples_per_axis columns; each glyph is one of `.:-+*#@X` binned by C3 decile, infeasible cells rendered as a space. Rows are arrive-epoch indices (top = earliest), columns depart-epoch indices (left = earliest).",
"title": "Ascii Summary",
"type": "string"
}
},
"required": [
"best",
"top_cells",
"ascii_summary"
],
"title": "PorkchopResponse",
"type": "object"
}
satellite_metadata¶
Look up persistent satellite metadata from the ESA DISCOSweb catalogue by NORAD catalogue ID. Returns information the OMM payload from tle_lookup does not carry: mass, bounding-box dimensions, launch date and site, owner / operator, mission type (Payload / Rocket Body / Debris / Unknown), and decay status. Use this to cross-reference results from tle_lookup (NORAD ID is the shared key between the two tools), to filter by physical properties (e.g. distinguish a CubeSat from an ISS-class platform), to sanity-check an OMM (e.g. confirm a launch epoch is consistent with the EPOCH field), or to verify an object is still in orbit before relying on its TLE. Requires an ESA Space Debris User Account bearer token; without credentials the call raises credential_required.discosweb without contacting the upstream. DISCOSweb has no record for very recent launches that have not yet been catalogued — those surface as data_source.discosweb_norad_not_found.
Input schema:
{
"properties": {
"norad_id": {
"description": "NORAD catalogue ID as a string of digits. The same value tle_lookup returns in its norad_id field; use it to cross-reference. Example: '25544' for the ISS, '20580' for Hubble. Names and group keywords are not accepted \u2014 look up the NORAD ID via tle_lookup first if you only have a name.",
"examples": [
"25544",
"20580"
],
"title": "Norad Id",
"type": "string"
}
},
"required": [
"norad_id"
],
"title": "satellite_metadataArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Dimensions": {
"additionalProperties": false,
"description": "Bounding-box dimensions of a satellite, when all three axes are known.\n\nDISCOSweb tracks width, height, and depth as separate nullable fields.\nThe tool only emits a :class:`Dimensions` value when all three are\npopulated \u2014 a partial set would force the LLM to guess which axis is\nmissing, so the schema prefers \"no dimensions\" over \"two of three\".",
"properties": {
"x": {
"$ref": "#/$defs/Quantity",
"description": "Width along the satellite's body-x axis. Unit must be a length (m / km).",
"examples": [
{
"unit": "m",
"value": 73.0
}
]
},
"y": {
"$ref": "#/$defs/Quantity",
"description": "Height along the satellite's body-y axis. Unit must be a length (m / km).",
"examples": [
{
"unit": "m",
"value": 45.0
}
]
},
"z": {
"$ref": "#/$defs/Quantity",
"description": "Depth along the satellite's body-z axis. Unit must be a length (m / km).",
"examples": [
{
"unit": "m",
"value": 27.5
}
]
}
},
"required": [
"x",
"y",
"z"
],
"title": "Dimensions",
"type": "object"
},
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "Top-level response from :func:`satellite_metadata`.\n\nCarries identifiers (name, COSPAR ID, NORAD ID), physical properties\n(mass, dimensions \u2014 both nullable because DISCOSweb's records vary),\nprovenance (launch date and site, operator), classification, decay\nstatus, and cache-freshness flags.",
"properties": {
"name": {
"description": "Object name as carried by DISCOSweb, e.g. 'ISS (ZARYA)'.",
"examples": [
"ISS (ZARYA)",
"HST"
],
"title": "Name",
"type": "string"
},
"cospar_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "COSPAR / International Designator (e.g. '1998-067A'). Stable per-object identifier issued at launch. Nullable for objects that lack one in DISCOSweb's records.",
"examples": [
"1998-067A",
"1990-037B"
],
"title": "Cospar Id"
},
"norad_id": {
"description": "NORAD catalogue ID as a string. The same identifier tle_lookup returns, allowing cross-reference between the two tools.",
"examples": [
"25544",
"20580"
],
"title": "Norad Id",
"type": "string"
},
"mass_kg": {
"anyOf": [
{
"$ref": "#/$defs/Quantity"
},
{
"type": "null"
}
],
"description": "Object mass. Nullable: DISCOSweb does not carry a mass for every object (long tail of debris). When populated the value is the launch / dry mass as recorded by ESA.",
"examples": [
{
"unit": "kg",
"value": 420000.0
}
]
},
"dimensions_m": {
"anyOf": [
{
"$ref": "#/$defs/Dimensions"
},
{
"type": "null"
}
],
"description": "Bounding-box dimensions (width, height, depth) in metres. Nullable: only populated when all three axes are known. DISCOSweb supplies these for tracked payloads but not for the long tail of fragmentation debris."
},
"launch_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Launch epoch as an ISO 8601 string when known. Nullable for very old objects whose launch records pre-date DISCOSweb's catalogue.",
"examples": [
"1998-11-20T06:40:00Z"
],
"title": "Launch Date"
},
"launch_site": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Name of the launch site (e.g. 'Baikonur Cosmodrome'). Nullable.",
"examples": [
"Baikonur Cosmodrome",
"Kennedy Space Center"
],
"title": "Launch Site"
},
"owner": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Operator(s) recorded by DISCOSweb. Multiple operators are joined with ', '. Nullable when no operator is recorded.",
"examples": [
"NASA",
"ESA, JAXA"
],
"title": "Owner"
},
"mission_type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "DISCOSweb object classification \u2014 typically one of 'Payload', 'Rocket Body', 'Debris', 'Unknown'. Free-form string because the upstream taxonomy can extend.",
"examples": [
"Payload",
"Rocket Body",
"Debris"
],
"title": "Mission Type"
},
"decay_status": {
"description": "Whether the object is still in orbit. 'decayed' when DISCOSweb carries a reentry epoch, 'active' otherwise. 'unknown' is reserved for future cases where DISCOSweb explicitly signals an indeterminate state; the current adapter never emits it.",
"enum": [
"active",
"decayed",
"unknown"
],
"examples": [
"active",
"decayed"
],
"title": "Decay Status",
"type": "string"
},
"decay_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "Reentry epoch as an ISO 8601 string. Populated iff decay_status='decayed'; null otherwise.",
"examples": [
"2001-03-23T05:59:24Z"
],
"title": "Decay Date"
},
"source": {
"const": "discosweb",
"default": "discosweb",
"description": "Always 'discosweb' for this tool \u2014 declared on the wire for symmetry with multi-source tools.",
"title": "Source",
"type": "string"
},
"fetched_at": {
"description": "UTC ISO 8601 timestamp when this record was retrieved from DISCOSweb (or originally cached, when stale=true).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Fetched At",
"type": "string"
},
"stale": {
"description": "True when DISCOSweb was unreachable and the cached value was returned as a stale fallback. Operators should treat the record as best-effort in that case.",
"title": "Stale",
"type": "boolean"
}
},
"required": [
"name",
"cospar_id",
"norad_id",
"mass_kg",
"dimensions_m",
"launch_date",
"launch_site",
"owner",
"mission_type",
"decay_status",
"decay_date",
"fetched_at",
"stale"
],
"title": "SatelliteMetadataResponse",
"type": "object"
}
sgp4_propagate¶
Propagate a TLE forward in time via SGP4/SDP4, returning Cartesian state vectors at the requested epochs. TLE input is either two raw 69-char lines ({line1, line2}) or a parsed OMM JSON object ({omm: {...}}). e.g. sgp4_propagate(tle={line1: ..., line2: ...}, epochs=['2026-05-23T12:00:00Z'], frame='TEME') returns one state vector in TEME. Epochs are UTC ISO 8601 with a mandatory time component (e.g. '2026-05-23T12:00:00Z') — a bare date is rejected. Default frame is TEME (SGP4's native output); for ICRF, GCRS, ITRS, or CIRS the tool transforms via astropy with per-epoch obstime. A list of one epoch is fine; 1000+ epochs is also fine — propagation cost scales linearly. Output shaping: the default output='summary' returns at most twelve evenly-spaced states (first and last epoch always included) so the response fits small-model input caps; pass output='full' to receive one state per epoch when you need the dense series. SGP4 propagation failures (decayed satellite, deep-space epoch beyond SDP4 validity) surface as upstream.sgp4_failure.
Input schema:
{
"$defs": {
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"TleLines": {
"additionalProperties": false,
"description": "Two-line TLE as raw strings.\n\nBoth lines must be exactly 69 characters (the canonical TLE width).\nSub-69-character lines are the most common LLM mistake \u2014 they almost\nalways come from a chat client stripping trailing whitespace.",
"properties": {
"line1": {
"description": "First TLE line (begins with '1 '), exactly 69 characters.",
"examples": [
"1 25544U 98067A 24001.50000000 .00010000 00000-0 18000-3 0 9990"
],
"title": "Line1",
"type": "string"
},
"line2": {
"description": "Second TLE line (begins with '2 '), exactly 69 characters.",
"examples": [
"2 25544 51.6400 90.0000 0001000 90.0000 270.0000 15.50000000000000"
],
"title": "Line2",
"type": "string"
}
},
"required": [
"line1",
"line2"
],
"title": "TleLines",
"type": "object"
},
"TleOmm": {
"additionalProperties": false,
"description": "Parsed OMM (Orbit Mean Elements Message) JSON.\n\nSchema is intentionally loose at v0.1: we accept whatever the upstream\nOMM source (CelesTrak) emitted. Downstream tools that need specific\nfields raise typed errors when those fields are missing.",
"properties": {
"omm": {
"additionalProperties": true,
"description": "Parsed OMM JSON object (CCSDS standard fields like CCSDS_OMM_VERS, EPOCH, MEAN_ELEMENTS, \u2026). Fetched from CelesTrak by the tle_lookup tool or supplied directly.",
"examples": [
{
"CCSDS_OMM_VERS": "2.0",
"EPOCH": "2024-01-01T12:00:00.000000",
"MEAN_MOTION": 15.5
}
],
"title": "Omm",
"type": "object"
}
},
"required": [
"omm"
],
"title": "TleOmm",
"type": "object"
}
},
"properties": {
"tle": {
"anyOf": [
{
"$ref": "#/$defs/TleLines"
},
{
"$ref": "#/$defs/TleOmm"
}
],
"description": "The orbit to propagate, supplied as either a TLE line pair ({line1, line2} \u2014 each line exactly 69 chars per the TLE spec) or as an OMM payload (the CCSDS-standard JSON returned by tle_lookup). Either is accepted; the OMM form is preferred when passing programmatically since it does not depend on TLE column alignment.",
"title": "Tle"
},
"epochs": {
"description": "UTC ISO 8601 epochs at which to evaluate the propagated state. Each entry must include a time component (e.g. '2024-01-01T00:00:00Z'); date-only is rejected. The list defines both the evaluation grid and the output ordering.",
"items": {
"description": "ISO 8601 UTC timestamp with a mandatory time component. Examples: '2026-05-23T12:00:00Z', '2026-05-23T12:00:00.500+00:00'. A bare date like '2026-05-23' is rejected.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"type": "string"
},
"title": "Epochs",
"type": "array"
},
"frame": {
"$ref": "#/$defs/Frame",
"default": "TEME",
"description": "Output frame. Supported here: TEME (SGP4-native, fastest), ICRF / GCRS (inertial, distinct origins), ITRS (Earth-fixed), CIRS (intermediate). For TIRS or IAU body-fixed frames use the frame_transform tool against a TEME state."
},
"output": {
"default": "summary",
"description": "'summary' (default) caps the response at a small number of evenly-spaced states across the epoch list, keeping the MCP payload tight. 'full' returns one state per input epoch \u2014 request only when downstream needs the full grid.",
"enum": [
"summary",
"full"
],
"title": "Output",
"type": "string"
}
},
"required": [
"tle",
"epochs"
],
"title": "sgp4_propagateArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Frame": {
"description": "Reference frames used for state vectors and frame conversions.\n\nThe v0.1 set covers the inertial, Earth-rotating, and IAU body-fixed\nframes the wrapped upstreams (sgp4, astropy.coordinates, skyfield) all\nspeak. Adding a new frame here means adding the corresponding transform\npath to the frame_transform tool.",
"enum": [
"TEME",
"ICRF",
"GCRS",
"ITRS",
"CIRS",
"TIRS",
"IAU_EARTH",
"IAU_MARS",
"IAU_MOON"
],
"title": "Frame",
"type": "string"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"StateVector": {
"additionalProperties": false,
"description": "Cartesian state in a named frame at a named epoch.\n\nThe position and velocity are :class:`QuantityVector` instances so the\nunit (km vs m, km/s vs m/s) is on the wire \u2014 never implicit.",
"properties": {
"r": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position vector [x, y, z]. Unit must be a length (km / m / AU).",
"examples": [
{
"unit": "km",
"value": [
7000.0,
0.0,
0.0
]
}
]
},
"v": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian velocity vector [vx, vy, vz]. Unit must be a velocity (km/s / m/s).",
"examples": [
{
"unit": "km/s",
"value": [
0.0,
7.5,
0.0
]
}
]
},
"frame": {
"$ref": "#/$defs/Frame",
"description": "Reference frame in which r and v are expressed.",
"examples": [
"TEME",
"ICRF"
]
},
"epoch": {
"description": "UTC ISO 8601 epoch at which the state is valid.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
}
},
"required": [
"r",
"v",
"frame",
"epoch"
],
"title": "StateVector",
"type": "object"
}
},
"additionalProperties": false,
"description": "A list of propagated state vectors.\n\nIn the default ``output=\"summary\"`` mode the list is capped to\n:data:`_SUMMARY_STATE_CAP` evenly-spaced entries (always including\nthe first and last requested epoch). Pass ``output=\"full\"`` to receive\none state per input epoch.",
"properties": {
"states": {
"description": "Propagated state vectors in the requested frame. In output='full' there is one entry per input epoch; in the default output='summary' the list is subsampled to at most twelve evenly-spaced entries (first and last always retained). Position in km, velocity in km/s, units explicit on every numeric field.",
"items": {
"$ref": "#/$defs/StateVector"
},
"title": "States",
"type": "array"
}
},
"required": [
"states"
],
"title": "Sgp4PropagateResponse",
"type": "object"
}
spice_body_parameters¶
Read physical and orientation constants for a body from furnished PCK kernels — triaxial radii (km), GM (km^3/s^2), and the pole / prime-meridian orientation coefficients — e.g. spice_body_parameters(body='MARS') returns Mars's triaxial radii and GM (the default common set), and spice_body_parameters(body='499', parameters=['radii','pole_ra','pm']) adds the orientation coefficients. body accepts a name ('MARS') or a NAIF ID as a string ('499'). parameters names the constants to fetch — radii, gm, pole_ra, pole_dec, pm — or is omitted for the common set (radii + gm). Each constant comes back as a list of {value, unit} elements (RADII is three km values; GM one km^3/s^2 value; an orientation item its polynomial coefficients, e.g. POLE_RA = [deg, deg/century, deg/century^2]), with the kernel-pool variable it came from (e.g. 'BODY499_RADII'). The PCK providing each constant must be furnished first via spice_load_kernel — radii / pole / PM from a planetary-constants PCK, GM from a gravity PCK — and a constant no loaded kernel supplies returns a typed error, never a silent gap.
Input schema:
{
"properties": {
"body": {
"description": "The body whose constants to read \u2014 a body name ('MARS', 'MOON') or a NAIF integer ID as a string ('499', '301'). Resolved by CSPICE; an unrecognised body returns a typed error.",
"title": "Body",
"type": "string"
},
"parameters": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Which constants to fetch: any of 'radii', 'gm', 'pole_ra', 'pole_dec', 'pm' (case-insensitive). Omit for the default common set ['radii', 'gm']. e.g. ['radii', 'pole_ra', 'pm'].",
"title": "Parameters"
}
},
"required": [
"body"
],
"title": "spice_body_parametersArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"SpiceBodyParameter": {
"additionalProperties": false,
"description": "One body constant: its element values (each ``{value, unit}``) and source.",
"properties": {
"name": {
"description": "The requested parameter name, echoed (one of 'radii', 'gm', 'pole_ra', 'pole_dec', 'pm').",
"title": "Name",
"type": "string"
},
"source": {
"description": "The CSPICE kernel-pool variable the values were read from, e.g. 'BODY499_RADII'. CSPICE does not expose the source kernel *file* for a pool variable; this is the authoritative provenance it does expose.",
"title": "Source",
"type": "string"
},
"values": {
"description": "The constant's elements, one {value, unit} each. A scalar like GM is a single-element list; RADII is three km elements [a, b, c]; an orientation item is its polynomial coefficients with per-element units (e.g. POLE_RA = [deg, deg/century, deg/century^2], PM = [deg, deg/day, deg/day^2]).",
"items": {
"$ref": "#/$defs/Quantity"
},
"title": "Values",
"type": "array"
}
},
"required": [
"name",
"source",
"values"
],
"title": "SpiceBodyParameter",
"type": "object"
}
},
"additionalProperties": false,
"description": "Requested physical / orientation constants for a body.",
"properties": {
"body": {
"description": "The body, echoed from the request (name or NAIF ID as supplied).",
"title": "Body",
"type": "string"
},
"parameters": {
"description": "One entry per requested parameter, in request order \u2014 or the default common set [radii, gm] when none were specified.",
"items": {
"$ref": "#/$defs/SpiceBodyParameter"
},
"title": "Parameters",
"type": "array"
}
},
"required": [
"body",
"parameters"
],
"title": "SpiceBodyParametersResponse",
"type": "object"
}
spice_frame_transform¶
Rotate a vector between SPICE reference frames defined by furnished FK / PCK kernels at an epoch — in particular the non-Earth body-fixed frames the kernel-free frame_transform tool cannot provide. e.g. spice_frame_transform(from_frame='J2000', to_frame='IAU_MARS', epoch='2026-01-01T00:00:00Z', state={position: {value: [4000, 5000, 6000], unit: 'km'}}) rotates a position into the Mars body-fixed frame (a Mars PCK must be furnished first). Omit state to get just the 3x3 rotation matrix; pass position only for a pxform rotation, or position+velocity for the full sxform state rotation (which folds the target frame's rotation rate into the rotated velocity). The FK / PCK defining the frame must be furnished first via spice_load_kernel, plus a leap-second kernel (LSK) for the epoch — a missing kernel or an unrecognised frame returns a typed error, never a silent result. epoch is UTC ISO 8601 with a time component (e.g. '2026-01-01T00:00:00Z'). Use this for body-fixed frames like IAU_MARS / IAU_MOON; for ICRF / ITRS / GCRS / TEME prefer the kernel-free frame_transform.
Input schema:
{
"$defs": {
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"RotatableState": {
"additionalProperties": false,
"description": "A 3- or 6-vector to rotate between SPICE frames.\n\n``position`` alone is a 3-vector, rotated by ``pxform``; adding ``velocity``\nmakes it a 6-vector state, rotated by ``sxform`` (which carries the target\nframe's rotation rate into the rotated velocity). Omit the whole object on\nthe tool call to request the rotation matrix alone \u2014 and to rotate any\nvector that is not a position (a pointing direction, an angular-momentum\nvector), request the matrix and apply it yourself.",
"properties": {
"position": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position [x, y, z] in the source frame, length unit (km / m / AU). Rotated into `to_frame`. e.g. {value: [4000, 5000, 6000], unit: 'km'}.",
"examples": [
{
"unit": "km",
"value": [
4000.0,
5000.0,
6000.0
]
}
]
},
"velocity": {
"anyOf": [
{
"$ref": "#/$defs/QuantityVector"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional Cartesian velocity [vx, vy, vz] in the source frame, velocity unit (km/s / m/s). When present the rotation uses the full state transform (sxform), so the rotated velocity includes the target frame's rotation rate; omit it to rotate position only (pxform). e.g. {value: [-1.0, 2.0, 0.5], unit: 'km/s'}.",
"examples": [
{
"unit": "km/s",
"value": [
-1.0,
2.0,
0.5
]
}
]
}
},
"required": [
"position"
],
"title": "RotatableState",
"type": "object"
}
},
"properties": {
"from_frame": {
"description": "The source SPICE frame the input is currently expressed in \u2014 any frame name CSPICE recognises once the defining kernels are furnished (e.g. 'J2000', 'ECLIPJ2000', 'IAU_MARS'). An unrecognised frame returns a typed error.",
"title": "From Frame",
"type": "string"
},
"to_frame": {
"description": "The target SPICE frame to rotate into (e.g. 'IAU_MARS', 'IAU_MOON', 'ITRF93'). The FK / PCK defining a body-fixed target must be furnished first via spice_load_kernel.",
"title": "To Frame",
"type": "string"
},
"epoch": {
"description": "UTC ISO 8601 epoch with a mandatory time component (e.g. '2026-01-01T00:00:00Z') at which the rotation is evaluated; a bare date is rejected. A leap-second kernel (LSK) must be furnished to resolve it.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Epoch",
"type": "string"
},
"state": {
"anyOf": [
{
"$ref": "#/$defs/RotatableState"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional 3- or 6-vector to rotate: {position} for a pxform rotation, or {position, velocity} for the full sxform state rotation. Omit entirely to return just the 3x3 rotation matrix. e.g. {position: {value: [4000, 5000, 6000], unit: 'km'}}."
}
},
"required": [
"from_frame",
"to_frame",
"epoch"
],
"title": "spice_frame_transformArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
}
},
"additionalProperties": false,
"description": "A frame-to-frame rotation, plus the rotated state when one was supplied.",
"properties": {
"from_frame": {
"description": "Source frame, echoed verbatim from the request.",
"title": "From Frame",
"type": "string"
},
"to_frame": {
"description": "Target frame, echoed verbatim from the request.",
"title": "To Frame",
"type": "string"
},
"epoch": {
"description": "The UTC ISO 8601 epoch the rotation is evaluated at, echoed verbatim from the request.",
"title": "Epoch",
"type": "string"
},
"rotation": {
"description": "The 3x3 orientation matrix from pxform, as three row vectors: row i is the i-th row of R, where a source-frame vector v maps to R @ v in the target frame. Dimensionless (unit '1'). Always present, including for a rotation-only request.",
"items": {
"$ref": "#/$defs/QuantityVector"
},
"title": "Rotation",
"type": "array"
},
"position": {
"anyOf": [
{
"$ref": "#/$defs/QuantityVector"
},
{
"type": "null"
}
],
"default": null,
"description": "The input position rotated into `to_frame`, in the same length unit as the input. Null when no state was supplied (a rotation-only request)."
},
"velocity": {
"anyOf": [
{
"$ref": "#/$defs/QuantityVector"
},
{
"type": "null"
}
],
"default": null,
"description": "The input velocity rotated into `to_frame` via the full state transform (sxform), in the same velocity unit as the input. Null when no velocity was supplied. For a rotating target frame this differs from rotation @ velocity, because sxform also folds in the frame's rotation rate."
}
},
"required": [
"from_frame",
"to_frame",
"epoch",
"rotation"
],
"title": "SpiceFrameTransformResponse",
"type": "object"
}
spice_list_kernels¶
List the SPICE kernels currently furnished in the process kernel pool, e.g. spice_list_kernels() to confirm a leap-second kernel and an SPK are both loaded before a state query, or spice_list_kernels(kind=['SPK','PCK']) to see only the ephemeris and planetary-constants kernels. Each row carries the kernel's name, type (SPK / CK / PCK / EK / DSK / META / TEXT), provenance, and handle. The pool is process-global, so on an HTTP deployment this reports every caller's kernels, not just yours.
Input schema:
{
"properties": {
"kind": {
"anyOf": [
{
"items": {
"enum": [
"SPK",
"CK",
"PCK",
"EK",
"DSK",
"META",
"TEXT"
],
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Optional category filter \u2014 list only kernels of these CSPICE types. Omit to list every loaded kernel. e.g. ['SPK'] for ephemerides, or ['SPK','PCK'] for both. Valid categories: ['SPK', 'CK', 'PCK', 'EK', 'DSK', 'META', 'TEXT'].",
"title": "Kind"
}
},
"title": "spice_list_kernelsArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"SpiceKernelInfo": {
"additionalProperties": false,
"description": "One kernel-pool entry, exactly as CSPICE reports it.",
"properties": {
"name": {
"description": "The local path CSPICE knows this kernel by \u2014 the furnished filesystem path, or the on-disk cache path for a kernel loaded from a URL. This is the unload key: pass this exact string to spice_unload_kernel, never the original URL.",
"title": "Name",
"type": "string"
},
"type": {
"description": "CSPICE kernel category: one of SPK, CK, PCK, EK, DSK, META (a meta-kernel), or TEXT. Leap-second (LSK), frame (FK), and spacecraft-clock (SCLK) kernels all report as TEXT \u2014 CSPICE does not distinguish them at this layer.",
"title": "Type",
"type": "string"
},
"source": {
"description": "Provenance within the pool: the meta-kernel that furnished this kernel, or an empty string when it was furnished directly. CSPICE does not retain the original URL for a URL load, so this is not the download source.",
"title": "Source",
"type": "string"
},
"handle": {
"description": "CSPICE file handle for binary kernels (SPK / CK / binary PCK / EK / DSK); 0 for text kernels, which load into the kernel pool rather than as DAF/DAS files. An opaque identifier, not a physical quantity \u2014 unitless.",
"title": "Handle",
"type": "integer"
}
},
"required": [
"name",
"type",
"source",
"handle"
],
"title": "SpiceKernelInfo",
"type": "object"
}
},
"additionalProperties": false,
"description": "The kernels currently furnished in the process pool.",
"properties": {
"kernels": {
"description": "One entry per kernel currently furnished in the process pool, after any `kind` filter. Shared by every client of an HTTP deployment \u2014 the pool is process-global.",
"items": {
"$ref": "#/$defs/SpiceKernelInfo"
},
"title": "Kernels",
"type": "array"
}
},
"required": [
"kernels"
],
"title": "SpiceListKernelsResponse",
"type": "object"
}
spice_load_kernel¶
Furnish a SPICE kernel into the process kernel pool from a local path or a NAIF https URL, so later spice_* queries can read it, e.g. spice_load_kernel('https://naif.jpl.nasa.gov/pub/naif/generic_kernels/lsk/naif0012.tls') furnishes a generic leap-second kernel. The pool is additive and persists across calls — load a leap-second kernel (LSK) before any time conversion, and a planetary SPK before a state query; both stay loaded together. A meta-kernel (.tm) furnishes everything it lists in one call, so loaded may contain several kernels of several types. URL sources must be on the NAIF allowlist (naif.jpl.nasa.gov, https only) — host your own mirror behind a local path otherwise; a repeat URL load is served from the on-disk cache (from_cache=true). Returns each furnished kernel's resolved name, type, and handle. Keep the returned name — it is what you pass to spice_unload_kernel.
Input schema:
{
"properties": {
"source": {
"description": "A local filesystem path to a kernel, or an https NAIF URL (naif.jpl.nasa.gov). A meta-kernel path furnishes every kernel it lists. e.g. '/data/de440.bsp' or 'https://naif.jpl.nasa.gov/pub/naif/generic_kernels/lsk/naif0012.tls'.",
"title": "Source",
"type": "string"
}
},
"required": [
"source"
],
"title": "spice_load_kernelArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"SpiceKernelInfo": {
"additionalProperties": false,
"description": "One kernel-pool entry, exactly as CSPICE reports it.",
"properties": {
"name": {
"description": "The local path CSPICE knows this kernel by \u2014 the furnished filesystem path, or the on-disk cache path for a kernel loaded from a URL. This is the unload key: pass this exact string to spice_unload_kernel, never the original URL.",
"title": "Name",
"type": "string"
},
"type": {
"description": "CSPICE kernel category: one of SPK, CK, PCK, EK, DSK, META (a meta-kernel), or TEXT. Leap-second (LSK), frame (FK), and spacecraft-clock (SCLK) kernels all report as TEXT \u2014 CSPICE does not distinguish them at this layer.",
"title": "Type",
"type": "string"
},
"source": {
"description": "Provenance within the pool: the meta-kernel that furnished this kernel, or an empty string when it was furnished directly. CSPICE does not retain the original URL for a URL load, so this is not the download source.",
"title": "Source",
"type": "string"
},
"handle": {
"description": "CSPICE file handle for binary kernels (SPK / CK / binary PCK / EK / DSK); 0 for text kernels, which load into the kernel pool rather than as DAF/DAS files. An opaque identifier, not a physical quantity \u2014 unitless.",
"title": "Handle",
"type": "integer"
}
},
"required": [
"name",
"type",
"source",
"handle"
],
"title": "SpiceKernelInfo",
"type": "object"
}
},
"additionalProperties": false,
"description": "Result of furnishing a kernel source into the process pool.",
"properties": {
"loaded": {
"description": "Every kernel this call added to the pool. A plain kernel yields one entry; a meta-kernel yields the META entry plus every kernel it references, each with its own resolved type. Empty only if the source was already fully loaded.",
"items": {
"$ref": "#/$defs/SpiceKernelInfo"
},
"title": "Loaded",
"type": "array"
},
"from_cache": {
"description": "Whether the source was served from the on-disk kernel cache with no network download. Always false for a local-path load; true for a URL whose bytes were already cached and fresh.",
"title": "From Cache",
"type": "boolean"
}
},
"required": [
"loaded",
"from_cache"
],
"title": "SpiceLoadKernelResponse",
"type": "object"
}
spice_state¶
Query the state — position (km) and velocity (km/s) — of a target body relative to an observer body at one or more epochs, read from furnished SPK kernels, e.g. spice_state(target='MOON', observer='EARTH', epochs=['2026-01-01T00:00:00Z']) returns the Moon's geocentric state in J2000 (CSPICE's name for the Earth-mean-equator/equinox-of-J2000 inertial frame, aligned with ICRF to milliarcsecond level). Requires the relevant SPK and a leap-second kernel (LSK) furnished first via spice_load_kernel — a missing kernel returns a typed error, never a silent empty state. Each epoch must be UTC ISO 8601 with a time component (e.g. '2026-01-01T00:00:00Z'), not a bare date. target and observer accept body names ('MOON', 'MARS') or NAIF integer IDs as strings ('301', '499'), not arbitrary labels. aberration selects the correction (NONE for the geometric state, or LT / LT+S / CN / CN+S and their X-prefixed forms for light-time and stellar-aberration corrections); light time is returned only when a correction other than NONE is requested. Use the kernel-free frame_transform tool for Earth-centred frame changes; this tool is for SPK-backed ephemeris states.
Input schema:
{
"properties": {
"target": {
"description": "The body whose state to query \u2014 a body name ('MOON', 'MARS') or a NAIF integer ID as a string ('301', '499'). Resolved by CSPICE against the furnished kernels; an unrecognised name returns a typed error.",
"title": "Target",
"type": "string"
},
"observer": {
"description": "The body the state is measured relative to \u2014 a body name ('EARTH', 'SOLAR SYSTEM BARYCENTER') or a NAIF integer ID as a string ('399', '0'). Same name/ID resolution as `target`.",
"title": "Observer",
"type": "string"
},
"epochs": {
"description": "One or more UTC ISO 8601 epochs with a mandatory time component (e.g. ['2026-01-01T00:00:00Z']); a bare date is rejected. Each is queried independently and returned in the same order.",
"items": {
"description": "ISO 8601 UTC timestamp with a mandatory time component. Examples: '2026-05-23T12:00:00Z', '2026-05-23T12:00:00.500+00:00'. A bare date like '2026-05-23' is rejected.",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"type": "string"
},
"title": "Epochs",
"type": "array"
},
"frame": {
"default": "J2000",
"description": "Reference frame the state is expressed in. Defaults to 'J2000' (CSPICE's Earth-mean-equator/equinox-of-J2000 inertial frame, aligned with ICRF). Any frame the furnished kernels define is accepted (e.g. 'ECLIPJ2000', 'IAU_MARS').",
"title": "Frame",
"type": "string"
},
"aberration": {
"default": "NONE",
"description": "Aberration correction: 'NONE' for the geometric state, or 'LT' / 'LT+S' / 'CN' / 'CN+S' (and the X-prefixed transmission forms) for light-time and stellar-aberration corrections. Light time is returned only for a non-NONE correction. Valid values: ['NONE', 'LT', 'LT+S', 'CN', 'CN+S', 'XLT', 'XLT+S', 'XCN', 'XCN+S'].",
"title": "Aberration",
"type": "string"
}
},
"required": [
"target",
"observer",
"epochs"
],
"title": "spice_stateArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"QuantityVector": {
"additionalProperties": false,
"description": "Pydantic model for a vector ``{value: [...], unit}`` payload.\n\nThe JSON-schema export uses ``type: array, items: {type: number}`` for\nthe ``value`` field \u2014 the unit-discipline meta-test treats this as the\ncanonical vector-quantity shape.",
"properties": {
"value": {
"description": "Numeric values of the quantity vector.",
"items": {
"type": "number"
},
"title": "Value",
"type": "array"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "QuantityVector",
"type": "object"
},
"SpiceStateAtEpoch": {
"additionalProperties": false,
"description": "A target's state relative to an observer at one epoch.",
"properties": {
"epoch": {
"description": "The UTC ISO 8601 epoch this state is for, echoed verbatim from the requested `epochs` so each entry is self-describing regardless of order.",
"title": "Epoch",
"type": "string"
},
"position": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian position [x, y, z] of the target relative to the observer, in the requested frame (km)."
},
"velocity": {
"$ref": "#/$defs/QuantityVector",
"description": "Cartesian velocity [vx, vy, vz] of the target relative to the observer, in the requested frame (km/s)."
},
"light_time": {
"anyOf": [
{
"$ref": "#/$defs/Quantity"
},
{
"type": "null"
}
],
"default": null,
"description": "One-way light time between target and observer (s). Present only when the aberration correction is not 'NONE'; a geometric ('NONE') query returns null here because no light-time correction was requested."
}
},
"required": [
"epoch",
"position",
"velocity"
],
"title": "SpiceStateAtEpoch",
"type": "object"
}
},
"additionalProperties": false,
"description": "States of a target relative to an observer at one or more epochs.",
"properties": {
"target": {
"description": "The target body, echoed from the request (name or NAIF ID as supplied).",
"title": "Target",
"type": "string"
},
"observer": {
"description": "The observer body, echoed from the request (name or NAIF ID as supplied).",
"title": "Observer",
"type": "string"
},
"frame": {
"description": "The reference frame the states are expressed in, echoed from the request.",
"title": "Frame",
"type": "string"
},
"aberration": {
"description": "The aberration correction applied, upper-cased and echoed from the request (e.g. 'NONE', 'LT', 'LT+S').",
"title": "Aberration",
"type": "string"
},
"states": {
"description": "One state per requested epoch, in the same order as the `epochs` input.",
"items": {
"$ref": "#/$defs/SpiceStateAtEpoch"
},
"title": "States",
"type": "array"
}
},
"required": [
"target",
"observer",
"frame",
"aberration",
"states"
],
"title": "SpiceStateResponse",
"type": "object"
}
spice_time_convert¶
Convert a time between SPICE kernel-defined systems — ET (TDB seconds past J2000), UTC, and SCLK (spacecraft clock) — using the furnished leap-second and spacecraft-clock kernels. e.g. spice_time_convert(value='2026-01-01T00:00:00Z', from_scale='UTC', to_scale='ET') returns {value: 820497669.184, unit: 's past J2000 TDB'}, the ephemeris time for that UTC epoch; spice_time_convert(value='2026-01-01T00:00:00Z', from_scale='UTC', to_scale='SCLK', spacecraft=-82) returns the Cassini spacecraft-clock string for it. ET output is a {value, unit} quantity in 's past J2000 TDB'; UTC output is an ISO 8601 calendar string; SCLK output is the raw clock string. spacecraft (a NAIF ID like -82, or a name a furnished kernel maps) is required for any conversion to or from SCLK. A leap-second kernel (LSK) must be furnished first (spice_load_kernel) for any ET<->UTC conversion, and an SCLK kernel for any SCLK conversion — a missing kernel returns a typed error, never a silent result. This is the kernel-backed counterpart to time_convert: prefer the kernel-free time_convert for plain UTC / TAI / TT / TDB / UT1 / GPS without a loaded kernel; reach for this tool only for ET's kernel-defined zero and for SCLK.
Input schema:
{
"properties": {
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
],
"description": "The time to convert. For from_scale='UTC' an ISO 8601 string with a time component ('2026-01-01T00:00:00Z'); for 'ET' a number of seconds past J2000 TDB (820497669.184, as a number or a numeric string); for 'SCLK' the spacecraft-clock string ('1/1465644281.171').",
"title": "Value"
},
"from_scale": {
"description": "The input time system: 'ET' (TDB seconds past J2000), 'UTC' (calendar string), or 'SCLK' (spacecraft clock). One of ['ET', 'UTC', 'SCLK']. SCLK requires `spacecraft`.",
"enum": [
"ET",
"UTC",
"SCLK"
],
"title": "From Scale",
"type": "string"
},
"to_scale": {
"description": "The output time system, same three values as `from_scale`. An 'ET' target returns a {value, unit} seconds quantity; 'UTC' and 'SCLK' return a string.",
"enum": [
"ET",
"UTC",
"SCLK"
],
"title": "To Scale",
"type": "string"
},
"spacecraft": {
"anyOf": [
{
"type": "string"
},
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "The spacecraft whose clock to use \u2014 required for any SCLK conversion, ignored otherwise. A NAIF spacecraft ID (-82 or '-82' for Cassini) or a name a furnished SCLK kernel maps. e.g. -82.",
"title": "Spacecraft"
}
},
"required": [
"value",
"from_scale",
"to_scale"
],
"title": "spice_time_convertArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
}
},
"additionalProperties": false,
"description": "A time converted between the SPICE kernel-defined systems ET / UTC / SCLK.",
"properties": {
"value": {
"anyOf": [
{
"$ref": "#/$defs/Quantity"
},
{
"type": "string"
}
],
"description": "The converted time. For an ET target a {value, unit} quantity in 's past J2000 TDB' (seconds past the J2000 TDB epoch); for a UTC target an ISO 8601 calendar string (e.g. '2026-01-01T00:00:00.000000' \u2014 no zone suffix, the scale is UTC by `to_scale`); for an SCLK target the spacecraft-clock string.",
"title": "Value"
},
"from_scale": {
"description": "The input time system, echoed from the request (ET / UTC / SCLK).",
"enum": [
"ET",
"UTC",
"SCLK"
],
"title": "From Scale",
"type": "string"
},
"to_scale": {
"description": "The output time system the `value` is expressed in, echoed from the request.",
"enum": [
"ET",
"UTC",
"SCLK"
],
"title": "To Scale",
"type": "string"
},
"spacecraft": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "The spacecraft whose clock was used, echoed as a string when either scale is SCLK; null for a conversion that does not touch SCLK.",
"title": "Spacecraft"
}
},
"required": [
"value",
"from_scale",
"to_scale"
],
"title": "SpiceTimeConvertResponse",
"type": "object"
}
spice_unload_kernel¶
Unload a previously furnished SPICE kernel from the process kernel pool, e.g. spice_unload_kernel('/path/to/de440.bsp') to drop a stale ephemeris before furnishing a newer one. Unload by the name returned from spice_load_kernel (or shown by spice_list_kernels), not the original URL — a name that is not loaded is a typed error rather than a silent no-op. Returns the remaining pool count.
Input schema:
{
"properties": {
"name": {
"description": "The name of the kernel to unload \u2014 the `name` returned by spice_load_kernel or shown by spice_list_kernels, not the original URL. e.g. '/data/de440.bsp'. A name that is not loaded returns a typed error.",
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "spice_unload_kernelArguments",
"type": "object"
}
Output schema:
{
"additionalProperties": false,
"description": "Confirmation that a kernel was unloaded, plus the remaining pool size.",
"properties": {
"unloaded": {
"description": "The name of the kernel that was unloaded; echoes the `name` argument.",
"title": "Unloaded",
"type": "string"
},
"remaining_count": {
"description": "Number of kernels still furnished in the pool after the unload. A cardinality, not a physical quantity \u2014 unitless.",
"title": "Remaining Count",
"type": "integer"
}
},
"required": [
"unloaded",
"remaining_count"
],
"title": "SpiceUnloadKernelResponse",
"type": "object"
}
time_convert¶
Convert a time value between scales (UTC / TAI / TT / TDB / UT1 / GPS / TCB / TCG) and formats (iso / jd / mjd / j2000_seconds / unix). e.g. time_convert(value='2026-05-23T12:00:00', from_scale='UTC', to_scale='TAI') returns '2026-05-23T12:00:37' (the current leap-second-aware offset). UTC -> TAI is exactly TAI - UTC = 37 s as of 2026 but historical conversions need leap-second-aware machinery — don't subtract 37 yourself; the tool does it right. GPS -> UTC respects the GPS-UTC offset (currently 18 s). UTC -> UT1 sources the small (<1 s) offset from IERS Bulletin A — the response includes ut1_utc_seconds and an iers_fetched_at anchor (ISO 8601). Format hint: iso is the safest interchange; jd is the Julian Date as a float (limited precision near present-day dates); mjd is Modified Julian Date; j2000_seconds is seconds since J2000 (2000-01-01T12:00:00 TT); unix is seconds since 1970-01-01T00:00:00 UTC.
Input schema:
{
"$defs": {
"TimeScale": {
"description": "Time-scale identifiers used across the time/frame/access tool surfaces.\n\nInherits from ``str`` so pydantic round-trips the enum as its string\nvalue in JSON (``\"UTC\"`` rather than ``\"TimeScale.UTC\"``).",
"enum": [
"UTC",
"TAI",
"TT",
"TDB",
"UT1",
"GPS",
"TCB",
"TCG"
],
"title": "TimeScale",
"type": "string"
}
},
"properties": {
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
],
"description": "The epoch to convert. String for ISO 8601 inputs ('2024-01-01T00:00:00Z'); number for numeric formats (JD, MJD, J2000-seconds, Unix). The exact interpretation is set by `in_format`.",
"title": "Value"
},
"from_scale": {
"$ref": "#/$defs/TimeScale",
"description": "Time scale of the input value: UTC, TAI, TT, TDB, UT1, GPS, TCB, or TCG. Conversion to/from UT1 triggers an IERS Bulletin A lookup; other scales are deterministic."
},
"to_scale": {
"$ref": "#/$defs/TimeScale",
"description": "Output time scale. Same enum as `from_scale`. Identity (from==to) is a valid no-op that still returns a structured response."
},
"in_format": {
"default": "iso",
"description": "How to parse `value`: 'iso' (default; ISO 8601 string with a mandatory time component), 'jd' / 'mjd' (Julian / Modified Julian Date as a number), 'j2000_seconds' (seconds since 2000-01-01T12:00:00 TT), or 'unix' (POSIX seconds).",
"enum": [
"iso",
"jd",
"mjd",
"j2000_seconds",
"unix"
],
"title": "In Format",
"type": "string"
},
"out_format": {
"default": "iso",
"description": "How to render the converted output. Same enum as `in_format`. Pick a numeric format when downstream wants math; pick 'iso' when downstream wants human-readable strings.",
"enum": [
"iso",
"jd",
"mjd",
"j2000_seconds",
"unix"
],
"title": "Out Format",
"type": "string"
}
},
"required": [
"value",
"from_scale",
"to_scale"
],
"title": "time_convertArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"Quantity": {
"additionalProperties": false,
"description": "Pydantic model for a scalar ``{value, unit}`` payload.\n\nUsed as the field type in every tool's pydantic output schema where the\nfield carries a scalar physical quantity. The JSON-schema export is what\nthe unit-discipline meta-test recognises as \"correctly wrapped\".",
"properties": {
"value": {
"description": "Numeric value of the quantity.",
"title": "Value",
"type": "number"
},
"unit": {
"description": "Unit string from the allowed-units registry.",
"title": "Unit",
"type": "string"
}
},
"required": [
"value",
"unit"
],
"title": "Quantity",
"type": "object"
},
"TimeScale": {
"description": "Time-scale identifiers used across the time/frame/access tool surfaces.\n\nInherits from ``str`` so pydantic round-trips the enum as its string\nvalue in JSON (``\"UTC\"`` rather than ``\"TimeScale.UTC\"``).",
"enum": [
"UTC",
"TAI",
"TT",
"TDB",
"UT1",
"GPS",
"TCB",
"TCG"
],
"title": "TimeScale",
"type": "string"
}
},
"additionalProperties": false,
"description": "Converted time value plus UT1/IERS metadata when relevant.",
"properties": {
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "number"
}
],
"description": "Converted time value in the requested out_format and to_scale. A string for `iso`; a float for `jd`, `mjd`, `j2000_seconds`, `unix`.",
"title": "Value"
},
"scale": {
"$ref": "#/$defs/TimeScale",
"description": "The time scale the output value is expressed in. Matches the requested `to_scale` for the scale-bound formats (iso / jd / mjd). For the absolute formats it is the fixed anchor instead \u2014 UTC for `unix`, TT for `j2000_seconds` \u2014 since those values do not depend on `to_scale`."
},
"format": {
"description": "The output format.",
"enum": [
"iso",
"jd",
"mjd",
"j2000_seconds",
"unix"
],
"title": "Format",
"type": "string"
},
"ut1_utc_seconds": {
"anyOf": [
{
"$ref": "#/$defs/Quantity"
},
{
"type": "null"
}
],
"default": null,
"description": "UT1-UTC offset used by the conversion when `from_scale` or `to_scale` is UT1 (s). None for conversions that do not touch UT1."
},
"iers_fetched_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "IERS Bulletin A freshness anchor (ISO 8601 UTC). Non-null when the conversion path touched UT1 and consulted IERS.",
"title": "Iers Fetched At"
}
},
"required": [
"value",
"scale",
"format"
],
"title": "TimeConvertResponse",
"type": "object"
}
tle_lookup¶
Fetch current two-line element sets (TLEs) from CelesTrak or Space-Track by NORAD catalogue ID or satellite name; CelesTrak additionally supports a group/category keyword for bulk fetches. Returns parsed OMM JSON plus the raw two-line strings. e.g. tle_lookup('25544') for the ISS, tle_lookup('HST') for the Hubble Space Telescope, or tle_lookup('stations') for the multi-satellite space-stations category. Name parameters are case-insensitive substring matches against the catalog name, which is often abbreviated — Hubble Space Telescope is 'HST', not 'HUBBLE' (a name search for 'HUBBLE' returns unrelated Spire smallsats). If a name lookup returns the wrong satellite or no results, fall back to the NORAD ID. Use source='celestrak' (the default) for everyday queries — no auth, soft per-IP cap, supports group keywords ('active', 'stations', 'weather', 'visual', 'science', 'geo', 'gnss', 'military', 'last-30-days', 'starlink', 'oneweb'). Use source='space-track' for recently-launched objects not yet on CelesTrak, deeper historical GP records, or when CelesTrak is unreachable. Requires a Space-Track account; without credentials the call raises credential_required.spacetrack without contacting the upstream. Group keywords have no Space-Track equivalent — passing one with source='space-track' falls through to a name substring search and typically returns nothing useful.
Input schema:
{
"properties": {
"query": {
"description": "What to look up: a NORAD catalogue ID like '25544', a satellite name (case-insensitive substring match against the catalog name \u2014 prefer the exact catalog spelling like 'ISS (ZARYA)' or 'HST', not the colloquial 'iss' or 'HUBBLE'), or (CelesTrak only) a group keyword ('active', 'stations', 'weather', 'visual', 'science', 'geo', 'gnss', 'military', 'last-30-days', 'starlink', 'oneweb'). If a name lookup returns the wrong satellite or zero results, fall back to the NORAD ID.",
"title": "Query",
"type": "string"
},
"source": {
"default": "celestrak",
"description": "Upstream catalogue to query. 'celestrak' (the default) is no-auth and supports group keywords. 'space-track' requires a Space-Track account passed via the credential channel; use it for recent launches not yet on CelesTrak, deeper historical GP records, or as a fallback when CelesTrak is unreachable.",
"enum": [
"celestrak",
"space-track"
],
"title": "Source",
"type": "string"
}
},
"required": [
"query"
],
"title": "tle_lookupArguments",
"type": "object"
}
Output schema:
{
"$defs": {
"TleResult": {
"additionalProperties": false,
"description": "A single TLE-lookup result.\n\nCarries the satellite name, NORAD catalogue ID (as a string \u2014 NORAD IDs\nare categorical identifiers, not measurements), the raw two-line element\nstrings, the parsed OMM JSON, and per-result freshness flags propagated\nfrom the upstream-data adapter.",
"properties": {
"name": {
"description": "Satellite name as carried in OMM.OBJECT_NAME, e.g. 'ISS (ZARYA)'.",
"examples": [
"ISS (ZARYA)",
"HST"
],
"title": "Name",
"type": "string"
},
"norad_id": {
"description": "NORAD catalogue ID as a string. Stable per-satellite identifier; leading zeros preserved. e.g. '25544' for the ISS.",
"examples": [
"25544",
"20580"
],
"title": "Norad Id",
"type": "string"
},
"tle_line1": {
"description": "First TLE line, exactly 69 characters (starts with '1 ').",
"examples": [
"1 25544U 98067A 24001.50000000 .00010000 00000-0 18000-3 0 9990"
],
"title": "Tle Line1",
"type": "string"
},
"tle_line2": {
"description": "Second TLE line, exactly 69 characters (starts with '2 ').",
"examples": [
"2 25544 51.6400 90.0000 0001000 90.0000 270.0000 15.50000000000000"
],
"title": "Tle Line2",
"type": "string"
},
"omm": {
"additionalProperties": true,
"description": "Parsed OMM JSON (CCSDS standard fields like CCSDS_OMM_VERS, EPOCH, MEAN_MOTION, \u2026) returned by CelesTrak. CelesTrak-metadata fields that the upstream occasionally omits are backfilled with CCSDS-spec defaults.",
"title": "Omm",
"type": "object"
},
"fetched_at": {
"description": "UTC ISO 8601 timestamp when this TLE was retrieved from the upstream (or originally cached, when stale=true).",
"examples": [
"2026-05-23T12:00:00Z",
"2026-01-01T00:00:00.500Z"
],
"title": "Fetched At",
"type": "string"
},
"stale": {
"description": "True when CelesTrak was unreachable and the cached value was returned as a stale fallback. Operators should treat the TLE as best-effort.",
"title": "Stale",
"type": "boolean"
}
},
"required": [
"name",
"norad_id",
"tle_line1",
"tle_line2",
"omm",
"fetched_at",
"stale"
],
"title": "TleResult",
"type": "object"
}
},
"additionalProperties": false,
"description": "Top-level response from :func:`tle_lookup`.\n\nA list of :class:`TleResult`. Single-satellite queries return a single\nelement; group/category queries return one element per satellite in the\ncatalogue.",
"properties": {
"results": {
"description": "List of matching TLE results, one per satellite returned by CelesTrak.",
"items": {
"$ref": "#/$defs/TleResult"
},
"title": "Results",
"type": "array"
}
},
"required": [
"results"
],
"title": "TleLookupResponse",
"type": "object"
}