# MANNA (pypi · manna-mcp)

MCP server for IVOA astronomical archives: NOIRLab Astro Data Lab, NRAO/ALMA, CADC, ESO, Gaia

- Trust score: 69/100 (medium)
- Change this week: +16
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-21

## Components

- pypi · `manna-mcp`: 69/100 (this document), [markdown](https://verifymcp.io/servers/nsf-simons-cosmicai-institute-manna/manna-mcp.md), [page](https://verifymcp.io/servers/nsf-simons-cosmicai-institute-manna/manna-mcp)

## Channel facts

- Registry: `pypi`
- Package: `manna-mcp`
- Version: `0.9.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-21.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it.
  - 1 of 35 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 45/100
  - Source repository is publicly reachable at the declared URL.
  - Provenance check failed: no build-provenance attestation is published.
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 4 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 65/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 5720 tokens (~381/item across 15 items; 15 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability check failed: the tool surface changed between 0.7.2 and 0.9.0: 15 tool removals, 0 breaking changes, 15 additions.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 15 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 15 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### How do I install the MANNA MCP server?

MANNA runs locally as a PyPI package, launched with uvx manna-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add nsf-simons-cosmicai-institute-manna -- uvx manna-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "nsf-simons-cosmicai-institute-manna": {
      "command": "uvx",
      "args": [
        "manna-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "nsf-simons-cosmicai-institute-manna": {
      "command": "uvx",
      "args": [
        "manna-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add nsf-simons-cosmicai-institute-manna -- uvx manna-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "nsf-simons-cosmicai-institute-manna": {
      "type": "local",
      "command": [
        "uvx",
        "manna-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add nsf-simons-cosmicai-institute-manna --command uvx --arg manna-mcp
```

### Hermes

```yaml
mcp_servers:
  nsf-simons-cosmicai-institute-manna:
    command: "uvx"
    args: ["manna-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "nsf-simons-cosmicai-institute-manna": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "manna-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add nsf-simons-cosmicai-institute-manna -t stdio -c uvx -a manna-mcp
```

### Other

```json
{
  "mcpServers": {
    "nsf-simons-cosmicai-institute-manna": {
      "command": "uvx",
      "args": [
        "manna-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-16 (score 69, +1)

- [security regression] Stability: unverified → fail
- [security regression] Tool safety: pass → unverified
- [security] Stability: Stability not yet verified: we do not have a sandbox capture of the MCP schema this version of the package serves yet.
- [functional regression] Capabilities: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional] First check of Schema quality: unverified
- [functional] Package version: 0.7.2 → 0.9.0

### 2026-09-15 (score 68, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-09-14 (score 53, −15)

- [security regression] Malware scan: pass → unverified

### 2026-09-12 (score 68, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-09-10 (score 53)

First indexed and scored.

## MCP tools (15)

### `list_archives` (~545 tokens)

List the IVOA archives this server has first-class knowledge of.

Each entry includes the archive's endpoint URLs (TAP / SIA / SCS),
waveband, description, notable tables, and **usage_notes** — short
agent-facing strings capturing archive-specific gotchas like
non-standard table locations, sync-vs-async routing recommendations,
target-name conventions, and ADQL quirks.

Call this FIRST when planning a query against an archive whose
behavior you don't already know — it'll save you the trial-and-error
of discovering quirks through failed queries. The notes are curated
based on real friction encountered while building the server.

Pass `short_name` and/or `waveband` to narrow the result. With no
arguments it returns every known archive (the usage_notes are verbose,
so prefer `short_name` once you know which archive you need).

Archives not listed here still work via `search_ivoa_registry` followed
by `describe_ivoa_service` / `run_adql_query` — this tool only covers the
well-known set.

Returns:
    {
      "archives": [
        {
          "short_name": "...",
          "display_name": "...",
          "host_substrings": ["..."],
          "tap_url": "...",
          "sia_url": "..." | null,
          "scs_url": "..." | null,
          "waveband": "...",
          "description": "...",
          "notable_tables": ["..."],
          "usage_notes": [
            "Short agent-facing strings — read these BEFORE composing "
            "a query. They capture real gotchas like non-standard "
            "table names, required mode='async' routing, target-name "
            "aliasing, etc."
          ]
        },
        ...
      ],
      "count": N
    }


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `short_name`: Optional. Return only the archive with this short_name (case-insensitive), e.g. 'alma'. Use this when you already know which archive you want — it returns a single entry instead of the full set, savi…
- `waveband`: Optional. Return only archives in this waveband (case-insensitive), e.g. 'radio', 'optical', 'millimeter'. Combines with short_name (both must match).

### `run_adql_query` (~879 tokens)

Run an ADQL query against any IVOA-compliant TAP service.

BEFORE composing a query against an archive you don't already know
cold, call `list_archives` first. It returns curated usage notes
for the well-known archives — non-standard table locations, required
mode='async' routing, ADQL quirks, target-name conventions — that
will save you trial-and-error here.

The server never holds result bytes. Small results come back inline;
anything larger than the inline cap is routed to an async job whose
result the client fetches itself (see get_async_job_results / fetch_recipe).

Returns one of two envelope shapes depending on what happened:

1\. Inline result envelope (row_count, columns, rows).
   Returned when the result fits the inline cap: mode='sync' with a
   small result, or mode='auto' when the query finished within the
   sync timeout AND fit inline. No `mode` key on the response.

2\. Promotion envelope (mode='async', job_url, fetch_recipe).
   Returned when mode='async', when mode='auto' and the sync attempt
   timed out, or when mode='auto' and the sync result was too large
   to inline. Disambiguate by checking payload.get('mode') == 'async'.

mode='sync' with an oversize result does NOT auto-promote — it raises
validation_error telling you to re-run with mode='async'.

For async results, poll get_async_job_status(job_url) until phase is
COMPLETED, then call get_async_job_results(job_url) — or fetch client-side
with the pyvo fetch_recipe carried on the promotion envelope. Pass the
job_url back verbatim; it is the job's only handle.

Successful result envelopes also carry `query_fingerprint` and a
\`save_recipe` — after loading the result, execute save_recipe.code
client-side to persist a CSV + manna_cache/catalog.csv row so the
query need not be re-run later.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on —…

Input parameters:

- `adql` (string, required): ADQL query. Geometry support is archive-specific: standard CIRCLE/POINT/CONTAINS work on obscore services (ALMA, ESO) but NOT on Astro Data Lab, which passes them to PostgreSQL and needs q3c_radial_q…
- `endpoint` (string, required): Full TAP service URL. Example: 'https://datalab.noirlab.edu/tap' (NOIRLab Astro Data Lab) or 'https://almascience.nrao.edu/tap' (ALMA Science Archive). Discover other services via search_ivoa_registr…
- `maxrec` (integer): Hard cap on rows returned. Default 10_000.
- `mode` (string): Execution mode. 'sync' = TAP /sync only (default Slice-A behavior; times out as archive_error). 'async' = skip sync, submit /async, return a promotion envelope with job_url. 'auto' (default) = try sy…

### `get_async_job_status` (~236 tokens)

Fetch the live UWS phase for an async TAP job.

Returns {job_url, phase, started_at, ended_at, error_message, archive}.
Phase is read live from the upstream service; no local caching.

Phases per UWS spec: PENDING, QUEUED, EXECUTING, COMPLETED, ERROR,
ABORTED, ARCHIVED, HELD, SUSPENDED, UNKNOWN. The LLM branches on
the string.

If the archive has deleted or expired the job, this raises job_gone
(retry_strategy=abandon) — re-submit rather than continuing to poll.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `job_url` (string, required): The upstream job_url returned by run_adql_query when it went async (mode='async' or auto-promote). Pass it back verbatim — it is the job's only handle.

### `get_async_job_results` (~266 tokens)

Return access info for a COMPLETED async TAP job.

The server does NOT fetch the result bytes. It returns the upstream
job_url, the direct result_url, and a pyvo fetch_recipe so the client
loads the data itself (e.g. in a Jupyter kernel). Anonymous access
only.

After calling this, execute the returned fetch_recipe code with your
code-execution tool to load the data. The query already ran — do not
re-submit it.

If the job is not yet COMPLETED, raises job_not_ready (retry_strategy=poll).
If the job ended in ERROR, raises tap_query_error with the upstream
message. If the archive no longer has the job, raises job_gone
(retry_strategy=abandon).


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `job_url` (string, required): The upstream job_url returned by run_adql_query when it went async (mode='async' or auto-promote). Pass it back verbatim — it is the job's only handle.

### `abort_async_job` (~168 tokens)

Cancel a running async TAP job.

Sends UWS DELETE upstream. Idempotent: aborting an already-deleted or
expired job returns the same {job_url, phase=ABORTED} shape rather than
raising (abort_job swallows the 4xx).


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `job_url` (string, required): The upstream job_url returned by run_adql_query when it went async (mode='async' or auto-promote). Pass it back verbatim — it is the job's only handle.

### `search_ivoa_registry` (~292 tokens)

Discover IVOA-registered services matching the given constraints.

Returns a {services: [...], row_count, truncated, truncation_reason}
envelope. Each service entry has: ivoid, title, description, publisher,
waveband, and one URL per capability (tap_url, sia_url, scs_url,
ssa_url; null when the service doesn't expose that capability).

Use for discovery before calling run_adql_query / search_images_by_position /
search_catalog_by_position on a specific endpoint. Smaller default maxrec (50)
than catalog tools — discovery is about choice, not bulk data.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `keywords`: Free-text keywords to match against service titles/descriptions. Example: ['Magellanic', 'photometry'].
- `maxrec` (integer): Hard cap on services returned. Default 50.
- `servicetype`: Filter by service type: 'tap', 'sia', 'scs', 'ssa'.
- `waveband`: Filter by waveband: 'radio', 'infrared', 'optical', 'uv', 'euv', 'x-ray', 'gamma-ray'.

### `describe_ivoa_service` (~364 tokens)

Introspect a specific IVOA service: its capabilities, and for TAP
services its tables and columns.

Returns {ivoid, title, description, capabilities, tables, truncated,
total_tables} (plus matched_tables when table_filter is used). Use after
search_ivoa_registry to learn what's queryable on a specific service before
composing ADQL via run_adql_query.

Each table normally carries its full column list. For a very large service
the response degrades to a table *catalog* (name + description +
column_count per table, no per-column detail) and sets truncated: true.
When that happens, either pass table_filter='<keyword>' to narrow to the
tables you want (a narrow match returns their columns inline), or get one
table's columns by querying tap_schema.columns WHERE table_name = '<table>'
via run_adql_query (or describe_table for curated tables). See the
returned hints.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `ivoid_or_url` (string, required): Either an IVOID (starts with 'ivo://') or a TAP service URL. The tool resolves both forms via RegTAP.
- `table_filter`: Optional case-insensitive keyword. When set, only tables whose name or description contains it are returned. Use this on large services (hundreds/thousands of tables) to find the ones you want AND ge…

### `describe_table` (~317 tokens)

Curated quirks + the real column list for one archive table.

Returns the table's actual columns (name + datatype, fetched live from the
archive's tap_schema.columns) alongside table-specific curated facts:
missing standard columns, value enums for filterable fields, notes, and
cross_refs to related tables.

For a known archive, exactly one of these is always present:
  \* `columns`: [{"name": ..., "datatype": ...}] — the real column list.
    Project these explicitly rather than SELECT *.
  \* `column_list_recipe`: the query to run yourself, if the fetch failed.

\`known: false` means only that we carry no curated notes for the table — for
a known archive the column list is still returned. Use `describe_ivoa_service`
to discover which tables an archive has, or `list_archives` for valid
archive short_names.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `archive` (string, required): Archive short_name (e.g. 'alma', 'datalab', 'gaia'). Use list_archives to discover available names.
- `table` (string, required): Fully qualified table name as it appears in the archive's TAP schema (e.g. 'ivoa.obscore', 'nsc_dr2.object').

### `resolve_target_name` (~249 tokens)

Resolve an object name to RA/Dec (ICRS, decimal degrees).

Returns ``ra`` and ``dec`` suitable for ``CONTAINS(POINT('ICRS',ra,dec),
CIRCLE('ICRS',<ra>,<dec>,<radius>))=1`` ADQL predicates or as the
positional input to ``search_catalog_by_position``.

On miss returns ``{"resolved": false, ...}`` — try an alternate
designation or use ``search_ivoa_registry`` to locate a catalog by keyword.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `name` (string, required): Astronomical object name to resolve to sky coordinates. Uses the CDS Sesame service (queries SIMBAD, then NED, then VizieR). Common name styles all work: Messier numbers (M87), NGC/IC designations, I…

### `search_catalog_by_position` (~277 tokens)

Run a Simple Cone Search (SCS) against a catalog endpoint.

Returns the inline tabular envelope, same shape as run_adql_query.
For most uses, prefer run_adql_query — SCS is here for catalogs that
only expose the legacy protocol.

Successful envelopes carry `query_fingerprint` + `save_recipe`; execute
save_recipe.code client-side to persist the result and its catalog row.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `dec` (number, required): Declination (ICRS), degrees.
- `endpoint` (string, required): Simple Cone Search endpoint URL. Example: 'https://gaia.ari.uni-heidelberg.de/cone/gaiadr2?' (Gaia ARI Heidelberg). Prefer run_adql_query for archives that expose a TAP endpoint — search_catalog_by_p…
- `maxrec` (integer): Hard cap on rows returned. Default 10_000.
- `ra` (number, required): Right ascension (ICRS), degrees.
- `radius_deg` (number, required): Cone radius in degrees.

### `search_images_by_position` (~558 tokens)

Discover images at a sky position via Simple Image Access (SIA 2.0 or 1.0).

Returns the inline tabular envelope. Each row is image metadata; the
\`access_url` column points at the image (a FITS file, or a cutout-service
URL for archives like Data Lab). The server does not download images —
fetch an access_url client-side (e.g. astropy.io.fits.open(access_url)).

Most archives speak SIA2; NOIRLab Data Lab speaks SIA1. With the default
version='auto' you don't need to know which — SIA2 is tried first and
SIA1 is used as a fallback.

For all-sky discovery first, see search_ivoa_registry with
servicetype='sia'.

Successful envelopes carry `query_fingerprint` + `save_recipe`; execute
save_recipe.code client-side to persist the result and its catalog row.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `band`: Optional waveband filter (e.g. 'optical', 'infrared').
- `dec` (number, required): Declination (ICRS), degrees.
- `endpoint` (string, required): SIA endpoint URL — SIA 2.0 or 1.0. Example: 'https://datalab.noirlab.edu/sia/coadd_all' (NOIRLab Astro Data Lab) or 'https://almascience.nrao.edu/sia2' (ALMA Science Archive). search_images_by_positi…
- `fmt`: Optional image format (e.g. 'image/fits').
- `maxrec` (integer): Hard cap on rows returned. Default 1_000.
- `ra` (number, required): Right ascension (ICRS), degrees.
- `size_deg` (number, required): Field-of-view size in degrees.
- `version` (string): SIA protocol version. 'auto' (default) tries SIA 2.0 and falls back to SIA 1.0 when the endpoint isn't SIA2 (e.g. NOIRLab Data Lab is SIA 1.0). Force with '2' or '1'. The 'band' filter applies to SIA…

### `find_observations_of_target` (~570 tokens)

Find observations of a target in one call (resolve -> select -> search).

A purpose-driven workflow over resolve_target_name + list_archives +
search_images_by_position / search_catalog_by_position. Pass an object name (auto-resolved) or
explicit 'RA DEC'; optionally steer archive choice with `waveband` or an
explicit `archive`.

Returns the standard inline tabular envelope (same shape as search_images_by_position /
search_catalog_by_position — typed `columns`, `rows`, explicit `truncated` bool; note
this workflow tool's envelope does NOT carry the `query_fingerprint` / `save_recipe`
cache fields the primitive tools attach — call the underlying search_images_by_position
/ search_catalog_by_position directly if you need those) plus:

  \- `resolved`: {target, ra, dec, frame} — the coordinates actually used.
  \- `plan`: {service, chosen_archive, endpoint, alternatives, usage_notes}
    — how the archive was picked, and its curated gotchas. Read
    `plan.usage_notes` before trusting the rows.

Soft-fails (no error_class) when a name can't be resolved
({"resolved": false, ...}) or no archive matches the filter
({"count": 0, "hint": ...}) — recover by adjusting the target/filter or
dropping to the atomic tools with an explicit endpoint.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `archive`: Optional short_name override ('alma', 'datalab') to skip auto-selection. Use when you already know the archive.
- `maxrec` (integer): Hard cap on rows returned. Default 1_000.
- `radius_deg` (number): Search radius / field size in degrees. Default 0.1.
- `service` (string): 'image' -> Simple Image Access (rows carry image/cutout access_urls). 'catalog' -> Simple Cone Search (source rows).
- `target` (string, required): Object name (resolved via CDS Sesame — 'M87', 'Cygnus A', '3C 273') OR explicit ICRS coordinates as 'RA DEC' in decimal degrees ('187.7059 12.3911', comma optional). Names are auto-resolved; you do N…
- `waveband`: Optional waveband to auto-select the archive — 'radio', 'optical', 'infrared', 'millimeter'. Omit to use the highest-priority archive offering this service.

### `count_observations_near_target` (~449 tokens)

Count observations/sources near a target in one call (resolve -> select -> COUNT).

Selects an archive by its curated `count_target` and runs the
archive-correct positional COUNT: q3c for Data Lab, CONTAINS/CIRCLE for
Gaia's gaia_source and ESO's obscore, INTERSECTS + COUNT(DISTINCT
member_ous_uid) for ALMA. An archive whose count_target is mode='async'
runs with a bounded poll.

Returns `count` (int) plus a `resolved` block and a `plan` block
(chosen_archive, table, endpoint, adql, count_expr, mode, alternatives,
usage_notes). If a slow async job outruns the poll budget, returns
\`{"status":"pending","count":null,"job_url":...,"next_steps":...}` — poll
get_async_job_status(job_url) until phase=COMPLETED, then get_async_job_results(job_url).
Pass job_url back verbatim; it is the job's only handle (no server-side
job id). Soft-fails (no error_class) on an unresolvable target or when no
archive offers counting.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `archive`: Optional short_name override ('alma', 'datalab') to skip auto-selection.
- `radius_deg` (number): Search radius in degrees. Default 0.1.
- `target` (string, required): Object name (CDS Sesame-resolved — 'M87', 'Cygnus A') OR explicit ICRS 'RA DEC' in decimal degrees ('187.7059 12.3911', comma optional). You do NOT need to call resolve_target_name first.
- `waveband`: Optional waveband to steer archive choice — 'radio', 'optical', 'millimeter'. Omit to use the highest-priority countable archive.

### `preview_table` (~280 tokens)

Columns + curated enums/notes + a sample of rows for one table, in one call.

Returns the real column list (name + datatype), curated `value_enums`,
\`notes`, and `sample_rows` (best-effort — `sample_status` ∈ {ok, error,
disabled}). Reliable metadata is always returned even if the sample fails
(some archives reject unfiltered reads). Soft-fails (`known: false` + hint)
when the table/archive can't be identified. Note `known` means "has
curated schema knowledge" (same convention as describe_table), NOT
"table identified" — for a known archive/endpoint the column list is
still returned even when `known` is false.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `archive`: Archive short_name. If omitted, inferred from curated schema knowledge.
- `sample_rows` (integer): How many example rows to sample (SELECT TOP n *). 0 disables.
- `table` (string, required): Fully qualified table name (e.g. 'ivoa.obscore', 'nsc_dr2.object').

### `survey_archives_for_target` (~270 tokens)

Survey which archives hold data for a target, with per-archive counts.

Resolves the target, then runs each countable archive's curated positional
COUNT and returns one row per archive: {archive, display_name, waveband,
table, count, status, [job_url, next_steps]}. `status` ∈ {ok, pending,
error} is always explicit — never a silent zero. A `summary` block totals
archives_with_data / wavebands / pending / errors. Soft-fails (no
error_class) on an unresolvable target. For a single archive with finer
control, drop to count_observations_near_target or run_adql_query.


On error, returns a Tool Execution Error payload with `error_class`, `message`, `retry_strategy`, and (when available) `hint`. The presence of `error_class` is the discriminator the LLM should branch on — do NOT rely on a separate `isError` field.

Input parameters:

- `radius_deg` (number): Search radius in degrees. Default 0.1.
- `target` (string, required): Object name (Sesame-resolved) or explicit ICRS 'RA DEC' in decimal degrees.
- `wavebands`: Optional list of wavebands to restrict the fan-out.

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/nsf-simons-cosmicai-institute-manna/manna-mcp#diagnostics

## Score history

- 2026-09-21: 69
- 2026-09-20: 69
- 2026-09-19: 69
- 2026-09-18: 69
- 2026-09-17: 69
- 2026-09-16: 69
- 2026-09-15: 68
- 2026-09-14: 53
- 2026-09-13: 68
- 2026-09-12: 68
- 2026-09-11: 53
- 2026-09-10: 53

## Common questions

### What is the MANNA MCP server?

MANNA is an MCP server listed in the public MCP registry as io.github.NSF-Simons-CosmicAI-Institute/manna. MCP server for IVOA astronomical archives: NOIRLab Astro Data Lab, NRAO/ALMA, CADC, ESO, Gaia. This page covers its PyPI package (manna-mcp).

### Is the MANNA MCP server safe to use?

MANNA scores 69 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 21 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the MANNA MCP server expose?

MANNA exposes 15 tools: list_archives, run_adql_query, get_async_job_status, get_async_job_results, abort_async_job, and 10 more. Their descriptions and schemas cost roughly 5,720 tokens of context every time the server is loaded.

### Is the MANNA MCP server still maintained?

MANNA is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the MANNA MCP server under?

MANNA declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- PyPI project: https://pypi.org/project/manna-mcp/
- Socket report: https://socket.dev/pypi/package/manna-mcp
- Repository: https://github.com/NSF-Simons-CosmicAI-Institute/manna
- Changelog RSS feed: https://verifymcp.io/servers/nsf-simons-cosmicai-institute-manna/manna-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/nsf-simons-cosmicai-institute-manna/manna-mcp.json
- HTML version of this page: https://verifymcp.io/servers/nsf-simons-cosmicai-institute-manna/manna-mcp
