Skip to main content

PRESTO-only MCP server. Typed, sandboxed, reproducible access to PRESTO radio-astronomy tools via Docker.

Project description

presto-mcp

A typed, sandboxed Model Context Protocol server that lets LLMs invoke real PRESTO (pulsar / radio-astronomy) tools — readfile, rfifind, prepfold — through Docker. Every invocation is sandboxed (--network none, read-only data mount, --pids-limit, no-new-privileges), persisted to a reproducible run manifest, and exposed as MCP resources.

PRESTO-only. Not PrestoDB, not Apache Pulsar, not PulsarX / TransientX / riptide / Heimdall / PULSAR_MINER.


What this MVP exposes

MCP tool What it does
presto.readfile Parse SIGPROC filterbank / PSRFITS header metadata.
presto.rfifind Find RFI; produce .mask/.rfi/.stats artifacts.
presto.prepfold (Mode A) Fold at a known candidate period + DM. Produces .pfd / .pfd.bestprof.
presto.list_runs List recent runs from the local runs/ directory.
presto.get_run_manifest Return the full manifest for one run_id.
MCP resource Contents
presto://runs/{run_id}/manifest manifest.json for the run
presto://runs/{run_id}/stdout captured PRESTO stdout
presto://runs/{run_id}/stderr captured PRESTO stderr
presto://runs/{run_id}/artifacts/{filename} one artifact (text inline; binary or >1 MiB returns a JSON descriptor)

Prerequisites

  • Python 3.11+ (3.11 is what uv sync provisions by default).
  • uv for environment management. The repo's pyproject.toml pins dev tools.
  • Docker Desktop / engine running on the host.
  • PRESTO image pulled:
    docker pull alex88ridolfi/presto5:png
    

Verify Docker + PRESTO

docker images alex88ridolfi/presto5:png
docker run --rm alex88ridolfi/presto5:png which readfile rfifind prepfold
# Expected: /software/presto5/installation/bin/{readfile,rfifind,prepfold}

Install

uv sync --extra dev

This creates .venv/, installs mcp==1.27.1, Pydantic v2, Typer, dev tools (pytest, ruff).

Copy .env.example to .env if you want to override defaults:

cp .env.example .env

Run the MCP server (STDIO)

uv run python -m presto_mcp.server

The server reads PRESTO_* env vars from .env and the environment, ensures runs/, outputs/, logs/ exist, runs a startup health check (docker --version, non-zero data file sizes), then speaks JSON-RPC on stdio.

From an MCP client

Copy examples/mcp/claude_desktop_config.example.json into your MCP client's config (e.g. Claude Desktop) and update the cwd to the absolute repo path:

{
  "mcpServers": {
    "presto": {
      "command": "uv",
      "args": ["run", "python", "-m", "presto_mcp.server"],
      "cwd": "C:/ABSOLUTE/PATH/TO/MCP-Server-Presto"
    }
  }
}

MCP Inspector (recommended for interactive debugging)

npx @modelcontextprotocol/inspector uv run python -m presto_mcp.server

In the Inspector UI:

  • tools/list should show all five presto.* tools.
  • Call presto.readfile with { "input_file": "57762_12049_J0532+3305_000022.fil" }.
  • Call presto.rfifind with { "input_file": "57762_12049_J0532+3305_000022.fil", "time": 2.0 }.
  • Read presto://runs/<run_id>/manifest after either call.

Tests

# Unit + integration (no Docker required)
uv run pytest -q

# Lint
uv run ruff check .

# End-to-end with real Docker + real data files
uv run pytest -q tests/e2e --run-e2e

E2E tests are skipped by default. They invoke the PRESTO image with real data, write real run dirs, and assert the parsed metadata matches expectations from data/57762_12049_J0532+3305_000022.fil.


Layout of one run

runs/
└── 20260516T143052Z-K7QM3A/
    ├── manifest.json
    ├── stdout.log
    ├── stderr.log
    └── artifacts/
        ├── rfi_rfifind.mask
        ├── rfi_rfifind.rfi
        ├── rfi_rfifind.stats
        ├── rfi_rfifind.ps
        ├── rfi_rfifind.bytemask
        └── rfi_rfifind.inf

run_id format: YYYYMMDDTHHMMSSZ-<6-char base32> (UTC + 30 bits entropy). Lexicographic order is chronological.

