topup MCP protocol

topup implements the Model Context Protocol over JSON-RPC 2.0. This page is the authoritative interface contract for the topup and topup-opt executables: the wire transport, the session lifecycle, the routing model, every tool, and the environment/convention details a client needs.

Transport

Messages are JSON-RPC 2.0 objects, one per line (newline-delimited UTF-8), exchanged over one of two channels:

Session lifecycle

The advertised protocol version is 2024-11-05.

initialize

Returns protocolVersion, capabilities (namely { "tools": {} }), serverInfo ({ "name": "topup", "version": "0.1.0" }), and an instructions string that advertises any currently-registered remote hosts and named local sessions (with their descriptions), so the client can discover routing targets.

tools/list

Returns the tools array — the descriptors documented under Tools below. Two internal tools, _send_blob and _recv_blob, back the file-transfer back-channel and are deliberately not advertised here.

tools/call

A standard MCP call with { "name": <tool>, "arguments": <object> }. The result carries a content array holding a single text item whose payload is a JSON string (the tool's structured result), plus an isError boolean.

notifications/cancelled

Interrupts an in-flight evaluation. It honours optional host/session fields to target a routed session; with neither, it cancels the local in-process eval. The cancel tool is the request-shaped equivalent.

Routing model

A single topup registration can drive three kinds of session:

host and session are mutually exclusive — pass at most one. Most tools accept them; the session- and host-management tools (start_session, restart_session, update_host, start_local_session, restart_local_session, update_local_session) act on the registry itself and do not route.

Tools

Parameter lists mark each field required or optional. Unless noted, every tool also accepts the optional host and session routing fields described above.

Core evaluation

eval

Evaluate one or more OCaml phrases in the persistent toplevel.

Returns { value_repr, type, stdout, stderr, warnings, error }. Background activity (Lwt/Eio fibres, Thread.create, Domain.spawn) is the caller's responsibility: it keeps running, is not killed by reset, and its later writes may land in a subsequent capture — join/await before the top-level expression returns. Oversized value_repr/stdout/stderr are truncated inline with a …[+N bytes; full at <path>] marker and spilled to the path in the matching *_overflow field.

eval_batch

Evaluate a list of source strings sequentially; earlier bindings are visible to later elements.

Returns { results, stopped_on_error }; results holds every element evaluated, in order, stopping at the first with a non-null error. Amortises protocol overhead for tight inner loops, especially when routed.

env

List user-defined value bindings as (name, type) — the way to recall a workspace built up across previous eval calls.

lookup

Inspect a single binding by name: type, source location, and a small value preview.

reset

Discard the toplevel environment and start fresh. Also drops #load-ed libraries (re-issue load after). For branching or recovery, prefer restore, which yields a known-good workspace rather than an empty one.

cancel

Interrupt the currently-running evaluation (SIGINT to the eval thread; surfaces as evaluation timed out). Bare cancel targets the local in-process eval and does not broadcast across hosts.

load

Dynlink a compiled archive into the live session. Extensions follow the driver: .cma/.cmo under topup, .cmxs under topup-opt.

Loaded modules become available to later eval calls under their unit names. Loaded archives are not replayed on reset and not recorded in the phrase log. With host, the path must exist on the remote filesystem.

Branching state

checkpoint

Snapshot the current phrase log under label as plain OCaml source at $TOPUP_CHECKPOINT_DIR/<label>.ml (default ~/.topup/checkpoints/), written atomically. Overwrites any prior snapshot with the same label.

Requires phrase logging enabled (TOPUP_LOG unset or writable). With host, the snapshot lives under the remote user's home.

restore

Reset the environment and replay the checkpoint named label, replacing the live phrase log with the checkpoint's contents first so the two stay consistent.

Returns the same shape as eval (a non-null error means a phrase failed mid-replay, leaving an intermediate state). #load-ed libraries are not in the log — re-issue load afterwards. Checkpoints are per-host.

Promotion

compile_to_binary

Promote the current session into a standalone native binary: the phrase log is dumped into a synthesised dune project under out, built, and the executable copied to out/main.exe.

Returns { ok, binary_path, build_log }. Curate first by restore-ing a clean checkpoint if the log carries exploratory clutter. Limitations: #load-ed archives are not auto-linked, and #require-d packages not listed in libraries will fail to resolve. Requires phrase logging and dune on PATH.

File transfer

Both directions carry the payload base64-encoded in-band over the same JSON-RPC channel (no scp/rsync). Files are capped at TOPUP_XFER_MAX_BYTES (default 16 MiB); oversized files are rejected before any bytes are read. Writes are atomic (.tmp + rename).

push_file

Copy a file from the local MCP-server filesystem to the remote host.

Returns { remote_path, bytes }.

pull_file

Copy a file from the remote host back to the local MCP-server filesystem.

Returns { local_path, bytes }.

Remote hosts

start_session

Bring up a remote topup session on host: opens an SSH tunnel (ssh -L <local>:<remote> <host> topup --socket <remote>) and performs the initialize handshake. Idempotent against a live tunnel.

Returns the registered host name and remote socket path, or a structured error with phase="connect".

restart_session

Kill the tunnel for host and bring it up again (for a wedged tunnel or crashed daemon; for a fresh OCaml environment use reset instead).

update_host

Set or replace the description and/or os metadata for a registered host; surfaced in the instructions block at the next initialize. Only the fields you pass are changed.

Named local sessions

start_local_session

Bring up a named local topup subprocess and optionally pre-warm it. Idempotent against a live session.

restart_local_session

Kill the subprocess for session and bring it up again with the same prewarm.

update_local_session

Set or replace the prewarm path and/or pool size for a registered session (persisted to ~/.topup/sessions.json). Does not affect the running subprocess — call restart_local_session to apply.

Environment variables