Back to Browse

Manna MCP Server

Developer ToolsUse Caution4.8MCP RegistryLocal
Free

Server data from the Official MCP Registry

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

About

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

Security Report

4.8
Use Caution4.8High Risk

MANNA is a well-structured astronomical data MCP server with appropriate authentication, input validation, and permission scoping. The codebase demonstrates good security hygiene with proper error handling and no evidence of malicious patterns. Minor code quality observations around broad exception handling do not materially impact security. Supply chain analysis found 8 known vulnerabilities in dependencies (1 critical, 4 high severity). Package verification found 1 issue.

4 files analyzed · 13 issues found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Permissions Required

This plugin requests these system permissions. Most are normal for its category.

HTTP Network Access

Connects to external APIs or services over the internet.

env_vars

Check that this permission is expected for this type of plugin.

File System Read

Reads files on your machine. Normal for tools that analyze or process local data.

What You'll Need

Set these up before or after installing:

Comma-separated archive short_names to activate (e.g. 'datalab,alma'). Unset means all.Optional

Environment variable: MANNA_ARCHIVES

Comma-separated hostnames the server may fetch. Unset means any public host.Optional

Environment variable: MANNA_ALLOWED_HOSTS

Logging verbosity.Optional

Environment variable: MANNA_LOG_LEVEL

Max rows returned inline before a result is promoted to an async job or truncated.Optional

Environment variable: MANNA_INLINE_ROW_LIMIT

How to Install

Add this to your MCP configuration file:

{
  "mcpServers": {
    "io-github-nsf-simons-cosmicai-institute-manna": {
      "env": {
        "MANNA_ARCHIVES": "your-manna-archives-here",
        "MANNA_LOG_LEVEL": "your-manna-log-level-here",
        "MANNA_ALLOWED_HOSTS": "your-manna-allowed-hosts-here",
        "MANNA_INLINE_ROW_LIMIT": "your-manna-inline-row-limit-here"
      },
      "args": [
        "manna-mcp"
      ],
      "command": "uvx"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

MANNA

MANNAMCP Architecture for NOIRLab and NRAO Archives.

An MCP server exposing IVOA-compliant astronomical archives (NOIRLab Astro Data Lab, NRAO/ALMA, CADC, ESO, Gaia, …) to LLM clients.

Naming: MANNA in prose (it's an acronym); lowercase manna for every identifier — the Python package, python -m manna, the manna:dev image tag, and the MCP client alias (mcp__manna__*). The one exception is the PyPI distribution, manna-mcp (bare manna is admin-prohibited on PyPI).

Tools

ToolProtocolDescription
vo_archive_listList known archives with endpoint URLs and usage notes
vo_schema_describeCurated per-table schema facts (missing columns, enum values, spatial index hints)
vo_target_resolveSesameResolve an object name (e.g. "M87", "Cygnus A") to RA/Dec coordinates
vo_tap_queryTAPSubmit sync or async ADQL queries; returns inline or promoted results
vo_tap_statusTAPPoll an async job by ID
vo_tap_resultsTAPReturn a completed async job's result URL + pyvo fetch recipe (client fetches the data)
vo_tap_abortTAPAbort a running async job
vo_registry_searchRegTAPSearch the IVOA registry by keyword or service type
vo_registry_describeRegTAPDescribe a specific registry resource (columns, capabilities)
vo_cone_searchSCSSimple Cone Search for legacy SCS-only archives
vo_sia_searchSIA 2.0Search for images by position and waveband (returns access URLs to fetch client-side)
vo_find_observationsSIA 2.0 / SCSOne-call facade: resolves a target name or coordinates, auto-selects an archive by service/waveband, then runs the SIA (image) or SCS (catalog) search — chains vo_target_resolve + vo_archive_list + vo_sia_search/vo_cone_search so the model doesn't have to
vo_count_observationsTAPCount observations/sources near a target in one call (resolve → select archive → COUNT)
vo_survey_targetTAPSurvey which archives hold data for a target, with per-archive counts
vo_inspect_tableTAPColumns + curated enums/notes + a sample of rows for one table, in one call

The recommended LLM workflow for a positional query:

  1. vo_target_resolve — get RA/Dec for a named object
  2. vo_archive_list — discover the archive and its endpoint
  3. vo_schema_describe — get table-specific quirks before writing ADQL
  4. vo_registry_describe — live column introspection
  5. vo_tap_query (mode=async for data reads) — run the query

Install

pip install manna-mcp                # distribution name; the import + CLI are `manna`
manna                                # boots the server on http://localhost:8000
# or run without installing:
uvx manna-mcp

As an MCP server in a client

MANNA speaks stdio with --stdio, and streamable HTTP otherwise. For a stdio client (Claude Desktop, Claude Code, IDE extensions):

claude mcp add manna -- uvx manna-mcp --stdio

or, editing a client config by hand:

{"mcpServers": {"manna": {"command": "uvx", "args": ["manna-mcp", "--stdio"]}}}

The first launch resolves astropy and pyvo, which is a large download; run uvx manna-mcp --stdio once in a terminal before wiring it into a client whose startup timeout is short.

Quickstart

uv sync
uv run pytest --record-mode=none        # 657 tests, offline replay
uv run python -m manna                  # server on http://localhost:8000

Smoke test with MCP Inspector:

npx -y @modelcontextprotocol/inspector --cli http://localhost:8000/mcp --method tools/list

Development

uv sync                        # install runtime + dev deps
uv run pre-commit install      # enable git pre-commit hooks (once per clone)

uv run ruff check .            # lint
uv run ruff format .           # format
uv run pyright                 # type check (src/, basic mode)
uv run pre-commit run --all-files   # run every hook over the whole tree

Pre-commit runs ruff (lint + format), file-hygiene checks, and pyright on each commit; the full test suite runs in CI, not at commit time.

Branch flow (see CLAUDE.md for detail): feature branches <initials>/<name> branch off dev and PR into dev; dev is promoted to main via PR. main is protected — it only advances through PRs with passing CI.

Configuration

All settings are optional — defaults work for local dev. Set via environment variables prefixed MANNA_ or in a .env file:

VariableDefaultDescription
MANNA_PORT8000HTTP listen port
MANNA_HOST0.0.0.0Bind address
MANNA_LOG_LEVELINFODEBUG / INFO / WARNING / ERROR
MANNA_TAP_SYNC_TIMEOUT_SECONDS20.0Timeout for sync TAP queries
MANNA_ALLOWED_HOSTS(unset)Comma-separated hostnames the server may fetch (exact or subdomain match). Unset ⇒ any public host; private/loopback/link-local targets are refused regardless
MANNA_ARCHIVES(unset)Comma-separated archive short_names to activate. Unset/empty ⇒ all archives physically present in archives/
MANNA_INLINE_ROW_LIMIT200Max rows in an inline result before it's routed to an async job (TAP) or truncated (cone/SIA)
MANNA_INLINE_BYTE_LIMIT49152Max bytes in an inline result before the same promotion/truncation applies (48 KiB)
MANNA_REGISTRY_DESCRIBE_BYTE_LIMIT49152Above this, vo_registry_describe degrades from per-column detail to a table catalog (names + descriptions + column counts)

See .env.example for a template.

Docker

docker build -t manna:dev .
docker run -p 8000:8000 manna:dev

Forking for a specific deployment

This repo is the multi-archive base. Each archive is one self-contained file — its endpoints, usage notes, and per-table schemas all live in src/manna/archives/<short_name>.py. Shape which archives make curated claims two ways:

  • Physical — delete the unwanted src/manna/archives/<short_name>.py files. Discovery picks up whatever remains; no other file needs touching.
  • Runtime — set MANNA_ARCHIVES=datalab,alma (comma-separated short_names) to narrow a shared image without deleting files. Unset/empty ⇒ every archive active.

A dropped or deselected archive loses only the server's curated claims about it — never its reachability. It's still reachable via vo_registry_search.

Refreshing recorded cassettes

Tests replay archive HTTP traffic from YAML cassettes in tests/<area>/cassettes/. To refresh a stale cassette:

# requires network access to the archive endpoint
rm tests/<area>/cassettes/<test_module>/<test_name>.yaml
uv run pytest tests/<area>/<test_module>.py::<test_name> --record-mode=once

Inspect the cassette diff before committing — large changes in the VOTable namespace URI or response headers may indicate an upstream breaking change.

Docs

Deployment configurations are maintained in a separate repository.

Reviews

No reviews yet

Be the first to review this server!