Some tasks deserve a clean machine: a test suite that needs root and Docker, a build that ties up the laptop for an hour, code you would rather not run at home. An AI agent can take care of that on its own, provided it can rent the machine, use it and give it back. That is what the FFxF MCP server is for.
This demo is a real run, made on 28 September 2026 with Claude Code: the agent ordered a Nano, got it in 42 seconds, worked on it over SSH, destroyed it in 10 seconds, and the whole thing cost 0.018 CAD. Below, every call it made, with what the server answered.
1. Connecting the agent
The server lives at https://ffxf.net/mcp. It relays each call to the
FFxF API with your API key and stores nothing. Create a key in the console,
Account → API keys, with only the scopes the agent needs: for this
demo vms.read, vms.create, vms.action,
vms.destroy, sshkeys.write and billing.read.
Then put it in the environment rather than in the conversation.
export FFXF_API_KEY="ffxf_live_…"
claude mcp add --scope user --transport http ffxf https://ffxf.net/mcp \
--header "Authorization: Bearer $FFXF_API_KEY"
claude mcp list
# ffxf: https://ffxf.net/mcp (HTTP) - ✔ Connected
Codex, Cursor and the other clients have their own command, and there is a shorter route: give the agent the sentence below, and it follows the instructions written for it in agents.md.
Set up the FFxF MCP server by following https://ffxf.net/agents.md. My API key is in the FFXF_API_KEY environment variable.
2. The request
The agent receives a plain request: run the project's tests on a fresh machine, then destroy it. The server itself reminds it of the ground rules each time it connects: estimate before ordering, show the price, poll the action, ask before erasing a disk.
3. What the agent does, call by call
First get_account, to know what it can spend. The answer gives the
balance, the hourly burn of machines already running and the monthly budget.
{ "credit": { "balance": "94.46" },
"hourly": { "burn_rate": "0.134", "active_machines": 2 },
"quota": { "limit": 5, "used": 3, "remaining": 2 } }
Then add_ssh_key with the local public key, so it can log in without a
password, and estimate_vm. The estimate runs every check of a real order
(quota, stock, credit, budget) and prices it, without creating anything.
estimate_vm { "plan": "nano", "region": "montreal", "image": "coding-agent",
"hostname": "mcp-e2e-01", "billing": "hourly", "ssh_keys": ["9"] }
{ "would_succeed": true, "hourly_rate": "0.018", "currency": "CAD",
"required_credit": "0.44",
"checks": [
{ "name": "quota", "status": "pass" },
{ "name": "stock", "status": "pass" },
{ "name": "credit", "status": "pass" },
{ "name": "budget", "status": "pass" } ] }
The image is coding-agent: Ubuntu with Docker, git and GitHub CLI,
Node.js, Python and uv, Claude Code and Codex CLI. The 0.44 CAD is not spent: an
hourly order is only accepted when the balance covers the first 24 hours. After
showing the price, the agent orders.
create_vm { …same order…, "password_delivery": "none" }
{ "vm": { "id": 105, "hostname": "mcp-e2e-01", "status": "provisioning" },
"action": { "id": 16, "type": "create", "status": "queued" } }
Creation runs in the background. The agent polls get_action until the
action leaves running, then reads the machine with
get_vm.
+1s action 16: running
+42s action 16: completed
get_vm 105: running · 1 vCPU · 2048 MB · 20 GB · IPv4 23.159.52.x · IPv6 2602:f3a4:0:100::1b
The disk was grown to the plan's 20 GB on the way. The agent logs in and does the
work: clone the repository into ~/work, run the tests, push the branch
or bring back the results.
ssh ubuntu@23.159.52.x
claude --version # 2.1.283 (Claude Code)
docker run --rm hello-world | head -2
df -h / # 19G, 3.5G used
cd ~/work && git clone https://github.com/… && …
When the task is over, destroy_vm, with the machine's exact hostname as
confirmation, then get_action once more.
destroy_vm { "vm": 105, "confirm_hostname": "mcp-e2e-01" }
+0s action 17 (delete): running
+10s action 17 (delete): completed
get_vm 105: deleted
4. The limits that held
An agent can get a machine wrong. The rules live in the API, not in its good behaviour, and the MCP server adds its own check on the two calls that erase a disk. Asked to reinstall another machine with the wrong name, it refuses before the API is even called:
reinstall_vm { "vm": 102, "image": "debian-13", "confirm_hostname": "NOT-BENCH" }
{ "error": { "status": 400, "code": "confirmation_mismatch",
"message": "confirm_hostname must be \"BENCH\"." } }
- Scopes: without
vms.destroythe key cannot delete anything, withoutvms.createit cannot order. - Prepaid credit: the balance never goes negative, and the first 24 hours must be covered.
- Monthly budget, set in the console: machines stop past 120 % of it.
- Retries: an order repeated after a timeout gets the first answer back, not a second machine.
- Quota: five machines per account by default.
5. What it cost
GET /v1/usage keeps the ledger hour by hour. For this run, one line:
one hour started, at the Nano rate.
{ "hostname": "mcp-e2e-01", "hours": 1, "rate": "0.018", "amount": "0.018", "currency": "CAD" }
Every hour started is owed, so a machine kept for two minutes costs as much as one kept for fifty-nine. For an agent that hands its machine back as soon as the task is done, that is the whole bill.
6. Making it a habit
For the agent to do this without being told each time, add a few lines to the
project's AGENTS.md or CLAUDE.md: when a task needs a real
machine, rent one, show the price, stay under a ceiling, destroy it afterwards. The
ready-to-paste block is in Remote mode.
- Key with only the scopes needed, kept in
FFXF_API_KEY. - Monthly budget set in the console.
estimate_vmbefore everycreate_vm, price shown.- Image
coding-agent, hourly billing, SSH key, no password. destroy_vmas soon as the task is done, thenget_accountto check nothing is left running.
The twenty tools and their scopes are listed in MCP tools, and the setup for each client in Connect an AI agent.