Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

MARS — MCP Astronomy Research Suite

CI Python 3.13

An agentic, tool-enabled system for automated astronomy — an LLM agent, named MARS (MCP Astronomy Research Suite), that plans and executes astronomy tasks by calling a registry of purpose-built tools backed by algorithms extracted from two production systems.

Contents

Overview

MARS is an agentic, tool-enabled system for automated astronomy: an LLM agent that plans and executes astronomy research and data-reduction tasks — literature and catalog search, target resolution, and, as the underlying algorithms come online, WCS plate solving, photometry, and photometric calibration — by calling a registry of purpose-built tools. See The Agent for the agent and its reusable local pipeline.

This repository is where the agent's tools, and the algorithms they call, are built and staged. Its current state is a small installable Python tool collection, extracted astronomy algorithms, a split database-query tool surface, and an architecture document for turning those pieces into a coherent tool surface.

The algorithms come out of two production systems built around the Skynet Robotic Telescope Network, operated by the University of North Carolina at Chapel Hill: Skynet itself, and Astromancer, its companion web application for light-curve, periodogram, and star-cluster (HR-diagram) analysis. The Skynet algorithms were extracted directly into Python; the Astromancer light-curve, periodogram, pulsar, and HR-diagram algorithms were ported from TypeScript into Python.

The Skynet packages are extractions, not rewrites: algorithms, constants, comments, and known bugs are byte-preserved from their source systems. The Astromancer ports preserve their documented numerical behavior while replacing browser and UI seams with headless Python interfaces. See Highlights below and docs/extraction.md for the provenance and named divergences.

The Agent

