Skip to content

Fly.io — Provisioning Machines using MCPs

Summary

Sam Ruby's short Fly.io developer-blog post (2025-05-07) marking the mutation transition of Fly.io's flyctl MCP server: the read-only two-tool prototype from a month earlier ([[sources/2025-04-10-flyio-30-minutes-with-mcp-and-flyctl|30 Minutes With MCP and flyctl]]) now supports write operations — the author "created my first fly volume using an MCP … and it worked the first time," then a few hours later (with GitHub Copilot assistance) added support for all fly volumes subcommands. The architectural thesis: "Today's state of the art is K8S, Terraform, web-based UIs, and CLIs. Those days are numbered." Ruby sketches the "Make it so" provisioning flow — LLM scans your code, produces a plan (regions, databases, S3, memory, machines), you adjust, you approve, it runs, it examines logs on failure. The post lands in flyctl v0.3.117 and documents a claude_desktop_config.json wiring template pointing at flyctl mcp server. One load-bearing safety claim: because the MCP server shells out to flyctl, "I would have received an error had I tried to destroy a volume that is currently mounted. Knowing that gave me the confidence to try the command" — the CLI's existing safety invariants become the agent's guardrail at zero additional engineering cost.

Key takeaways

  1. The mutation transition. One month after the read-only fly logs + fly status prototype, the same MCP server now covers the full fly volumes surface (create / list / extend / show / fork / snapshots / destroy). This is the wiki's first instance of a wrapped-CLI MCP server crossing the read-only → mutating boundary with production resources. "A few hours later, and with the assistance of GitHub Copilot, i added support for all fly volumes commands."
  2. "Make it so" as the new provisioning UX. Ruby's thesis paragraph is the canonical wiki statement of concepts/natural-language-infrastructure-provisioning: "Imagine a future where you say to your favorite LLM 'launch my application on Fly.io', and your code is scanned and you are presented with a plan containing details such as regions, databases, s3 storage, memory, machines, and the like. You are given the opportunity to adjust the plan and, when ready, say 'Make it so'." This is a plan-then-apply loop — Terraform's control discipline reimplemented as an LLM interaction.
  3. CLI safety invariants protect the agent user. "Since this support is built on flyctl, I would have received an error had I tried to destroy a volume that is currently mounted. Knowing that gave me the confidence to try the command." The MCP server inherits every guardrail the CLI's human-operator path already enforces — no agent-specific authz layer, no refuse-list, no dry-run mode required at the MCP tier. Canonical wiki instance of patterns/cli-safety-as-agent-guardrail: wrap a CLI that already knows how to refuse unsafe operations, and the wrapper stays a thin proxy. Structurally the same argument as the 2025-04-10 --json-as-load-bearing observation (concepts/structured-output-reliability) — five-year-old CLI design decisions become load-bearing for AI integration.
  4. Agent-surfaced resource hygiene as an emergent UX. "I asked for a list of volumes for an existing app, and Claude noted that I had a few volumes that weren't attached to any machines. So I asked it to delete the oldest unattached volume, and it did so." The LLM spotted a hygiene issue the human hadn't specifically asked about; the human responded to the finding with a mutation. Sibling shape to the agentic troubleshooting loop extended into provisioning — "the LLM noted something, brought it to my attention, and I asked it to make a change as a result."
  5. Alternatives the author explicitly rejects. Ruby enumerates three mechanisms he could have used and why the MCP flow beat each: (a) Machines API"would have required some effort"; (b) flyctl directly"would have had to use the documentation and a bit of copy/pasting of arguments"; (c) Fly.io dashboard"isn't any way to sort the list or delete a volume" (dashboard is read-shaped; mutations are still CLI-first). Load-bearing wiki datum on concepts/agent-ergonomic-cli: agent-driven provisioning flattens the dashboard-vs-API-vs-CLI choice the author would otherwise have to make.
  6. Vision — MCP servers on Fly private network, sidecar, or in-app. "There will be MCP servers running on your Fly.io private network — either on separate machines, or in 'sidecar' containers, or even integrated into your app. These will enable you to monitor and interact with your application." First wiki disclosure of Fly.io's near-term roadmap for in-network MCP surfaces. Pairs with the 2025-04-08 "Our Best Customers Are Now Robots" post's long-lived-SSE session affinity framing — the routing infrastructure for this is already being built.
  7. MCP Inspector as agent-free validation surface. "You don't even need a LLM to try out the flyctl MCP server. If you have Node.js installed, you can run the MCP Inspector … visit http://127.0.0.1:6274/, click on 'Connect', then 'List Tools', select 'fly-platform-status', then click on 'Run Tool'." Tooling-ergonomic observation: the MCP ecosystem has a local-dev-mode validator so server authors can iterate on tool schemas without burning LLM tokens.
  8. Local MCP server risk continues. The post doesn't repeat the 2025-04-10 security caveat explicitly, but the shape is unchanged — the Claude Desktop / Cursor / GitHub Copilot instance is running flyctl as a native subprocess, now with mutation authority. "If you ask it to destroy a volume, that operation is not reversable. Perhaps try this first on a throwaway application." Canonical wiki instance of concepts/local-mcp-server-risk extending into the mutation-authority regime; the read-only → mutating transition raises the blast radius of prompt-injection accordingly.

