Ops & agents

MCP server design

An MCP server that gives AI agents the full VM lifecycle through 21 tools that mirror the CLI and the API.

flash-mcp is the Model Context Protocol server for FlashVMM: a JSON-RPC 2.0 process on the MCP stdio transport that gives an AI agent the whole FlashVM lifecycle through 21 tools mapped one to one onto the flash CLI and the OpenAPI-described REST resources of flashvm, flashpoold, flashhostd, flashgpud and flash controller.

One source crate (flash-mcp-core) carries the tool contract (names, input schemas and descriptions), and each operating system's flash-mcp binary supplies its native transport: Unix sockets on Linux and macOS, named pipes on Windows. An agent learns one tool set and drives every platform with it. Every tool description is written for a model: it states the exact arguments, the reply shape and the next tool to call. See MCP for what agents do with it.

Key facts

Runs onLinux (x86_64, arm64), macOS (Apple silicon), Windows (x64, arm64); one tool contract on all of them
InterfacesMCP tools/list · tools/call · notifications/progress · backed by the CLI and REST
Tools21
ProtocolJSON-RPC 2.0, one message per line on stdin/stdout; protocolVersion 2024-11-05; capability tools
Transport to a FlashVMHTTP/1.1 over the VM's own Unix socket (Linux, macOS) or named pipe (Windows)
flash_cli timeout120 s per call; argv passed as an array, no shell
flash_exec defaultstimeout_s 300; output kept up to 16 MiB per stream
Fan-outflash_watch_fleet merges up to 20 FlashVM streams

How it works

A thin, faithful layer

flash-mcp holds no state of its own. The MCP client starts it as a child process; it answers initialize, lists its tools on tools/list, and turns each tools/call into exactly the request an operator's flash would make. Results come back as MCP tool results, with isError set from the real outcome and the daemon's own error body carried through unchanged.

Three kinds of tool

  • Create and destroy through the real binary. flash_cli runs the installed flash with an argv array. Creation (run, restore, clone, fork) goes through the same code path an operator uses (spawning the flashvm process with its capability drops, userfaultfd detection on Linux and registry locking), so an agent gets identical VMs and identical behavior.
  • Typed per-VM tools. flash_ls, flash_inspect, flash_metrics, flash_diagnose, flash_logs, flash_exec, flash_cp, flash_diff, flash_connections, flash_secret_proxy_calls, flash_sleep, flash_wake and flash_resource each name one job with a structured reply (for example flash_exec returns {exit_code, signal, state, stdout, stderr, duration_ms, truncated}). flash_api sends any method to a running VM's REST API for every write the typed tools leave to it, such as PATCH /v1/flashvm/nics/{i} or PUT /v1/flashvm/workload.
  • Daemon tools with route allowlists. flash_pool (flashpoold, warm pools), flash_host (flashhostd, the host), flash_gpu (flashgpud) and flash_controller (the controller) each send one request to their daemon. Each tool checks method and path against the routes in that daemon's OpenAPI document, segment by segment, before anything is sent; a test keeps the list equal to the spec.

Streaming

flash_watch opens one of a VM's server-sent event streams — metrics, logs, lifecycle or guest_messages — and emits one MCP notifications/progress message per event against the call's progressToken while the call is in flight. flash_watch_fleet runs the same watch for up to 20 VMs on parallel threads and prefixes each message with its FlashVM id; every stdout write takes its own short lock, so concurrent streams interleave cleanly.

Per-platform transport

Each OS uses its native local IPC: Linux and macOS reach each flashvm and each daemon over a Unix socket (flashpoold at /run/flashpoold.sock, flashhostd at /run/flashhostd.sock, flashgpud at /run/flashgpud.sock by default on Linux); Windows uses named pipes, and the tools' address argument is named pipe there. The descriptions are localized at tools/list time so the model reads its own platform's addresses.

Interfaces

CLI

# register flash-mcp with an MCP client as a stdio server, then drive it; the same work by hand:
flash run --bundle /opt/flashvm/bundles/current --vcpus 2 --mem-mib 1024 --egress public --id agent1 --detach
flash exec agent1 -- sh -c 'uname -a'
flash checkpoint agent1
flash fork agent1 --count 3
flash connections agent1

MCP

flash_cli(args: ["run", "--bundle", "/opt/flashvm/bundles/current", "--vcpus", "2", "--mem-mib", "1024", "--egress", "public", "--id", "agent1", "--detach"])
flash_exec(vm_id: "agent1", argv: ["sh", "-c", "pip install requests"], changes: true, checkpoint: true)
flash_diff(a: "agent1", b: "agent1-fork1")
flash_watch(vm_id: "agent1", stream: "lifecycle")
flash_watch_fleet(vm_ids: ["agent1", "agent2"], stream: "metrics")
flash_pool(method: "POST", path: "/v1/vm-pools", body: {"shape": {"vcpus": 1, "mem_mib": 256}, "count": 8})
flash_host(method: "GET", path: "/v1/host")

REST

flash_api(vm_id: "agent1", method: "PATCH", path: "/v1/flashvm/nics/0", body: {"egress": "allow:pypi.org,*.pythonhosted.org"})
flash_api(vm_id: "spare-3", method: "PUT", path: "/v1/flashvm/workload", body: {"entry": "/app/run", "entry_arg": []})

Contract an agent relies on:

  • flash_watch_fleet rejects a request for more than 20 ids as a whole.
  • A daemon tool rejects a method and path outside that daemon's OpenAPI routes before sending it.
  • flash_wake on a VM that slept with a NIC returns nic_missing until nic is given.
  • flash_cli run --engine with an engine the host lacks returns 422 unsupported_engine.
  • A claim returns once accepted; poll flash_inspect for entry_started and state.

Guarantees

  • One contract. The same 21 tool names, schemas and replies on Linux, macOS and Windows.
  • Same path as the operator. Agents create VMs through the installed flash binary, so every VM is built exactly as the CLI builds it.
  • No injection surface. Commands travel as argv arrays; no shell string is ever interpolated.
  • Spec-bound daemon calls. Daemon requests are checked against each daemon's OpenAPI routes before they leave.
  • Live progress. Event streams arrive as standard MCP progress notifications while the call runs.

What it does

Related