Skip to content

The mf CLI

mf is the official managed.dev command-line client. Every command is a thin shell over a public API endpoint, so the CLI never does anything the API can’t — what it can do is governed by your API key’s scopes and your role. The CLI is free on every plan.

This page is the task-oriented walkthrough: install, sign in, the everyday deploy loop, and scripting. The exhaustive command-group reference lives at the CLI reference.

mf is a single Go binary:

Install mf
go install terriblegit.com/terrible/mf/cmd/mf@latest
mf version
# mf 0.2.0

mf login verifies an API key and stores it in the CLI’s config file. There is no browser flow — you paste a key you minted in the dashboard (or via POST /v1/api-keys), and the prompt reads it with echo disabled so it never lands in your shell history:

Authenticate the CLI
mf login
# Paste your API key: (hidden)

The recommended practice is to not store that key at all — pass --mint and mf login uses it once to mint a narrower, shorter-lived key, then stores that instead:

Down-scope on login (recommended)
mf login --mint --scopes sites:read,deployments:write,jobs:read --ttl 24h

--ttl is rounded up to whole days; without it the server default of 90 days applies. mf logout removes the stored key.

For CI, skip mf login entirely and pass the key through the environment:

Non-interactive auth for CI
export FORGE_TOKEN="mfk_live_…"
mf sites list

mf deploy build kicks off a build — an async job — and by default auto-deploys the resulting artifact. Async commands accept --wait (poll quietly to completion) or --follow (stream progress); with neither, they print the job id and return immediately. Either way the command exits non-zero if the job fails, so it drops cleanly into a pipeline step:

Build and deploy, streaming progress
mf deploy build --builder wordpress --ref feature/checkout --follow

mf deploy targets the production environment by default; pass --env for anything else. If a release goes wrong, roll back to a previous deployment:

Roll back a deployment
mf deploy deployments # find the deployment to return to
mf deploy rollback dep_01J9… --wait

Day-to-day you’ll still deploy most changes with git push managedmf deploy is for scripting, re-deploys, and CI.

mf envs reads the site from --site or the config’s default_site:

Create a branch environment
mf envs create --type branch --branch feature/checkout --wait
Tear one down when the branch is merged
mf envs delete env_01J8C4RX --wait

Promoting a staging environment to production is mf envs push:

Push staging to production
mf envs push env_01J8C4RX --mode full --follow

See environment lifecycle for how creation, refresh, and teardown work, and promote & rollback for what push does under the hood.

mf envs exec runs a WP-CLI command inside an environment and streams the output — the same surface as an SSH shell, but driven by the API and gated by the wp.cli:exec scope:

One-off exec
mf envs exec env_01J8C4RX -- wp plugin list --status=active
A dry-run search-replace before promoting
mf envs exec env_01J8C4RX -- \
wp search-replace 'staging.example.com' 'example.com' --dry-run

Logs come from the observability endpoints. There is no live tail -f-style stream — you query a window and re-run (or script it):

Recent logs for a site
mf insights logs --hours 2

mf insights logs is per-site (capped at 200 lines per query); mf obs logs is the account-wide view with more filters:

Account-wide errors in the last hour
mf obs logs --hours 1 --severity error --limit 100

mf listen polls your account’s events feed and forwards each new event to a local URL, so you can develop a webhook handler without exposing a public endpoint. It baselines on startup and forwards only events that happen after it — no history replay:

Forward events to a local server
mf listen --forward-to http://localhost:4000/webhooks
Only forward one event type
mf listen --type deploy.completed --forward-to http://localhost:4000/webhooks

Every command accepts --json (anywhere in the argv) and prints machine-stable output: 2-space-indented JSON, with empty lists normalized to []. Errors come back as the full API error envelopetype, code, message, doc_url, request_id, and retry_after_s where relevant — instead of a formatted message.

Exit codes are a contract you can branch on:

Code Meaning
0 Success
1 Runtime or API error (including a failed job under --wait/--follow)
2 Usage error — bad flags or arguments
130 Interrupted (Ctrl-C)
Script a check against the JSON output
mf jobs list --status failed --json | jq -r '.[].id'

Two more surfaces ship inside the same binary:

  • mf fn — local development for hosted functions: mf fn setup installs the builder images, and mf fn dev builds, serves, and watch-rebuilds a function on your machine before you deploy it with mf functions.
  • mf mcp — the official MCP server over stdio, so an AI agent can drive the API with the same scoped, job-returning surface you do. Register it with claude mcp add forge -- mf mcp.