Skip to content

Architecture

This page is the map. It shows how the pieces fit together; the following pages zoom into each one.

Three layers, one direction

The whole system is three stacked layers, and data only ever flows downward:

flowchart TB
    LIB["Python library"]
    CLI["spectro CLI"]
    MCP["spectro-mcp server"]
    KERNEL["spectro-kernel - the library<br/>registry · algorithms · pipelines · types · I/O"]
    DEPS["Foundations<br/>astropy · numpy · scipy · specutils · astroquery · plotly"]

    LIB --> KERNEL
    CLI --> KERNEL
    MCP --> KERNEL
    KERNEL --> DEPS
  • Access layer (top) - three independent front-ends. None of them holds scientific logic; they only route into the kernel. That is why they cannot drift apart.
  • spectro-kernel (middle) - the library. Everything scientific lives here.
  • Foundations (bottom) - the established libraries spectro-kernel leans on (see Why spectro-kernel?).

The two packages

The repository ships two Python packages, with a strict one-way dependency:

flowchart LR
    MCP["spectro_mcp<br/>the MCP server"] -->|depends on| KERNEL["spectro_kernel<br/>the pure library"]
  • spectro_kernel - a pure library: no server, no web framework. It can be imported into any application, notebook or script.
  • spectro_mcp - the MCP server. It depends on spectro_kernel; the reverse is forbidden, so the kernel stays lightweight and server-free.

The central idea: a registry

Everything revolves around the registry. An algorithm is a small class that registers itself with one decorator:

@register_algorithm("snr_der", category=AlgorithmCategory.QUALITY, version="1.0.0")
class SnrDer(BaseAlgorithm):
    ...

From that single registration, the algorithm is reachable everywhere:

flowchart LR
    FILE["a file in<br/>algorithms/"] -->|@register_algorithm| REG["the registry"]
    REG --> LIB["Python library"]
    REG --> CLI["spectro CLI"]
    REG --> MCP["MCP tools"]
    REG --> PIPE["pipeline steps"]

Adding a file under algorithms/ is the only thing you ever do to extend the catalogue - no registration list to edit, no front-end to touch.

How data flows through an analysis

One mutable object, the WorkContext, is threaded through the whole analysis. Each algorithm reads what it needs from it and writes its results back:

flowchart LR
    IN["FITS / ASCII<br/>VOTable file"] --> CTX0["WorkContext<br/>spectrum loaded"]
    CTX0 --> A1["normalize_<br/>polynomial"]
    A1 --> A2["snr_der"]
    A2 --> A3["fit_gaussian_<br/>line"]
    A3 --> OUT["WorkContext<br/>+ metrics, line fits<br/>+ audit trail"]

Every step also appends a record to WorkContext.history - the audit trail that makes a result reproducible.

The next pages explain each box: