◆ FORGE · MCP
The Forge MCP server exposes every Forge operation as a tool your agent can call
directly — register a dataset, run augmentation, fine-tune a world model or policy on your
robot, read its gate receipts, deploy it to a serving endpoint and drive it, check
credits, pull the trained weights — without ever opening
forge.alakazam.gg. It’s a thin, stateless proxy over the
same /api/workbench, /api/aug, and /api/billing surfaces the web app uses; your
bearer is forwarded verbatim and Forge re-verifies every call, so an agent can only
touch your own campaigns.
One server, the whole Forge
71
forge_* tools across eight areas — account, datasets & embodiment, sources,
augmentation, world-model fine-tuning, evaluation & serving, monitoring &
diagnostics, and the playground — behind one Streamable-HTTP endpoint. The loop
closes: a dataset goes in, a served model endpoint comes out.Connect
The server speaks MCP over Streamable HTTP at/mcp. Point your agent at it and
supply your Forge credential (see Authenticate).
- Claude Code
- Cursor / mcp.json
forge_authenticate first (below) — every other tool accepts the returned token as an
optional token argument.
The terminal client
The MCP also serves a small CLI, version-locked to the deployed tools — nothing separate to install or keep updated:forge_upload_dataset returns the exact command to
run. Output is a rendered panel in a terminal and plain text when piped, so it is safe to
capture in logs. Add --json to any command for raw data.
Authenticate
Not sure which method to use? Callforge_login — it returns a short menu to show the
user (email link · API key · password) and routes to the right tool, the way a CLI would.
Everything below is what it routes to.
Forge is owner-scoped by a Supabase JWT or a forge_sk_ API key — both go in the same
Authorization: Bearer header and every endpoint accepts either. A headless agent gets one
three ways:
forge_send_login_code(email)→forge_verify_login_link(url)— passwordless. An email arrives with a “Log in” button. Copy the link address (right-click → Copy link address; do not click it — the link is single-use) and pass the whole URL toforge_verify_login_link. It returns{ access_token, refresh_token, expires_in }. If your project’s email template carries a 6-digit code instead, useforge_verify_login_code(email, code)— same result.forge_authenticate(email, password)→ same{ access_token, … }, for accounts that actually have a password.- Connection header — set
Authorization: Bearer <jwt>on the MCP connection (above); every tool inherits it, notokenargument needed.
forge_refresh_token(refresh_token) before expires_in elapses (~1h).
forge_whoami confirms who a token is; forge_get_credits is your affordability
preflight before any paid render.
No account yet?
The MCP deliberately cannot create one. A login tool that mints accounts turns a typo’d address into a real user, soforge_send_login_code sends to existing accounts only. An
unknown address comes back with reason: "no_account" and no email is sent.
Sign up once at forge.alakazam.gg — email + password, Google,
or Apple all work — then come back and call forge_send_login_code with that same address.
New accounts start with a small credit grant; forge_get_credits shows it.
If you signed up with Google or Apple, you have no password — use the passwordless code
flow above.
forge_authenticate cannot work for those accounts, by construction.Keeping the token
The server is stateless — it stores nothing, including your session. Where the token lives is your client’s job:- Per-call — pass
token: "<access_token>"to each tool. Simple, but the token lives only in the agent’s context: a fresh session (or a context compaction that drops it) means logging in again. - On the connection — put it in the
Authorization: Bearerheader (see Connect). Survives across sessions until it expires; every tool inherits it with notokenargument.
reason: "rate_limited" rather than an opaque error.
API keys — the credential for anything unattended
A login token expires in about an hour, and renewing it for an OAuth account means another email. That is fine for a person and wrong for an MCP client that reconnects, a CI job, or a partner integration. Log in once, mint a key, use the key forever:forge_list_api_keys shows every key masked, with last_used_at so you can spot ones
nothing uses any more. forge_revoke_api_key kills one permanently; an already-running
server may honour it for up to a minute while its auth cache expires. Twenty keys per
account.
The tools
Account, keys & credits (16)
Account, keys & credits (16)
Datasets & embodiment (24)
Datasets & embodiment (24)
Augmentation (14)
Augmentation (14)
World-model fine-tuning (4)
World-model fine-tuning (4)
Evaluate & serve (5)
Evaluate & serve (5)
Monitor & diagnose (2)
Monitor & diagnose (2)
Playground (5)
Playground (5)
Import from anywhere
Your dataset does not have to live on Hugging Face, and it does not have to be uploaded chunk by chunk.forge_import_dataset takes a source_uri in any of these schemes,
materializes the bytes server-side, and gives you a normal {campaign_id} that every other
tool already understands.
The import is asynchronous: the call returns {campaign_id} immediately, with
health_status "importing" while the server fetches the bytes. Poll
forge_get_campaign until it leaves "importing" — ready means the dataset is
mineable, failed means the fetch did not complete and health_error says why. Pass a
stable idempotency_key so a retried create returns the same campaign instead of
importing the dataset a second time.
A private
gs:// path needs a stored gcs credential or a signed URL — a credential-less
gs:// import falls back to the server’s own identity and is therefore allowed only for
buckets an operator has explicitly allowlisted.
The same schemes work for a fine-tune: pass the URI as dataset_ref to
forge_create_world_model, with credential_id alongside it when the source is private
and you could not presign.
Credential flow
When presigning isn’t an option, keep a credential on the account and pass it by reference:forge_store_source_credential(provider, name, payload)→{id, provider, name, created_at}.payloadholds the provider fields —s3:access_key_id,secret_access_key, optionalregion/endpoint_url/session_token;gcs:service_account_json;azure:account_name+sas_tokenoraccount_key;hf:hf_token.forge_import_dataset(source_uri, credential_id=<id>)— the server decrypts the credential for that one fetch.forge_delete_source_credential(credential_id)when the import is done.
forge_list_source_credentials returns only {id, provider, name, created_at}, so a lost
secret is re-stored, not recovered. Give each one the narrowest read-only scope that works.
Imports are guarded: https fetches are SSRF-checked (private, loopback, link-local, and
metadata address ranges are refused, redirects capped and re-checked), archives unpack
traversal-safe, and both byte and file counts are capped server-side (defaults 20 GB /
20 000 files). Failures answer 400 unsupported source scheme: <scheme> or
400 source fetch failed: <why>; an unknown credential_id answers 404 credential not found, and a server with no credential-store key answers 503 credential store not configured.
Long-running work & spend
Runs and fine-tunes are asynchronous. The create tool returns immediately; pollforge_get_campaign (and forge_get_job for augmentation renders) until a stage is
terminal, then fetch the artifact. For a cross-job view, forge_list_activity returns
everything you own that is queued or running, with measured ETAs (never guessed — a job
with no observed rate reports eta: null and says why).
Stuck vs. slow. Read all_healthy and needs_attention before saying anything is
wrong: a job is a problem only when its row carries a diagnosis. Every row also carries
stale_s (seconds since it last reported), but staleness alone is not a verdict — the
server compares it against that job’s known heartbeat cadence (~60s for a training box)
and writes the conclusion into diagnosis. For fine-tunes, forge_diagnose_finetune returns the full
forensics: a verdict (healthy, late_heartbeat — usually a long checkpoint save,
zombie_suspected — runner dead with the box still up and billing), the job’s state
history, and the training log’s tail with error lines pre-extracted. Zombie boxes are also
auto-remediated server-side: a train job silent for 45+ minutes is failed and its box
deleted; forge_cancel_world_model frees the training slot immediately if you’d rather
not wait.
Guardrails, so a looping agent can’t run away with your wallet or GPUs:
- Idempotency — pass an
idempotency_keyonforge_start_run,forge_start_playground_op, andforge_create_world_model; a retry with the same key returns the original result instead of billing again. - Caps — a per-owner ceiling bounds concurrent world-model jobs (count and summed
$budget); over it, creation is refused. Budget/steps are clamped server-side. - Cancel —
forge_cancel_world_modelstops a fine-tune and its billable box;forge_cancel_jobstops an augmentation run. - Queueing — training capacity is a shared fleet. A waiting job’s campaign note reads
waiting for a training slot — position N of M (K training); that is normal motion, not a hang, and the queue advances whenever a box finishes. The first minutes after create may readcreated via forge workbench— the scheduler simply has not ticked yet. - Out of credits — a billable call with an empty wallet fails with HTTP
402and a bodyinsufficient_credits:need=N:checkout=/api/billing/credits/checkout:packs=/api/billing/credits/packs. To resolve it headlessly:forge_list_credit_packs→ pick a pack →forge_credits_checkout→ open the returned Stripeurl→ re-checkforge_get_credits→ retry. No web UI required.
Serve the trained model
The pipeline does not end at a weights archive. Once a fine-tune passes export:forge_deploy_model(id)— free: writes a deployment record and returns awam_sk_deployment token exactly once (the server keeps only a hash). No GPU runs yet.forge_run_rollout(deployment_id, deployment_token, frames|video_b64)— the first call cold-boots the model (minutes: weights + load); calls within the warm window return in seconds. For awampolicy you get the denoised action chunk —chunk × action_dimabsolute targets, ready to drive the robot.forge_undeploy_model(deployment_id)— revokes the token; idle containers drain on their own either way.
*_preview, *_video, *_clip, forge_download_export,
forge_download_weights) never stream multi-MB payloads through the model — they return
a URL (and, for weights, a short-lived signed URL) you fetch directly.
