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 onspectro_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:
- Data types -
Spectrum1DandWorkContext. - Algorithms & the registry - the
BaseAlgorithmcontract. - Pipelines - composing algorithms and running presets.