Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

MANNA

PYPI · MANNA-MCP · SCANNED SEP 21

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

Available components

+16 this week 69 Trust /100
Trust breakdown (7 categories)

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. How we score → Why this is hard to score →

Supply Chain Security100
  • No malware found by supply-chain analysis.Pass
  • No known CVEs affecting this package version or its production dependencies.Pass
  • Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it. View diagnostics → Pass
  • 1 of 35 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency45
Schema Quality & AI Usability65
  • AI-judged instruction clarity (excellent).Pass
  • 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. See how to fix → Fail
  • Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management0
  • Stability check failed: the tool surface changed between 0.7.2 and 0.9.0: 15 tool removals, 0 breaking changes, 15 additions. See how to fix → Fail
Tool Coverage100
  • 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
  • 100% of tool parameters carry a description.Pass
  • Structured output schemas are declared (100% of tools); any adoption earns full credit.Pass
Tool Safety100
  • No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
  • We read all 15 captured tool definition(s), and no name or description among them implies an irreversible operation.Pass
  • An AI judge read all 15 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
  • Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
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.

pypi · manna-mcp

# add to Claude Code
claude mcp add nsf-simons-cosmicai-institute-manna -- uvx manna-mcp
// .cursor/mcp.json
{
  "mcpServers": {
    "nsf-simons-cosmicai-institute-manna": {
      "command": "uvx",
      "args": [
        "manna-mcp"
      ]
    }
  }
}
// .vscode/mcp.json
{
  "servers": {
    "nsf-simons-cosmicai-institute-manna": {
      "command": "uvx",
      "args": [
        "manna-mcp"
      ]
    }
  }
}
# add to Codex CLI
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
    }
  }
}
# add to OpenClaw
openclaw mcp add nsf-simons-cosmicai-institute-manna --command uvx --arg manna-mcp
# ~/.hermes/config.yaml
mcp_servers:
  nsf-simons-cosmicai-institute-manna:
    command: "uvx"
    args: ["manna-mcp"]
// ~/.netclaw/config/netclaw.json
{
  "McpServers": {
    "nsf-simons-cosmicai-institute-manna": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "manna-mcp"
      ]
    }
  }
}
# add to Vellum
assistant mcp add nsf-simons-cosmicai-institute-manna -t stdio -c uvx -a manna-mcp
// mcp.json
{
  "mcpServers": {
    "nsf-simons-cosmicai-institute-manna": {
      "command": "uvx",
      "args": [
        "manna-mcp"
      ]
    }
  }
}
Changelog

Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.

  • 16 Sept 26 +1
    • Stability: unverified → fail security
    • 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. security
    • Capabilities: pass → unverified functional
    • Tool coverage: 100 → unverified functional
    • First check of Schema quality: unverified functional
    • Package version: 0.7.2 → 0.9.0 functional
  • 15 Sept 26 +15
    • Malware scan: unverified → pass security
  • 14 Sept 26 −15
    • Malware scan: pass → unverified security
  • 12 Sept 26 +15
    • Malware scan: unverified → pass security
  • 10 Sept 26 53

    First indexed and scored.

Diagnostics

Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.

Captured 21 Sept 2026 · Analysed pypi/manna-mcp@0.9.0

Provenance No attestation

The registry publishes no build provenance for this version, so there is nothing to verify.

Result No attestation
Ecosystem pypi

Background: How many MCP packages publish verified provenance →

Install scripts 1 script
Hook Tier Command
build_backend allowlisted hatchling.build

Background: Why install scripts are a supply-chain risk →

Dependencies 35 packages
Packages resolved 35
Stale 1
Tree resolution Complete

Background: SBOMs and build attestations, explained →

MCP tools · 15 exposed · ~5,720 tokens

The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →

Tool Tokens
abort_async_job ~168

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.

NameTypeReqDescription
job_urlstringyesThe 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.

Structured output declared, but exposes no named fields.

No examples provided.

count_observations_near_target ~449

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.

NameTypeReqDescription
archiveOptional short_name override ('alma', 'datalab') to skip auto-selection.
radius_degnumberSearch radius in degrees. Default 0.1.
targetstringyesObject 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.
wavebandOptional waveband to steer archive choice — 'radio', 'optical', 'millimeter'. Omit to use the highest-priority countable archive.

Structured output declared, but exposes no named fields.

No examples provided.

describe_ivoa_service ~364

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.