The manifest records the full Docker argv, the PRESTO argv, the image (and digest if Docker provides one), CPU/memory caps, timeout, status, exit code, durations, and the list of artifacts.


Security model (summary)

  • All inputs go through presto_mcp.path_security.resolve_input_path: rejects absolute paths, .. segments, case-folding tricks, and paths outside DATA_DIR.
  • All execution uses subprocess.run(argv, shell=False, timeout=...). There is no shell=True and no generic run_command tool.
  • Every Docker invocation runs with --network none, --security-opt no-new-privileges, --pids-limit 256, --cpus, --memory, a named container, and --rm. data/ mounts read-only.
  • Resource URIs revalidate path-traversal at read time. Large/binary artifacts return a JSON descriptor rather than the file body.
  • Errors are typed (PathSecurityError, PolicyViolationError, DockerInvocationError, ParserError, ManifestError); never raw stack traces.

Full design notes in ARCHITECTURE.md. Repo conventions for AI coding agents in CLAUDE.md and AGENTS.md.


Known limits (MVP)

  • Binary PRESTO outputs are not parsed. .mask, .stats, .rfi, .pfd are recorded as artifacts and exposed as resources, but never opened/decoded server-side.
  • prepfold Mode A only. Folding by accel-search candidate file (Mode B) is documented but not implemented — it requires upstream accelsearch plumbing which is out of scope.
  • STDIO transport only. Remote HTTP is deliberately deferred.
  • No SQLite run index. presto.list_runs walks runs/*/manifest.json. This is fine for thousands of runs; not for millions.
  • Image is tag-pinned, not digest-pinned at install time. The manifest captures the digest at runtime if Docker reports it.

Not in MVP

  • PulsarX, TransientX, riptide, Heimdall, PULSAR_MINER, DRAFTS++.
  • Workflow tools that chain rfifind → prepsubband → accelsearch → prepfold.
  • A Python executor or generic shell tool. (And we won't be adding one.)

Project files

  • src/presto_mcp/server.py — FastMCP entrypoint (the only file that imports FastMCP).
  • src/presto_mcp/docker_backend.py — argv builder + subprocess.run + timeout/kill.
  • src/presto_mcp/path_security.py — input-path traversal guards.
  • src/presto_mcp/executor.py — orchestrates one run (paths → backend → parse → manifest).
  • src/presto_mcp/parsers/* — PRESTO stdout parsers (MVP: stdout only).
  • src/presto_mcp/tools/* — one module per PRESTO binary.
  • src/presto_mcp/resources.py — MCP resource handlers.
  • tests/fixtures/stdout/ — captured real PRESTO stdout for contract tests.
  • tests/fakes/fake_docker_backend.py — in-memory backend honoring BackendProtocol.

License

MIT (see pyproject.toml).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

presto_mcp-0.1.0.tar.gz (122.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

presto_mcp-0.1.0-py3-none-any.whl (73.4 kB view details)

Uploaded Python 3

File details

Details for the file presto_mcp-0.1.0.tar.gz.

File metadata

  • Download URL: presto_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 122.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for presto_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 74c78579579e7122c97b833d6ee9f31e3cd021bacf7cd17ca3b0e66a551b6258
MD5 f1b8ab84c72cc4b6aef4de0878e046a7
BLAKE2b-256 205ad9d4c34f67beda4830ad4f0c3f5faf4c8fd2fbb8ee143ab9cbf784e12d4a

See more details on using hashes here.

Provenance

The following attestation bundles were made for presto_mcp-0.1.0.tar.gz:

Publisher: publish.yml on Kodamonkey/MCP-Server-PRESTO

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file presto_mcp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: presto_mcp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 73.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for presto_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5c7fe01f0894e6a8dcc516aef3b6d156cea582cdf23562abedfc53752be22aa5
MD5 768411e5e8af9dc75c2952daef572b10
BLAKE2b-256 8f0fa74887f22a35efa55a9779ef12c44eb4a32af131fa7ce69e44350380c2e1

See more details on using hashes here.

Provenance

The following attestation bundles were made for presto_mcp-0.1.0-py3-none-any.whl:

Publisher: publish.yml on Kodamonkey/MCP-Server-PRESTO

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page