Skip to content

Using it as an MCP server

The MCP server is the AI-agent door. It exposes the whole catalogue to assistants like Claude through the Model Context Protocol, so an agent can analyse spectra by calling tools - the same operations you call from the library or the CLI.

It requires the mcp extra:

pip install "spectro-kernel[mcp]"

Running the server

spectro-mcp --stdio

This is the transport for desktop agents such as Claude Desktop.

spectro-mcp --http --host 0.0.0.0 --port 8000

Connecting Claude Desktop

Add the server to Claude Desktop's configuration file:

{
  "mcpServers": {
    "spectro-kernel": {
      "command": "spectro-mcp",
      "args": ["--stdio"]
    }
  }
}

Restart Claude Desktop; the spectro-kernel tools appear in the conversation.

What the agent sees

The server exposes two kinds of tools:

One tool per algorithm. Every registered algorithm becomes a tool automatically - so the agent-facing catalogue can never drift from the code. Each tool's description includes the parameters, the backend and the literature references.

Transverse tools for session and discovery:

Tool Purpose
create_session Start a working session; returns a session_id.
load_spectrum Load a FITS/ASCII/VOTable spectrum into a session.
get_session_state Inspect everything held in a session.
get_spectrum_preview A downsampled preview of the session's spectrum.
list_algorithms Discover the catalogue (optionally by category).
describe_algorithm Full details of one algorithm.
get_algorithm_source The wrapper's source code + GitHub permalink - for auditing what the algorithm actually does, especially when it delegates to an external library.
list_presets List the bundled pipeline presets.
run_preset Run a whole preset pipeline in one call.
request_upload_url Pre-signed S3/Spaces PUT URL for large file uploads (needs SPECTRO_MCP_S3_BUCKET).
load_spectrum_from_url Download a spectrum from any http(s) URL and load it into the session.
end_session Discard a session.

Why sessions?

MCP tool calls are stateless, but a real analysis is a sequence of calls sharing one spectrum. A session keeps a WorkContext alive between calls, identified by an opaque session_id.

sequenceDiagram
    participant A as AI agent
    participant S as spectro-mcp
    A->>S: create_session()
    S-->>A: session_id "s_ab12"
    A->>S: load_spectrum("s_ab12", "obs.fits")
    A->>S: normalize_polynomial("s_ab12", {order: 3})
    A->>S: snr_der("s_ab12")
    A->>S: fit_gaussian_line("s_ab12", {line_center_angstrom: 6562.8})
    A->>S: get_session_state("s_ab12")
    S-->>A: metrics, line fits, history

Every algorithm tool takes a session_id and an optional params object; it runs against that session's context and returns the metrics, the context summary and any artifacts.

Session storage

Sessions are kept in process memory with a sliding TTL and a cap on live sessions (SPECTRO_MCP_MAX_SESSIONS, default 200, least-recently-used eviction). A multi-instance deployment backs them with Redis (set SPECTRO_MCP_REDIS_URL; payloads are HMAC-signed with SPECTRO_MCP_SESSION_SECRET).

Hardened HTTP mode

spectro-mcp --http is the shared, network-facing variant. Since v0.6.1 it applies the same guards to every tool, not only to load_spectrum:

  • No local filesystem access. Any parameter that names a path (path, *_path, *_paths, *_dir, model_path, target_path, atlas_dir) is refused when it points at a local file, for the read_* bricks, the export_* bricks, the EasySpec wrappers and the embedding bricks alike. Presets can only be called by bundled name.
  • URLs go through the SSRF guard. Private, loopback, link-local, CGNAT and NAT64-embedded-private addresses are refused, redirects are refused, the resolved address is pinned for the connection, and downloads are bounded in size (256 MiB), wall-clock time and concurrency. Downloaded FITS files are checked for their pixel count before conversion.
  • An explicit posture is mandatory. The server refuses to start unless you either set SPECTRO_MCP_API_KEY (closed : clients send the key in the X-API-Key header, the rate limiter counts per key) or SPECTRO_MCP_ALLOW_ANONYMOUS=1 (open : no accounts, no key, the rate limiter counts per client IP). The public demo server is open.
  • Error details are masked and non-finite metrics (NaN, inf) are serialised as null, so every response is strict JSON.
  • get_spectrum_data returns at most max_points samples (default 20 000, stride-downsampled; pass 0 for everything) and says so with downsampled, npix_total, npix_returned and stride.

None of this changes stdio mode, except that URLs handed to the read_* bricks are now fetched through the same guard as load_spectrum.

Variable Default Meaning
SPECTRO_MCP_API_KEY unset Closed posture : shared secret, X-API-Key header.
SPECTRO_MCP_ALLOW_ANONYMOUS unset Open posture : 1 to start --http without a key.
SPECTRO_MCP_ALLOW_KEY_IN_URL unset 1 also accepts the key as ?api_key=… in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. Authorization: Bearer <key> is always accepted.
SPECTRO_MCP_RATE_PER_MINUTE 0 (off) Sliding-window limit per API key (per client IP when auth is off).
SPECTRO_MCP_TRUSTED_PROXY unset 1 behind a load balancer: trust X-Forwarded-For.
SPECTRO_MCP_CORS_ORIGINS spectrokernel.io sites Comma-separated browser origins allowed to call the server (* for any, none to disable). The Mcp-Session-Id header is exposed to browsers.
SPECTRO_MCP_MAX_SESSIONS 200 Live in-memory sessions before LRU eviction.
SPECTRO_MCP_MAX_PIXELS 32000000 Largest downloaded FITS accepted (data elements); 0 disables.
SPECTRO_MCP_DOWNLOAD_BUDGET_S 120 Wall-clock budget per URL download.
SPECTRO_MCP_MAX_CONCURRENT_DOWNLOADS 4 Downloads in flight at once.
SPECTRO_EMBED_ENDPOINT_ALLOW unset Hosts embed_remote may call with the server-side SPECTRO_EMBED_API_KEY.

A typical agent conversation

You: Here is a FITS spectrum. Measure its SNR and fit H-alpha.

Claude (calling tools):

  1. create_session()s_qrp
  2. load_spectrum("s_qrp", "<path>")
  3. normalize_polynomial("s_qrp", {"order": 3})
  4. snr_der("s_qrp") → SNR 142
  5. fit_gaussian_line("s_qrp", {"line_center_angstrom": 6562.8})

Claude: SNR is 142 (excellent). H-alpha is in emission, FWHM 12.4 Å…

Because the MCP tools are generated from the same registry as the library and CLI, the agent is never working with a different or stale catalogue.