Systems / concepts / patterns extracted

Systems - systems/fly-flyctl — the CLI being wrapped; now has an mcp server subcommand exposing tools. - systems/model-context-protocol — the transport. - systems/fly-volumes — the first mutating-resource surface added to the MCP server (full fly volumes subcommand set). - systems/fly-machines — second-order surface; volumes are attached to Machines, so the MCP hygiene flow surfaces Machine-attachment state. - systems/claude-code — sample MCP client via claude_desktop_config.json; GitHub Copilot is also named as an authoring assistant for expanding the server.

Concepts - concepts/natural-language-infrastructure-provisioning — the "Make it so" thesis; natural language replaces K8S / Terraform / UIs / CLIs as the human-infra interface. - concepts/agent-ergonomic-cli — the design-pressure framing; three named alternatives (API / CLI / dashboard) all lose to natural language through MCP. - concepts/agentic-troubleshooting-loop — extended into a provisioning / hygiene loop: agent surfaces a finding, human confirms, agent executes. - concepts/structured-output-reliability--json as the substrate enabling the wrap. - concepts/local-mcp-server-risk — the security posture, now with mutation authority.

Patterns - patterns/wrap-cli-as-mcp-server — extended: the read-only 90-LoC April prototype now covers a full-mutation resource family without rewriting the substrate. - patterns/plan-then-apply-agent-provisioning — the "Make it so" interaction shape; plan presented, adjusted, approved, then executed; on failure the LLM pulls logs + proposes next steps. - patterns/cli-safety-as-agent-guardrail — the CLI's existing invariants (can't destroy a mounted volume) become the MCP server's authorization boundary for free.

Operational numbers

  • Release version. flyctl v0.3.117 is the first flyctl that ships the MCP-server-with-volumes-support.
  • First-try success. Volume creation via MCP worked "the first time."
  • Build velocity. Full fly volumes subcommand family added "a few hours later, and with the assistance of GitHub Copilot."
  • Config format. claude_desktop_config.json snippet binds the server as {"command": "/Users/rubys/.fly/bin/flyctl", "args": ["mcp", "server"]}.
  • MCP Inspector local port. http://127.0.0.1:6274/.
  • Timeline gap. 2025-04-10 (read-only flymcp / 2 tools) → 2025-05-07 (mutation + full volumes surface) = ~27 days from read-only to production-mutation MCP.

Caveats / open questions

  • No code disclosure for the mutation wrappers. The post names internal/command/mcp/server.go + the server/ directory on superfly/flyctl but doesn't paste the actual tool-registration shape, safety checks at the MCP layer (if any beyond the CLI's own), or elicitation / confirmation UX.
  • No failure-mode numbers. No discussion of what happens on a prompt-injection attempt that asks the agent to destroy any volume without confirmation; the mitigation story leans on flyctl-level checks, but those only cover mounted vs unmounted, not is this the user's intent.
  • "Make it so" is a roadmap sketch. The plan-then-apply UX is described as "not science fiction" but "some assembly required"; it's an aspirational target for the post, not a deployed mechanism in v0.3.117.
  • No scope on write-surface ceiling. The post doesn't say which other mutating command families are next (Machines, apps, networks, secrets, etc.); the implied gradient is "see what works well and what doesn't work so well, make adjustments, build support in a bottoms up fashion, and iterate rapidly."
  • Local-MCP security caveat is understated vs the 2025-04-10 post. Given the mutation transition, the expected structural answer (patterns/disposable-vm-for-agentic-loop) isn't re-emphasized; the reader has to carry the earlier post's risk framing forward.
  • Claude Desktop / Copilot / Cursor lock-in not discussed. The snippet is Claude Desktop–specific; the stdio transport means any MCP client works, but no cross-client matrix is given.

Source

Last updated · 200 distilled / 1,178 read