Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/deeplethe/forkd/llms.txt

Use this file to discover all available pages before exploring further.

forkd-mcp is a stateless Model Context Protocol server that wraps the forkd-controller REST API as MCP tools. Once registered, any MCP-aware client — Claude Desktop, Claude Code, Cursor, Cline — can spawn Firecracker microVMs, exec commands inside them, branch a running sandbox into a new snapshot, and fan out grandchildren, all from natural language. The server itself holds no state; every tool call hits the forkd-controller daemon fresh, and the daemon owns sandbox lifecycle. Running forkd-mcp requires a forkd-controller daemon already running on the host.

Installation

pip install forkd-mcp

Client configuration

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
  "mcpServers": {
    "forkd": {
      "command": "forkd-mcp",
      "env": {
        "FORKD_URL": "http://127.0.0.1:8889",
        "FORKD_TOKEN": "your-token-here"
      }
    }
  }
}
Restart Claude Desktop. The forkd tools appear in the “hammer” menu.

Available MCP tools

The server exposes eleven tools, each mapping 1:1 to a forkd-controller REST endpoint.

list_snapshots

List parent snapshots registered with the forkd controller. Returns SnapshotInfo objects: tag, dir, created_at_unix. No parameters.

spawn_sandboxes

Fork N children from a parent snapshot.
snapshot_tag
string
required
Name of a registered snapshot (see list_snapshots).
n
int
default:"1"
Number of children to spawn, 1–1000.
per_child_netns
bool
default:"false"
When true, each child is placed in a per-child network namespace forkd-child-<i>. The host must have run scripts/netns-setup.sh N first.
memory_limit_mib
int
default:"256"
Cgroup memory.max for each child in MiB.
prewarm
bool
default:"false"
When true, the daemon performs a throwaway snapshot immediately after restore to fault in all guest pages. Trades ~170 ms / 512 MiB of extra spawn time for predictable BRANCH latency on the first user-visible BRANCH. Useful when you have a BRANCH SLO and fan out 3+ sandboxes from the same source.
live_fork
bool
default:"false"
v0.4+. Boots the sandbox with a memfd-backed RAM region so later branch_sandbox calls can use mode="live" (UFFD_WP). Requires kernel 5.7+ and the vendored Firecracker fork.
returns
list[SandboxInfo]
Spawned sandbox objects, one per child. Each contains id, pid, guest_addr, snapshot_tag, netns, created_at_unix, and memory_limit_mib.

branch_sandbox

Branch a running sandbox into a new snapshot. This is forkd’s core primitive: pause the source briefly, snapshot its memory and vCPU state, then resume. The resulting snapshot can be used as snapshot_tag in any future spawn_sandboxes call — fan out N children that all inherit the source’s exact state at branch time.
sandbox_id
string
required
Id of the source sandbox (see list_sandboxes).
tag
string
Optional name for the new snapshot. When unset the daemon generates branch-<sandbox-id>-<unix-ts>.
mode
string
v0.4+ canonical mode selector. One of "full", "diff", or "live". Prefer this over the legacy diff boolean.
  • "full" — copy entire guest RAM (0.5–8 s pause). Default.
  • "diff" — Firecracker Diff snapshot (v0.3+). ~200 ms idle source; 6–15× faster on typical agent workloads.
  • "live" — UFFD_WP (v0.4+). Sub-50 ms source pause; requires source spawned with live_fork=True. Mutually exclusive with diff.
diff
bool
default:"false"
Legacy. Equivalent to mode="diff". Kept so the server can drive v0.3.x daemons that don’t understand mode. Mutually exclusive with mode.
measure_diff
bool
default:"false"
Measurement-only hook. Takes a Diff snapshot inside the existing Full pause to report what diff would have cost, without changing semantics.
wait
bool
default:"true"
v0.4+, only meaningful with mode="live". Default True blocks until the background copy finishes (status="ready"). Set to False to return after the source resumes (~10 ms); poll list_snapshots until status="ready".
returns
SnapshotInfo
tag, dir, pause_ms, and status when mode="live".

create_snapshot

Build a parent snapshot from a kernel and rootfs. Boots a fresh VM with the given kernel + rootfs, waits boot_wait_secs for the guest to settle, snapshots it, and registers it under tag.
tag
string
required
Name to register the snapshot under (alphanumeric + dash/underscore, 1–64 chars).
kernel
string
required
Host path to a vmlinux kernel image.
rootfs
string
required
Host path to a rootfs image (.ext4 for writable, .squashfs for read-only).
rw
bool
default:"false"
When true, mount the rootfs read-write. Auto-enabled for .ext4 paths.
tap
string
Optional host tap device to attach as guest eth0 (create with scripts/host-tap.sh).
boot_wait_secs
int
default:"10"
Seconds to wait for the guest to settle before snapshotting. Increase to 30+ for snapshots that need to warm up large Python packages.
returns
SnapshotInfo
The registered snapshot. Durable across daemon restarts.

list_sandboxes

