microvm-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@microvm-mcpCreate a Debian microVM and runpython3 --version."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
microvm-mcp
A standalone first pass at private Linux development workspaces over MCP and HTTP. One workspace credential can create and operate its own Firecracker microVMs. Commands execute in a full Debian guest, never in the service process.
This directory is its own Python package, dependency lockfile, image recipe, CLI,
and test suite. It can move into another repository unchanged. It imports nothing
from fast-agent. The optional fast-agent integration generates configuration for
the existing host_fetch transport; no harness or demo code changes are required.
Implemented
Official MCP Python SDK, Streamable HTTP at
/mcp, plus matching JSON HTTP operations atPOST /v1/tools/{tool_name}. Both share validation and authorization.Host-issued opaque workspace tokens, SHA-256 token hashes in SQLite, expiration, rotation without changing ownership, and workspace revocation. Every request is authenticated; every VM/job/file operation checks workspace ownership.
One Debian 13 image with Python, Node, Git, Bash, GCC and standard build tools. Guest commands run as UID 1000. Customize the trusted image recipe to install system packages; the API does not grant root or passwordless sudo to commands.
One jailed Firecracker process and cgroup per VM. Distinct jail UIDs, private network namespaces, CPU/memory/PID limits, no host-directory mounts in the guest.
Reflink CoW raw ext4 disks on an XFS/Btrfs host filesystem. Unsupported storage fails preflight. Guest disks are fixed at the image's size (3 GiB in the recipe).
Async command handles; separate stdout/stderr returned as readable UTF-8 text; a shared output bound; cancellation/timeouts cover descendants through guest cgroups, including processes that call setsid.
Workspace file transfer using base64, bounded to 128 KiB per call, with symlink traversal refused. Larger artifacts can be inspected through command output; bulk upload/download is not part of this first pass.
Manual pause (retains RAM), hibernate (full memory/device checkpoint, releases the VMM process), resume, automatic idle suspension, and automatic wake on work.
Network off by default. Network on adds IPv4 NAT with guest-source enforcement, private/metadata destination denial and inbound denial. No guest IPv6 egress.
Durable metadata and creation/execution retry receipts. A transport-uncertain execution is not replayed automatically.
vm_status.jobsexposes known handles for reconciliation; the history is capped at 4096 requests per workspace.
Related MCP server: agent-sandbox-mcp
Host setup
The validated target is Linux x86_64 with real /dev/kvm, cgroup v2, matching
Firecracker and jailer 1.17.0, Python 3.11+, ip, nft, and iptables.
Firecracker 1.16 has a vsock pause/resume defect; preflight refuses versions older
than 1.17. The service currently runs as root to provision jails and networking.
Keep its listener on loopback; put authenticated remote access behind TLS.
Install Firecracker/jailer from the operator-verified
upstream release.
Supply an operator-pinned Firecracker-compatible Linux kernel with ext4,
virtio-block, virtio-net, virtio-vsock and cgroup/PID-controller support built in.
The real smoke used the Firecracker CI x86_64 vmlinux-6.1.155 kernel.
Kernel and rootfs SHA-256 digests are checked before serving.
Use a dedicated reflink-capable volume for the base image and state: reflinks
must stay on one filesystem. Reserve enough real capacity for every VM's fully
written disk and two RAM checkpoint generations, even though new disks share
blocks. Set max_storage_gib, max_memory_mib, and max_vms to the host budget;
these are conservative reservations across all retained VMs, including hibernated
VMs. Admission and checkpointing also check available disk space. This is not a
substitute for storage isolation from unrelated host services.
uv sync --locked
# Docker and mkfs.ext4 are needed only to build the image. Preserve guest UIDs.
sudo scripts/build-image /var/lib/microvm-images
sudo install -d -m 0700 /var/lib/microvm-mcpCopy examples/service.toml, set the binary/kernel/image paths, and replace the
digest placeholders using sha256sum. The image digest pins the deployed image;
the Docker recipe uses Debian package repositories and is not a bit-reproducible
historical rebuild. Reserve unused host UIDs starting at uid_base, one per VM
slot. Do not reuse this range for host users or another service instance.
Networking needs operator-enabled IPv4 forwarding and an explicit uplink.
The service uses 10.231.<slot>.0/30 and 10.232.<slot>.0/30; these ranges must not
overlap existing routes. For each online VM it adds a private network namespace,
veth/TAP, route, nftables isolation/NAT table and narrowly scoped iptables FORWARD
rules. It removes those on hibernation or deletion. It never flushes host rules or
changes the host's default firewall policies. Add public management networks to
blocked_cidrs; private-address filtering cannot recognize a public control plane.
sudo .venv/bin/microvm-mcp issue-token \
--state-dir /var/lib/microvm-mcp --workspace demo-alice \
--output /run/microvm-alice.token --ttl 86400 --allow-network
sudo .venv/bin/microvm-mcp serve --config /etc/microvm-mcp.tomlIssuance is a local administrator action, not an MCP tool. Token files are 0600 and are never printed. Give the trusted client/broker read access to its token file. Reissuing for the same workspace creates another token and preserves its original quota/policy; it does not revoke the old token or widen permissions. Do not put tokens in model prompts, tool arguments, guest environments or images.
sudo .venv/bin/microvm-mcp revoke-workspace \
--state-dir /var/lib/microvm-mcp --workspace demo-aliceRevocation prevents future access, including refresh by this CLI. Existing jobs are not automatically killed. An operator needing immediate containment must stop the workspace's VM cgroups as well. An integrated revoke-and-stop admin command is not implemented. Credential expiry likewise does not delete VM state.
MCP and HTTP clients
Configure an HTTP MCP client with URL http://127.0.0.1:8090/mcp and the workspace
credential in its Authorization: Bearer ... header. The SDK handles modern and
legacy protocol negotiation. This deployment uses provisioned bearer credentials;
OAuth login/discovery and interactive client registration are not implemented.
For ordinary HTTP clients, send the same arguments as JSON to the named operation:
POST /v1/tools/vm_create
{"request_id":"workspace-create-1","network":"off","memory_mib":1024,"vcpus":2}
POST /v1/tools/vm_exec
{"vm_id":"<returned id>","request_id":"build-1","command":"python3 -c 'print(2+2)'"}Requests require the authorization header; errors use non-2xx status codes and
{"error":{"code":...,"message":...}}. MCP tool errors use isError=true.
Creation and execution require request_id. Reuse the same ID and arguments for
a retry. A reused ID with changed arguments is refused. An interrupted operation
with an unknown outcome is also refused instead of executing again.
Tools: vm_create, vm_list, vm_status, vm_exec, vm_job_status,
vm_job_output, vm_job_cancel, vm_read_file, vm_write_file, vm_suspend,
vm_resume, vm_destroy. Discovery advertises their complete JSON schemas.
File paths and command working directories are relative to /workspace.
There are at most four active jobs and 256 total jobs per VM.
Reading command output
vm_exec returns a job_id. Call vm_job_output while the job runs or after it
finishes. Its default response contains separate, readable stdout and stderr
strings; an LLM does not need to decode base64 to read command results:
{
"job_id": "<returned job id>",
"encoding": "utf-8",
"stdout": "0, 1, 1, 2, 3, 5\n",
"stderr": "",
"next_cursor": {"stdout": 17, "stderr": 0},
"decoding_errors": {"stdout": false, "stderr": false},
"state": "completed",
"exit_code": 0,
"truncated": false,
"timed_out": false
}Omit cursor on the first call. Pass the entire returned next_cursor object
as cursor on the next call, alongside vm_id and job_id. Each cursor field
is a byte offset in that stream, not a character count. Omitted stream offsets
start at zero. Reading is non-destructive: repeating a cursor rereads the same
retained bytes. Once the job is completed, continue reading until neither offset
advances. An empty response while the job is running is not end-of-output.
The guest supervisor drains both pipes independently. Up to 256 KiB total is
retained across the two streams, with at most 16 KiB per stream per call.
Excess output is drained and discarded, with truncated=true; it does not block
the command. Ordering within each stream is preserved; there is no ordering
guarantee between stdout and stderr. Use python3 -u or explicit flushing when
you want prompt output from Python.
Text decoding preserves whitespace, line breaks, and tracebacks. Incomplete UTF-8
characters at a page boundary or at the current end of a running stream wait for
the next bytes. Invalid bytes, including incomplete characters at EOF or the
retention limit, become �; decoding_errors flags the affected streams for
that response. A literal valid � does not set the flag.
For exact binary output, request "encoding":"base64". The response then has
stdout_base64 and stderr_base64 instead of the text fields, and omits
decoding_errors. The same byte cursors and limits apply. File transfer tools
continue to use base64.
The host retrieves output over Firecracker's Unix-socket-to-vsock bridge to the supervisor on guest port 1024, then returns it through the authenticated HTTP/MCP API. Guest networking can remain off. Workspace credentials stay on the host.
Upgrade note: this replaces the previous combined data_base64 output and
integer cursor. Update clients and regenerate optional adapter manifests. Rebuild
the guest image, pin its new SHA-256 in the service config, and create new VMs.
Existing guests and restored checkpoints keep their old supervisor; restarting
the host service alone does not upgrade them.
Suspension and failure semantics
Creation defaults to hibernating after 300 idle seconds. Set idle_seconds=0 to
disable it, or choose idle_mode="pause". Active managed commands, including
detached descendants, prevent automatic suspension. Status polling does not keep
an otherwise idle workspace awake. Background system daemons are not counted as
user activity. This is a scheduling heuristic, not a host security boundary.
Manual suspend defaults to mode="hibernate" and refuses active jobs. With
force=true, it freezes them. VM status, list and cached job status never wake a
suspended VM. Execution, file transfers, output retrieval and cancellation do.
Cancellation of a frozen job resumes the guest before terminating its cgroup.
Managed command timeouts exclude suspension; suspension can extend wall time.
Before stopping vCPUs, the supervisor freezes the managed jobs' entire cgroup
subtree and pauses timeout accounting. On boot and every resume, the host sends
its current wall time through the private vsock control channel. The guest sets
CLOCK_REALTIME before thawing jobs, and restores their remaining timeout budgets
under the supervisor lock before timeout checks resume. This works with both
tsc and kvm-clock, including network-off VMs. Failed synchronization stops
the VMM and marks the VM failed.
Old images/checkpoints without this lifecycle protocol are rejected; rebuild and
pin the guest image and create new VMs when upgrading.
Firecracker's clock_realtime snapshot option is deliberately disabled: with
kvm-clock, it also advances monotonic time across a restore. Wall time is instead
corrected separately, without changing the monotonic clock. This is a correction
at boot/resume, subject to local control-channel latency, not continuous NTP/PTP
synchronization; the host clock must itself be accurate. Applications' own
timers and sleeps still follow guest kernel semantics: plain pause advances
monotonic time, while hibernation normally excludes time spent stopped. The
supervisor compensates its managed command timeouts for either behavior, but
does not rewrite application timers. Wall-clock deadlines can expire during
suspension.
Hibernation pauses the guest, writes a full memory/device checkpoint, drains disk I/O, retains the corresponding private disk, fsyncs checkpoint metadata, stops the process and removes its network resources. Resume restores that same VM on the same host. It does not clone a checkpoint into a second VM or promise live TCP connections survive. The snapshot's Firecracker binary digest is checked. Never modify a hibernated VM disk externally or restore stale memory over a disk that has since run. The state machine marks interrupted mutations failed rather than silently rolling back to such a checkpoint.
Graceful service shutdown checkpoints running/paused VMs, including active jobs. After a service crash, hibernated VMs remain restorable; other VMs are marked failed and leftover processes are killed through their owned cgroups. They can be inspected/deleted, but automatic recovery of in-flight commands is deferred. There is no HA, cross-host migration, VM sharing, interactive terminal, inbound web preview, automatic VM retention expiry, or public production deployment in this first pass. Guest roots/checkpoints are sensitive workspace data and remain inside the service's private state directory.
Optional fast-agent-demo integration
The adapter uses host_fetch, so the host owns the token and TLS. The service does not know about fast-agent sessions, manifests, state envelopes or the VMM. Generate a fragment and append it to the demo's existing tool manifest:
.venv/bin/microvm-mcp demo-manifest \
--endpoint http://127.0.0.1:8090 --vm-class exec > microvm-tools.fragment.tomlThe fragment assumes that the existing manifest defines an exec class (the
packaged demo does). That class is schema-required but inert for host_fetch.
Add this entry to the host's existing private secrets file:
[secrets.microvm_workspace]
file = "/run/microvm-alice.token"Run the demo with that manifest, --secrets, --stdio-tools and --expose-tools,
as its normal launcher already does. Use an HTTPS endpoint and a matching service
Host allowlist when the service is remote. The returned tool output has the normal
fast-agent host_fetch wrapper, {"status":200,"body":{...}}.
One static demo credential means one workspace for all sessions in that demo
instance. It must not be presented as per-user isolation on a shared public demo.
For the first demo, use one separately configured demo instance per workspace.
Shared multi-user hosting needs a future host-side adapter that derives workspace
identity from authenticated context and chooses its credential. A caller-supplied
session_id/workspace_id is not authorization. Neither service transport accepts
an ownership override, and the generated tool schemas contain no token field.
examples/tool_probe_agent.py exercises this path without an LLM. Boot it with
fa-serve and the generated manifest, then send a turn with
{"input":{"tool":"vm_create","args":{"request_id":"probe-1"}}} and a
normal front-end session ID. Its purpose is integration validation only.
Validation
uv run --locked ruff check src guest tests scripts examples
uv run --locked ruff format --check src guest tests scripts examples
uv run --locked pytest
# Run on a provisioned host. Missing KVM, jailer, reflinks etc. are hard failures.
sudo .venv/bin/python scripts/smoke.py --config /etc/microvm-mcp.toml --network
# Longer suspension probes, including jobs with a three-second timeout:
sudo .venv/bin/python scripts/smoke.py --config /etc/microvm-mcp.toml \
--network --clock-suspend-seconds 30The unit suite uses a fake backend that cannot execute code. The separate real
smoke boots jailed VMs, checks Fibonacci output, separate streams, UTF-8 pagination,
binary output, and output limits, compiles generated C, checks disk isolation, preserves a
running process across suspension, checks wall time and managed timeouts across
pause, hibernation, and pause-to-hibernate transitions, checks automatic
hibernation, verifies network policy, and denies cross-workspace access. It deletes
its VMs and revokes its credentials in cleanup. See VALIDATION.md for measured
results and the history of smoke failures and fixes.
License
Licensed under the Apache License, Version 2.0.
Related MCP Connectors
MCP server for Superserve sandboxes: create, exec, and manage Firecracker microVMs
Persistent Linux microVMs for agents: root, internet, sub-second resume and a public URL.
Build, deploy, and host full-stack web apps from any MCP client. DB, auth, storage, cron included.
Create, deploy, and operate MCP servers directly from your GitHub repositories.
Related MCP Servers
AlicenseAqualityCmaintenanceRuns AI-generated code in secure Firecracker microVMs with opt-in network policy enforcement, PII scanning, prompt injection defense, and audit logging. Exposes MCP tools for running commands, managing files, and the full sandbox lifecycle.737 npm1Apache 2.0- AlicenseNot gradedqualityBmaintenanceEnables AI agents to create, manage, and execute code in isolated Firecracker microVM sandboxes via the MCP protocol, with support for sandbox lifecycle and file operations.3Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables time-travel debugging for AI agent sandboxes using Firecracker microVMs, with tools for code execution, file operations, and VM snapshot/restore via MCP.MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI clients to securely operate isolated coding workspaces with file, command, Git, and deployment tools via authenticated remote MCP.8MIT