package topup

  1. Overview
  2. Docs

doc/mcp_protocol.html

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:

  • stdio (default) — the client launches topup and speaks on its stdin/stdout. This is the normal MCP integration path.
  • Unix domain sockettopup --socket <path> serves one client at a time on an AF_UNIX stream socket, created owner-only (mode 0600). Toplevel state persists across successive connections to the same socket, so a client may disconnect and reconnect without losing its bindings.

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:

  • the local in-process toplevel — the default; omit both host and session, or pass the literal "local";
  • a named local subprocess session, brought up with start_local_session and selected per-call with session;
  • a remote toplevel reached over an SSH tunnel, brought up with start_session and selected per-call with host.

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.

  • source (string) — the phrase(s) to evaluate.
  • timeout (number) — seconds before the evaluation is interrupted.

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.

  • sources (array of string).
  • timeout (number) — per element, not per batch.

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.

  • filter (string) — substring filter on binding names.
  • all (boolean) — include stdlib/library bindings (hidden by default).

lookup

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

  • name (string).

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.

  • path (string) — absolute; its directory is added to the load path so the sibling .cmi is found.

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.

  • label (string) — must match [A-Za-z0-9._-]+, no leading dot, no ...

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.

  • label (string).

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.

  • entry (string) — a binding in scope of type unit -> _; the wrapper is let () = ignore (<entry> ()).
  • out (string) — absolute output directory; created on demand, refused if non-empty without a prior .topup-promote marker.
  • libraries (array of string) — findlib package names emitted into the synthesised (libraries ...) stanza.

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.

  • host (string) — required; purely-local copies are rejected.
  • local_path (string) — read on the MCP-server side.
  • remote_path (string) — defaults to $TOPUP_XFER_DIR/<basename> on the remote (default ~/.topup/xfer/).

Returns { remote_path, bytes }.

pull_file

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

  • host (string) — required.
  • remote_path (string).
  • local_path (string) — defaults to $TOPUP_XFER_DIR/<basename> locally.

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.

  • host (string).
  • remote_socket (string) — remote socket path (default ~/.topup/sockets/topup.sock).

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).

  • host (string).

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.

  • host (string).
  • description (string), os (string).

Named local sessions

start_local_session

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

  • session (string).
  • prewarm (string) — a path #use-d in the subprocess before returning; a failing prewarm kills it and surfaces the error.
  • pool (number) — when > 1, also spawns siblings <session>.1<session>.(pool-1) sharing the prewarm, so restore against a sibling branches off the primary without cold-load cost.

restart_local_session

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

  • session (string).

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.

  • session (string).
  • prewarm (string), pool (number).

Environment variables

  • TOPUP_XFER_DIR — default directory for push_file/pull_file (default ~/.topup/xfer/; =off requires explicit paths).
  • TOPUP_XFER_MAX_BYTES — file-transfer size cap (default 16 MiB).
  • TOPUP_CHECKPOINT_DIR — checkpoint directory (default ~/.topup/checkpoints/).
  • TOPUP_LOG — phrase-log path; must be unset or writable for checkpoint, restore, and compile_to_binary to work.