List currently-alive child sandboxes. No parameters.
returns
list[SandboxInfo]
All live sandboxes the daemon tracks.

get_sandbox

Fetch metadata about one sandbox by id.
sandbox_id
string
required
The sandbox id to inspect.
returns
SandboxInfo
Full metadata for the sandbox.

exec_command

Run a subprocess inside a sandbox.
sandbox_id
string
required
The target sandbox id.
args
list[string]
required
Argv list (e.g. ["python3", "-c", "print(2+2)"]).
timeout_secs
int
default:"30"
Maximum seconds to wait for the command.
returns
dict
{"stdout": str, "stderr": str, "exit_code": int}

eval_code

Evaluate a Python expression against the sandbox’s warmed PID-1. The parent VM already imported numpy, torch, etc.; eval_code returns in single-digit milliseconds instead of ~100 ms for a fresh subprocess.
sandbox_id
string
required
The target sandbox id.
code
string
required
Python expression to evaluate. Pre-imported packages are in scope.
returns
dict
{"result": Any, "error": str | None, "exit_code": int}

wait_for_text

Poll a file inside a sandbox until it contains a marker string. Common pattern: an agent writes its progress to a log file inside the guest; the orchestrator polls until a marker like "READY_TO_BRANCH" appears, then triggers branch_sandbox.
sandbox_id
string
required
The target sandbox id.
path
string
required
Absolute path inside the guest of the file to poll.
marker
string
required
Substring to wait for.
timeout_secs
float
default:"60"
Maximum wall-clock seconds to wait.
poll_interval_ms
int
default:"200"
How often to re-check in milliseconds.
returns
dict
{"found": bool, "elapsed_ms": int, "last_excerpt": str}. When found is false, last_excerpt contains the last ~256 bytes of the file so you can see what was actually written.

ping_sandbox

Round-trip to the in-guest agent. Returns its health and runtime info.
sandbox_id
string
required
The target sandbox id.
returns
dict
Health and version info from the in-guest agent.

kill_sandbox

Terminate one sandbox. Kills the Firecracker process and removes its cgroup leaf.
sandbox_id
string
required
The sandbox id to terminate.
returns
dict
{"id": str, "killed": true}

Environment variables

FORKD_URL
string
default:"http://127.0.0.1:8889"
Base URL of the forkd-controller daemon. Override when the daemon runs on a non-default port or a remote host.
FORKD_TOKEN
string
Bearer token for the controller daemon. Required when the daemon was started with --token-file. Read the token from /etc/forkd/token on Linux installs: sudo cat /etc/forkd/token.
FORKD_HTTP_TIMEOUT
string
default:"60"
Per-request HTTP timeout in seconds. Branching a large VM can take several seconds; increase this on slow storage.

Smoke test

# Start the controller daemon (if using systemd):
sudo systemctl start forkd-controller

# Run the MCP server standalone on stdio:
FORKD_TOKEN=$(sudo cat /etc/forkd/token) forkd-mcp
# The server blocks on stdin waiting for an MCP client.
To test without a full MCP client, point the MCP Inspector at the server:
npx @modelcontextprotocol/inspector forkd-mcp

Example: Claude branching an agent mid-thought

Once forkd-mcp is registered, Claude can orchestrate the full branch-and-fan-out pattern from a single conversation. A typical session looks like this:
  1. Claude calls list_snapshots to discover available parent templates (e.g. langgraph-react).
  2. Claude calls spawn_sandboxes with live_fork=True to boot a source sandbox with memfd-backed RAM.
  3. Claude calls exec_command repeatedly to run the agent’s warmup steps inside the sandbox.
  4. Claude calls wait_for_text on a progress file, blocking until the agent writes "READY_TO_BRANCH".
  5. Claude calls branch_sandbox with mode="live" and wait=False — source pause is sub-50 ms, Claude returns in ~10 ms.
  6. Claude polls list_snapshots until the branch snapshot shows status="ready".
  7. Claude calls spawn_sandboxes with the branch tag and n=3, then injects a different steering hint into each child via exec_command.
  8. Claude calls kill_sandbox to tear down the source and all children when done.
All three child agents inherit the source’s exact reasoning state — including any in-memory computation, loaded model weights, or partially executed tool calls — and diverge under copy-on-write from that point forward.

Prerequisites

forkd-mcp is a thin proxy — it holds no VM state itself. The forkd-controller daemon must be running and reachable at FORKD_URL for any tool call to succeed. See the forkd quick start for daemon setup and the operator runbook for production deployment.
  • Linux x86_64 with KVM (/dev/kvm accessible)
  • forkd-controller daemon running (systemd unit at packaging/systemd/forkd-controller.service)
  • At least one registered snapshot (forkd pull deeplethe/langgraph-react or forkd from-image python:3.12-slim --tag mysnap)
  • For mode="live" BRANCH: Linux kernel 5.7+, vm.unprivileged_userfaultfd=1, and the vendored Firecracker fork

Build docs developers (and LLMs) love