NameTypeReqDescription
ivoid_or_urlstringyesEither an IVOID (starts with 'ivo://') or a TAP service URL. The tool resolves both forms via RegTAP.
table_filterOptional 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…

Structured output declared, but exposes no named fields.

No examples provided.

describe_table ~317

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.

NameTypeReqDescription
archivestringyesArchive short_name (e.g. 'alma', 'datalab', 'gaia'). Use list_archives to discover available names.
tablestringyesFully qualified table name as it appears in the archive's TAP schema (e.g. 'ivoa.obscore', 'nsc_dr2.object').

Structured output declared, but exposes no named fields.

No examples provided.

find_observations_of_target ~570

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.

NameTypeReqDescription
archiveOptional short_name override ('alma', 'datalab') to skip auto-selection. Use when you already know the archive.
maxrecintegerHard cap on rows returned. Default 1_000.
radius_degnumberSearch radius / field size in degrees. Default 0.1.
servicestring'image' -> Simple Image Access (rows carry image/cutout access_urls). 'catalog' -> Simple Cone Search (source rows).
targetstringyesObject 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…
wavebandOptional waveband to auto-select the archive — 'radio', 'optical', 'infrared', 'millimeter'. Omit to use the highest-priority archive offering this service.

Structured output declared, but exposes no named fields.

No examples provided.

get_async_job_results ~266

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.

NameTypeReqDescription
job_urlstringyesThe 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.

Structured output declared, but exposes no named fields.

No examples provided.

get_async_job_status ~236

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.

NameTypeReqDescription
job_urlstringyesThe 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.

Structured output declared, but exposes no named fields.

No examples provided.

list_archives ~545

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.

NameTypeReqDescription
short_nameOptional. 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…
wavebandOptional. Return only archives in this waveband (case-insensitive), e.g. 'radio', 'optical', 'millimeter'. Combines with short_name (both must match).

Structured output declared, but exposes no named fields.

No examples provided.

preview_table ~280

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.

NameTypeReqDescription
archiveArchive short_name. If omitted, inferred from curated schema knowledge.
sample_rowsintegerHow many example rows to sample (SELECT TOP n *). 0 disables.
tablestringyesFully qualified table name (e.g. 'ivoa.obscore', 'nsc_dr2.object').

Structured output declared, but exposes no named fields.

No examples provided.

resolve_target_name ~249

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.

NameTypeReqDescription
namestringyesAstronomical 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…

Structured output declared, but exposes no named fields.

No examples provided.

run_adql_query ~879

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 —…

NameTypeReqDescription
adqlstringyesADQL 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…
endpointstringyesFull 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…
maxrecintegerHard cap on rows returned. Default 10_000.
modestringExecution 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…

Structured output declared, but exposes no named fields.

No examples provided.

search_catalog_by_position ~277

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.

NameTypeReqDescription
decnumberyesDeclination (ICRS), degrees.
endpointstringyesSimple 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…
maxrecintegerHard cap on rows returned. Default 10_000.
ranumberyesRight ascension (ICRS), degrees.
radius_degnumberyesCone radius in degrees.

Structured output declared, but exposes no named fields.

No examples provided.

search_images_by_position ~558

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.

NameTypeReqDescription
bandOptional waveband filter (e.g. 'optical', 'infrared').
decnumberyesDeclination (ICRS), degrees.
endpointstringyesSIA 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…
fmtOptional image format (e.g. 'image/fits').
maxrecintegerHard cap on rows returned. Default 1_000.
ranumberyesRight ascension (ICRS), degrees.
size_degnumberyesField-of-view size in degrees.
versionstringSIA 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…

Structured output declared, but exposes no named fields.

No examples provided.

search_ivoa_registry ~292

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.

NameTypeReqDescription
keywordsFree-text keywords to match against service titles/descriptions. Example: ['Magellanic', 'photometry'].
maxrecintegerHard cap on services returned. Default 50.
servicetypeFilter by service type: 'tap', 'sia', 'scs', 'ssa'.
wavebandFilter by waveband: 'radio', 'infrared', 'optical', 'uv', 'euv', 'x-ray', 'gamma-ray'.

Structured output declared, but exposes no named fields.

No examples provided.

survey_archives_for_target ~270

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.

NameTypeReqDescription
radius_degnumberSearch radius in degrees. Default 0.1.
targetstringyesObject name (Sesame-resolved) or explicit ICRS 'RA DEC' in decimal degrees.
wavebandsOptional list of wavebands to restrict the fan-out.

Structured output declared, but exposes no named fields.

No examples provided.

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.