MARS has one model-driven entry point — the mars console — and a reusable local pipeline, both built on the plain Python functions in tools/. Running the Console is the guide; this section is what it is made of:

  • mars — the interactive console. The full-screen Textual application over the agent loop: a transcript that keeps your question in view and renders each tool call as it runs, the model's reasoning where the provider reveals it, image and waveform previews of the artifacts a run produces, session browsing and resume, and slash commands. A turn is something you are in rather than something you wait out — the prompt stays open while the model works, a note typed mid-run reaches the next turn alongside the tool results, and escape stops the run at its next safe point. It takes no required arguments, because everything it needs — the backend and the model included — is chosen inside the session.

  • tools/agent/ — the loop the console drives. A bounded tool-use loop (max_turns=20) over tools.registry.TOOL_SCHEMAS: one schema per remote database — SIMBAD, NED, VizieR, ATNF, MAST, MPC, CASDA, and ADS — plus SIMBAD-backed target resolution, local aperture photometry on a bundled FITS library (tools.photometry), a local pulsar pipeline (tools.pulsar: ingest a scan, compute its periodogram, fold it into a pulse profile, sonify it, and plot any stage), and an HR-diagram pipeline (tools.hr_diagram) with two entry points: a catalog-only one that needs nothing but a cluster name (fetches Gaia DR3 directly around the cluster's own resolved position) and a FITS-frame one for a user who has their own plate-solved frame and wants that frame's own photometry driving the fit — both end at the same literature comparison, isochrone fit, and plot — and a radio-source pipeline (tools.radio_sources.plot_field_sed): identify sources in a processed radio FITS frame against VizieR's radio catalogs, then plot every identified source's spectral energy distribution from NED together on one labeled plot, each with its own fitted spectral index. Its system prompt encodes per-database quirks confirmed by direct testing (NED's resolver fails on colloquial names where SIMBAD's succeeds; MPC and ATNF do zero name resolution and require formal designations; ADS needs fielded queries, not natural language), sourcing discipline (quote a paper's abstract before attributing a number to it), and repeat-call caching, so an identical tool call costs no extra network round trip. Every run also writes a session manifest (tools.sessions.AgentSession) recording each turn and tool call — including cache hits — under artifacts/sessions/<session_id>/session_manifest.json, so a session's tool-call history can be inspected or replayed after the fact (tools.sessions.list_session_manifests / read_session_manifest). It drives a provider-neutral model port (tools/llm/: Anthropic, OpenAI-compatible, Ollama, Gemini) and imports no UI toolkit, so it is equally callable from plain Python:

    from tools.agent.engine import run_session
    from tools.llm.factory import build_backend
    
    for event in run_session(
        "all historical radio data on Cassiopeia A",
        backend=build_backend("ollama/qwen3.8:27b-mlx"),
    ):
        print(event)
    
  • tools/photometry_pipeline.py — reusable automated photometry. It loads a FITS image, runs source extraction and aperture photometry, resolves an optional verified zero point through a live field-calibration catalog solve, and saves plots. tools.photometry (list_photometry_targets, run_photometry_on_target) is the public wrapper over this same pipeline. The retired standalone CLI and direct Claude summary are not part of this module. Listing and resolving a target are offline; running field calibration is not. run_photometry_on_target defaults to use_field_cal=True, which queries VizieR for reference magnitudes. Pass use_field_cal=False for instrumental magnitudes, or provide zero_point_mag when a trusted value is already available. The recorded-solve replay — tools.photometry.calibrate_zeropoint(path, catalog_fixture="selected_rows", compare_to="ngc5128_b_002") injects the APASS rows Skynet actually matched, so extraction → photometry → matching → reference magnitude → solve all run for real with no socket opened, and catalog_fixture="full_response" does the same from the recorded APASS response for the whole field (with the recorded VSX variables filtered out), so the matches are chosen rather than given. Either way compare_to checks the answer against the recorded Skynet solve. tools.fieldcal_reference.replay_field_calibration("ngc5128_b_002") is the bit-exact form of the second path: the recorded detections through the whole selection — 132 rows in the cone, 45 on the frame after the live path's clipping, the recorded 35 chosen — and 21.147659857998637 exactly. Only ngc5128_galaxy_b_001.fits can be driven that way end to end; the three NGC 5286 solves have no bundled frame and no recorded response. Neither this nor tools.photometry calibrates against Gaia or fits an isochrone — for that, see tools.hr_diagram above; run_photometry_on_target(..., write_source_table=True) writes a CSV in the column shape tools.hr_diagram.crossmatch_gaia expects, as a bridge between the two when a bundled photometry target turns out to be a cluster.

All three call directly into the same plain Python functions and extracted algorithm packages described below — an agent's tool call is the identical function any other caller would import and run.

Highlights

  • Provider-neutral agent surface. The mars console, and the tools/agent/ loop under it, run a bounded tool-use loop over eight remote astronomy databases — Anthropic by default, or an OpenAI-compatible, Ollama, or Gemini backend, chosen with /backend in the session or with MARS_MODEL_BACKEND before it starts. The registered tools.photometry wrapper runs the local FITS photometry pipeline without a separate model client. See The Agent.
  • Explicit extraction and port contracts. Severed dependencies in the byte-preserved Python extractions are marked with # EXTRACTED: was <symbol>; language translations use # PORTED: markers. Documented parity quirks and known upstream bugs are deliberately kept rather than "fixed" in transit.
  • Bit-exact parity test suite. tests/ backs the Python algorithms with 42 real PROMPT/Skynet FITS frames and four complete recorded Skynet zero-point solves, checked bit-for-bit against production output. These aren't tests of whether the algorithms are right — that was settled upstream — they're tests of whether the extraction still does exactly what Skynet did, wrong parts included. See tests/README.md and Testing & Validation.
  • Strict domain ownership. algorithms/wcs/, algorithms/photometry/, and algorithms/fieldcal/ deliberately do not import each other; the same discipline holds between algorithms/catalogs/ (declarations, no network) and algorithms/query/ (the only layer that opens a socket). Cross-domain values are passed in explicitly by the caller rather than injected through a seam, and one public tool call is the whole execution boundary — no run, stage, or session state survives it.

Current Contents

Path Status What it contains
tools/ Python tools Plain Python wrappers for local frame discovery (tools/optical.py), WCS description and plate solving (tools/astrometry.py, tools/wcs.py), catalog metadata, reference-band resolution, zero-point solving and the recorded-solve references (tools/fieldcal_reference.py), local artifact inspection, remote database/archive queries, local aperture photometry (tools/photometry.py), the pulsar pipeline (tools/pulsar.py), and FITS-to-HR-diagram pipeline orchestration (tools/hr_diagram.py).
tools/agent/, tools/llm/, tools/tui/, tools/registry.py, tools/sessions.py Python agent The tool-use loop behind the mars console: the headless engine (tools/agent/), the provider-neutral model port (tools/llm/: Anthropic, OpenAI-compatible, Ollama, Gemini), the Textual console (tools/tui/), the tool-schema registry, and the per-run session manifest recorder. See The Agent.
tools/bench/, benchmarks/ Python agent The model benchmark harness (mars-bench) and its corpus. Answers which model is better on this tool surface and at what cost in work: it reads the registry and the session manifest, replays the 22 remote tools from recorded fixtures, runs the 26 local ones live, and grades on four axes. It owns no tool. Offline and deterministic -- the smoke suite runs inside a plain uv run pytest with no key and no socket. See docs/tool-architecture.md 10.1.
tools/photometry_pipeline.py Python pipeline Reusable FITS photometry, zero-point resolution, and plotting implementation used by tools.photometry; it has no CLI or model-provider client.
algorithms/wcs/ Extracted Python algorithm Skynet WCS calibration: source extraction, FITS-header hinting, astrometry.net solve-field, ATLAS triangle solving, solution validation, and FITS-header write-back.
algorithms/photometry/ Extracted Python algorithm Skynet source extraction and aperture photometry using the shared algorithms/skylib_lite/ Skylib subset.
algorithms/fieldcal/ Extracted Python algorithm Skynet photometric zero-point calibration: catalog-source matching, variable-star filtering, reference-magnitude resolution, and weighted zero-point solving.
algorithms/skylib_lite/ Shared Python support Consolidated local Skylib subset used by WCS, photometry, and field calibration: astrometry, SEP extraction, background estimation, aperture photometry, FITS helpers, angle math, and statistics.
algorithms/catalogs/ Extracted Python algorithm Skynet and Afterglow photometric catalog declarations, SIMBAD object-type vocabulary, and provider lookup tables used by ADS/NED/ATNF tools. Declaration only — no network code.
algorithms/query/ Extracted Python algorithm Skynet and Afterglow remote catalog access: the VizieR engine, SDSS SkyServer SQL, SIMBAD identifier resolution, astroquery cache handling, filter-aware catalog selection, and WCS-footprint query orchestration.
algorithms/hrdiagram_py/ Python parity port + new capability Star-cluster CMD/HR-diagram fitting: CM↔HR transform, extinction, isochrone loading, a distance/E(B-V)/age optimizer Astromancer's own tool never had, field-star removal, and geometric catalog matching. Not a byte-preserving extraction — see docs/extraction.md, "HR Diagram (Python)".
algorithms/radio/ New Python capability Radio spectral-index/log-parabola flux-vs-frequency fitting and generic RA/Dec-column-guessing catalog cross-matching. No upstream Skynet/Astromancer equivalent.
algorithms/pulsar/ Ported Python algorithm The four-stage pulsar chain: file ingest and background subtraction, Lomb-Scargle periodogram, phase folding and binning, and light-curve sonification. A port of the Astromancer TypeScript, not an extraction — see docs/pulsar-tool-pipeline.md.
algorithms/variable_star/ Ported Python algorithm Exact-parity Astromancer variable-star ingestion, differential light curves, weighted Lomb-Scargle periodograms, and period folding.
tests/ Python test suite Algorithm-preservation and tool-smoke tests: bit-exact parity against recorded Skynet output, real FITS fixtures, and no-network coverage of the public tools/ surface. See tests/README.md.
docs/ Documentation Reference documents at the top level, docs/analysis/ for point-in-time reviews, docs/benchmarking/ for the model benchmark, docs/archive/ for completed track documents, docs/working/ for in-progress plans, and one committed sample output in docs/examples/. docs/README.md is the map; Architecture & Further Reading lists what each one covers.

The consolidated extraction record in docs/extraction.md captures provenance, severed framework dependencies, known parity behaviors, dependency notes, and verification already performed for every extracted algorithm package.

Repository Shape

MARS/
  pyproject.toml                 # Python package metadata and dependencies
  uv.lock                        # uv lockfile for reproducible installs
  CONTRIBUTING.md                # contribution guidelines
  AGENTS.md                      # repository guidelines for agentic contributors
  docs/
    README.md                    # documentation map + lifecycle
    extraction.md                # master algorithm extraction record
    tool-architecture.md         # master package architecture
    repository-folders.md        # per-folder guide
    pulsar-tool-pipeline.md      # the four-stage pulsar tool chain
    installing.md                # installing with no checkout; the MCP server
    releasing.md                 # release policy and the data bundles
    analysis/                    # point-in-time algorithm/design reviews
    benchmarking/                # the model benchmark: design, results, figures
    archive/                     # completed track documents, kept as records
    working/                     # in-progress plans
    examples/                    # one committed sample output
    assets/                      # the README banner
  tools/                         # public Python tool wrappers and shared models
    agent/ llm/ tui/ bench/      #   the loop, the model port, the console, the benchmark
    mcp/                         #   the MCP server (mars-mcp) and data bundles
    skill/                       #   the agent skill's one source and renderer
    _data -> ../data             #   bundled data: a symlink here, core data in a wheel
  skills/mars-tools/           # the rendered agent skill (generated; see tools/skill)
  algorithms/
    wcs/                         # Python WCS extraction from Skynet
    photometry/                  # Python photometry extraction from Skynet
    fieldcal/                    # Python zero-point calibration extraction
    skylib_lite/                 # shared vendored Skylib subset
    catalogs/                    # Python catalog declarations (no network code)
    query/                       # Python remote catalog access (VizieR/SDSS/SIMBAD)
    pulsar/                      # Python port of the Astromancer pulsar chain
    variable_star/               # Python port of Astromancer variable-star algorithms
    hrdiagram_py/                # Python HR-diagram port plus a new optimizer
    radio/                       # new Python radio SED capability
  benchmarks/                    # model benchmark corpus and fixtures
  tests/                         # pytest suite (algorithm-preservation + tool smoke)
  data/                          # the data root: fixture frames, recorded
                                 #   reference outputs, and the (untracked)
                                 #   fits_downloads/ archive download root

Getting Started

Fixture Data and Git LFS

Almost all of data/ is plain git and arrives with a normal clone. Three frames are the exception — data/optical/ngc5286_globular_b_{000,001,002}.fits, 93 MB of Git LFS objects — so a clone made without LFS leaves a small text pointer in place of each:

git lfs install && git lfs pull      # fetch the three NGC 5286 B frames

They are optional. Without them the suite still passes: the tests that need those pixels skip themselves with the command above, list_optical_frames reports a frames_not_checked_out warning rather than the frames, and resolve_optical_frame returns a frame_not_checked_out error. What they buy is the end-to-end pixel path for three of the four recorded zero-point solves — without them, those three are checked at the calc_solution level only. data/README.md has the detail.

Python Setup

Use uv to create the virtual environment and install the Python package with its dependencies:

uv sync

pyproject.toml is the installable package metadata and includes the Python dependencies needed by the split database tools and extracted algorithm modules. uv.lock records the resolved dependency set.

Some extracted runtime paths also require non-Python solver data called out in docs/extraction.md, including astrometry.net index files and local UCAC4/UCAC5 catalogs.

Using MARS from Your Own Coding Agent

MARS's tools are also served over MCP, so Claude Code, Codex, Cursor or any other MCP host can call them from a machine that has no checkout of this repository. Install a release — Python 3.13 is the target — and register its server:

python3.13 -m venv mars-env
mars-env/bin/pip install "skynet-mars[mcp] @ https://github.com/SkynetRTN/MARS/releases/download/v0.1.0rc3/skynet_mars-0.1.0rc3-py3-none-any.whl"
mars-env/bin/mars-mcp self-test          # launches the server as a host would, runs a pulsar detection
{"mcpServers": {"mars": {"command": "/path/to/mars-env/bin/mars-mcp"}}}
  • Data. The wheel carries the core data (the pulsar scans and the zero-point references). The optical frame library and the isochrone grid are optional bundles: mars-mcp fetch-data optical (or isochrones, or all). Each is checksum-verified against the install and resumes if interrupted.
  • Scope. mars-mcp --tools databases,timeseries serves only some of the five tool groups: databases, optical, timeseries, hr, radio.
  • The skill. The server brings its own usage guidance — stage orders, identifier forms, period provenance — as its instructions and as mars://skill/... resources. In a checkout, the same skill is skills/mars-tools/.
  • Where files go. Artifacts and downloads go under a per-user MARS home (~/.local/share/mars, or MARS_HOME), never into the install.
  • A C compiler may be needed. On Python 3.14 or on Linux ARM, two dependencies compile from source.

docs/installing.md covers all of this, and docs/releasing.md how releases are cut.

Running the Console

mars is the one model-driven entry point, and it takes no required arguments — everything it needs is chosen inside the session:

uv run mars
╭─ M A R S ──────────────────────────────────────────────────────────────────╮
│ astronomy research console · anthropic/claude-sonnet-5                     │
╰────────────────────────────────────────────────────────────────────────────╯

  › how far away is M31?

  Session 20260918T164552Z_2ac41045d42a started.

  Turn 1 started.

  ▊  ✻ thinking
  ▊  NED's resolver is weaker on colloquial names than SIMBAD's, so
  ▊  resolve first.

  ✓ search_simbad  (696 ms)

  Turn 1 finished: tool_use.

  Turn 2 started.

  M31 is the Andromeda Galaxy, 2.5 Mly away.

  Turn 2 finished: end_turn.

  Session finished: end_turn.

 ▊▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▎
 ▊  Ask MARS…                                                             ▎
 ▊▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▎
  2/20 turns • 1 artifact • halfblock graphics • F3 artifacts • F4 sessions

The header names the session's provider/model; the status bar counts turns against the ceiling, token usage once a turn reports it, the artifacts written, and the graphics tier detected for this terminal. Everything between is the transcript: what you asked, the model's reasoning where its provider reveals it, each tool call as it runs, and the answer.

It needs a backend before it will answer anything — a key for the provider it opens on, or a local Ollama daemon, which needs none. If the default cannot be used, the console still opens and says why; /backend fixes it from inside the session. See Model Backend Configuration.

Slash commands never reach the model. Type / and the console lists them; Tab completes one match whole and several only as far as they agree. A doubled leading slash (//) escapes to a literal one, for the rare question that starts with a slash.

Command Aliases Does
/help /? List the commands, generated from the registry.
/backend [name|spec] [model] /b List the backends, or switch. See below.
/artifacts /a Browse what this session wrote, with previews.
/sessions /s Browse saved sessions and resume one.
/resume <id> /r Resume a saved session by id.
/quit /q, /exit Exit.

/status, /tools, /approve, /prompt and /new are registered and offered by the menu, but not implemented yet: they answer with a notice rather than doing anything.

Key Does
Tab Complete the slash command being typed.
F3 / F4 Artifact browser · session browser.
Esc Stop the running turn at its next safe point; close a browser.
o In the artifact browser, open the selected file in the desktop handler.
Ctrl+Q Quit.

Choosing a backend and a model. /backend on its own lists what is offered and marks the running one. /backend ollama opens a picker of the models that daemon actually holds, marking the one in use and the default; /backend anthropic, or an explicit /backend ollama/qwen3.8:27b-mlx, switches straight over. A switch happens only after the new backend is built and probed, so a stopped daemon or a missing key costs a command rather than the session — the transcript says which backend is still answering.

uv run mars --backend ollama       # or start on the local daemon
uv run mars --thinking-budget 0    # or without asking for reasoning
uv run mars --max-turns 40         # or with a higher ceiling than 20

A turn is something you are in. The prompt never closes while the model works: type into it and the note is queued, marked queued until the engine reports it delivered, and merged into the next turn alongside the tool results. Esc stops the run at its next safe point — the step in flight has to finish first, and the console says so rather than pretending otherwise. What ran is saved and resumable either way.

Everything a run writes is kept. Tool artifacts land under artifacts/sessions/<session_id>/, and F3 browses them with image and waveform previews at whatever tier the terminal supports. The session manifest beside them records every turn and tool call, which is what /sessions and /resume read back.

Python Entry Points

ADS queries require ADS_DEV_KEY. The optional agent loop — the mars console and tools/agent/ under it — needs a model backend: a key for the provider it opens on, or a local Ollama daemon, which needs none. The backend is selectable inside the session with /backend, so this is a question of which one you start on (see Configuration).

The plain Python tools live under tools:

from tools.optical import list_optical_frames, resolve_optical_frame
from tools.astrometry import describe_image_wcs
from tools.wcs import solve_astrometry
from tools.catalogs import list_photometric_catalogs, resolve_reference_band
from tools.calibration import solve_zeropoint_from_measurements
from tools.fieldcal_reference import (
    compare_zeropoint_to_reference,
    list_zeropoint_references,
    load_zeropoint_reference,
    replay_catalog_sources,
    replay_field_calibration,
)
from tools.simbad import search_simbad
from tools.vizier import search_vizier
from tools.ned import search_ned
from tools.ads import search_ads, build_literature_review
from tools.mast import search_mast
from tools.mpc import search_mpc
from tools.atnf import search_atnf
from tools.casda import search_casda
from tools.photometry import (
    calibrate_zeropoint,
    list_photometry_targets,
    run_photometry_on_target,
)
from tools.pulsar import load_pulsar_lightcurve, compute_pulsar_periodogram
from tools.hr_diagram import run_full_hr_pipeline, run_full_hr_pipeline_from_catalog
from tools.workspace import describe_artifact, list_artifacts

The same tools are wired into a tool-use loop by the console:

uv run mars

Advanced callers can still import the extracted algorithm packages directly:

from algorithms.wcs.wcs import solve_wcs
from algorithms.photometry.photometry import run_photometry
from algorithms.photometry.source_extraction import run_source_extraction
from algorithms.fieldcal import perform_field_calibration, calc_solution
from algorithms.query.runner import query_catalogs
from algorithms.query.simbad import resolve_simbad

fieldcal deliberately does not own WCS, photometry, or catalogs, and it has no dependency-injection seam to wire: perform_field_calibration takes the header, the image array, and then wcs, catalog_sources, optional variable_sources, and any extraction/photometry/calibration settings as explicit keyword arguments. Fetching the catalog rows is the caller's job — algorithms/fieldcal/ opens no socket — which is what keeps the zero-point solve runnable with no network stack installed. In this repo the tool layer is that caller; see tools.photometry.calibrate_zeropoint, and pass it catalog_fixture= (or catalog_sources= from tools.fieldcal_reference.replay_catalog_sources) for a fully offline solve.

Catalog metadata and catalog access are separate on purpose. Import algorithms.catalogs for band tables, colour transforms, and provider vocabularies; it is pure data and pulls in no network stack. Import algorithms.query.registry when you need to actually fetch sources.

Testing & Validation

MARS's Python folders are byte-preserving extractions from Skynet, so tests/ is not there to check whether the algorithms are right — that question was settled upstream. It checks whether the extraction still does exactly what it did before, including the parts that are wrong. Fixtures are 42 real PROMPT/Skynet frames and four complete recorded Skynet zero-point solves; the centerpiece, test_fieldcal_solution.py, feeds calc_solution the exact rows Skynet fed it and compares against the exact numbers Skynet returned, bit-for-bit, on four fields. Known upstream defects are pinned by name in the suite rather than fixed silently — see tests/README.md for the full list and for what is deliberately not yet covered.

For Python changes, run the same checks CI enforces:

python3 -m compileall tools algorithms tests
uv run pytest
git diff --check

Astromancer-derived behavior runs through the Python ports in algorithms/pulsar/, algorithms/variable_star/, and algorithms/hrdiagram_py/. Their parity tests pin the numerical behavior that MARS uses; docs/extraction.md retains the retired TypeScript extraction record and the upstream source locations.

The extraction notes record broader one-off checks such as compile/import smoke tests, source diffs, and selected behavior checks. Full end-to-end WCS, photometry, and calibration parity beyond the bundled pytest fixtures still requires solver binaries and local catalog data.

Configuration

Model Backend Configuration

The agent loop's model backend is chosen by a provider/model spec, split on the first slash only. With MARS_MODEL_BACKEND unset it uses Anthropic.

  • MARS_MODEL_BACKEND: e.g. anthropic/claude-sonnet-5, openai/gpt-4.1, ollama/qwen3.8:27b-mlx, gemini/gemini-2.5-pro.
  • ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY: the provider key.
  • OPENAI_BASE_URL: an OpenAI-compatible endpoint. A non-default base URL needs its key passed explicitly alongside it — an environment OPENAI_API_KEY is only sent to api.openai.com.
  • OLLAMA_BASE_URL: defaults to http://localhost:11434/v1; no key.
  • OLLAMA_TIMEOUT_S: how long one local turn may take, default 600 s — a turn here is bounded by the host's hardware rather than a provider's SLA. Asking the daemon what it holds is not timed like a turn and is always bounded at five seconds.
MARS_MODEL_BACKEND=openai/gpt-4.1 OPENAI_API_KEY=... uv run mars

.env at the repository root is read at launch and on every /backend, so a key added while the console is open takes effect on the next switch. The real environment always wins over the file, and the file is gitignored.

Catalog Query Configuration

The remote catalog backends read settings from environment variables:

  • VIZIER_SERVER: VizieR mirror hostname, defaulting to vizier.cds.unistra.fr.
  • VIZIER_CACHE_ENABLED: whether astroquery caches responses on disk (default on).
  • VIZIER_CACHE_AGE_DAYS: cache retention, defaulting to 30.

With the cache enabled, query regions are snapped to a fixed grid so that near-identical fields share a cache entry. This is observable near a field edge — see docs/extraction.md, Query §5.1.

WCS Configuration

The WCS solver reads backend settings from environment variables:

  • ANET_INDEX_PATH: astrometry.net index directory or os.pathsep-separated directories.
  • ANET_TIMEOUT_S: astrometry.net low-level solve-attempt limit in seconds (minimum 1).
  • ATLAS_CATALOG_ROOT: local UCAC4/UCAC5 catalog root for the ATLAS fallback.
  • ATLAS_CATALOG: catalog name, defaulting to ucac5.
  • ATLAS_TIMEOUT_S: ATLAS matcher timeout in seconds.

ATLAS and astrometry.net serve different observing workflows. The normal solver order is astrometry.net first, then ATLAS as its fallback. For a quick local solve of a frame with trustworthy pointing and pixel-scale keywords, configure only ATLAS (leave ANET_INDEX_PATH unset): ATLAS uses the header hints to narrow its local UCAC triangle search. For a blind solve, configure astrometry.net instead: it needs solve-field on PATH (or a supported SKYLIB_* override) and indexes in ANET_INDEX_PATH.

ATLAS catalog dependency

The UCAC catalog is an operator-owned dependency, like the local HR-diagram isochrone grid: do not download, copy, or commit it under MARS. Set both variables for the installed catalog. The supplied UCAC5 tree on this host is:

export ATLAS_CATALOG_ROOT=/srv/agents/catalogs/ATLAS/UCAC5
export ATLAS_CATALOG=ucac5

Supported layouts are:

# UCAC5: ATLAS_CATALOG_ROOT may be either directory
<root>/u5z/u5index.asc
<root>/u5z/z001 ... z900

# UCAC4: ATLAS_CATALOG_ROOT is the directory holding zone files
<root>/Z000.UC4 ... Z179.UC4

The supplied complete UCAC5 tree uses 5.3 GB; reserve at least 6 GB for a local UCAC5 installation. A complete native UCAC4 tree is approximately 8.5 GB; reserve at least 10 GB. Verify the configured reader can instantiate and query the catalog without network access before running a solve:

uv run python -c "from pathlib import Path; from algorithms.skylib_lite.astrometry.atlas.catalog import get_catalog_spec; import os; root = Path(os.environ['ATLAS_CATALOG_ROOT']); catalog = os.environ.get('ATLAS_CATALOG', 'ucac5'); index = get_catalog_spec(catalog).index_factory(root); result = index.query_box(0.0, 0.25, -0.1, 0.1); print(f'{catalog}: {len(result.ra_deg)} stars in preflight box')"

For the operator-only ATLAS validation route, run:

ATLAS_CATALOG_ROOT=/srv/agents/catalogs/ATLAS/UCAC5 ATLAS_CATALOG=ucac5 \
  uv run pytest tests/test_wcs_solution.py::test_atlas_looks_up_operator_catalog_with_an_explicit_scale_window -v

For local blind astrometry.net validation on this host, use indexes under /srv/agents/catalogs/astrometry rather than the ATLAS catalog tree. If neither backend is configured, the package can still import, but end-to-end plate solving will not produce a solution.

Architecture & Further Reading

  • docs/README.md — the documentation map and the document lifecycle.
  • docs/tool-architecture.md — the master package architecture: public tools, algorithm ownership, future services, runtime policy, and skylib_lite consolidation.
  • docs/extraction.md — the consolidated provenance record for every extracted algorithm package: source paths, line ranges, severed dependencies, parity notes, and verification performed.
  • docs/repository-folders.md — a per-folder guide to responsibilities, important files, and current caveats.
  • docs/pulsar-tool-pipeline.md — the four-stage pulsar tool chain and the extracted Astromancer code behind each stage.
  • docs/analysis/algorithm-remediation-plan.md — the algorithm-review finding register and a proposed rollout for the defects pinned by the test suite.
  • docs/benchmarking/README.md — the model benchmark: what tools/bench/ measures, the 144-session sweep it produced, and the limits on reading it.
  • docs/archive/README.md — completed track documents, kept for the reasoning a reference document has no room for. Records, not current state.
  • tests/README.md — what the test suite proves, its markers, and what it deliberately does not cover yet.

Development Notes

  • Keep changes narrow and target dev unless a maintainer asks otherwise.
  • Do not commit API keys, local environment files, downloaded FITS products, generated plots, caches, or large astronomy datasets.
  • Keep live remote astronomy service calls gated; default checks should remain deterministic and bounded.
  • Preserve documented legacy-parity behavior in extracted code unless a change is explicitly intended to diverge from Skynet or Astromancer.

Contributing

See CONTRIBUTING.md for the current contribution guidance and AGENTS.md for repository guidelines aimed at agentic contributors. .github/CODEOWNERS marks the repository as maintainer-owned; changes to main require Code Owner review.

License

Copyright (C) 2026 Skynet Robotic Telescope Network.

MARS is licensed under the GNU General Public License v3.0 only (GPL-3.0-only); the full text is LICENSE. It is the licence of the Skynet projects it grew from: Astromancer and Skycat carry the same text.

The code under algorithms/ comes from three places (provenance per file in docs/extraction.md):

  • Skynet, by the same group as MARS: skylib and the optical processing in skynet-db, extracted into algorithms/skylib_lite/, wcs/, photometry/, fieldcal/ and catalogs/.
  • Astromancer, by the same group, GPL-3.0: ported to Python in algorithms/pulsar/, algorithms/variable_star/ and algorithms/hrdiagram_py/.
  • Afterglow Core, Apache-2.0: parts of the query layer in algorithms/query/. Apache-2.0 code may be combined into a GPL-3.0 work.

Files that carry their own third-party notice — for example the BSD-3-Clause header in algorithms/skylib_lite/util/overlap.py — keep it, and those terms apply to those files as well.

Metadata

Release files for skynet-mars 0.1.0rc4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for skynet-mars 0.1.0rc4
File Size Uploaded
skynet_mars-0.1.0rc4.tar.gz 2.4 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for skynet-mars 0.1.0rc4
File Interpreter ABI Platform
skynet_mars-0.1.0rc4-py3-none-any.whl Python 3 none any Details

Total release size: 4.6 MB

Release files / skynet_mars-0.1.0rc4.tar.gz

Download URL skynet_mars-0.1.0rc4.tar.gz
Size 2.4 MB
Tags Source
SHA-256 checksum
How to use checksums
df2d9101fea5631b9936b2b5d728ab933bdee8cc2df2484d5b5f56f3fdb6285e
BLAKE2b-256 checksum
How to use checksums
99d1646ad3f877a21edf25ef6b28067755a81c0f79abab253c4c626777d729b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.

Transparency log

Release files / skynet_mars-0.1.0rc4-py3-none-any.whl

Download URL skynet_mars-0.1.0rc4-py3-none-any.whl
Size 2.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
9d3cfbaf657d0aee2029185514d3e388dd358c7bd4314c666e903c4f46ea9066
BLAKE2b-256 checksum
How to use checksums
37630f19cb5f7755b00067d456144d9c8af9456c48a0e194d8b7375e8f363bb3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page