Skip to main content
Are you a large language model? This page is available as raw markdown at /mcp.md. The full docset is at /llms-full.md and the index is at /llms.md.

MCP server

C3 hosts a Model Context Protocol (MCP) server so coding agents can use C3 directly. An agent connected to it can list hardware and prices, deploy a job to a chosen GPU class or provider, wait for it, read its logs, and collect its results. Nothing needs to be installed on the machine running the agent.

The MCP server is a third thin client next to the CLI and the web dashboard. Every tool mirrors one c3 command, calls the same API the CLI calls with the same credential, and returns cli_equivalent, the exact c3 invocation that reproduces what it did. If you can do it with c3, an agent can do it through MCP; if c3 refuses, so does the tool, with the same error.

Endpoint

EnvironmentURL
Productionhttps://api.cthree.cloud/mcp
Staging previewhttps://test.api.cthree.cloud/mcp

The setup examples below use the production URL; substitute the staging URL to try a change before it reaches production. The transport is Streamable HTTP. Every tool call is a self-contained request; there are no sessions to keep alive.

Two ways to authenticate

URL only (OAuth). Give your host just the URL. On first use it receives a login challenge, discovers C3's Auth0 tenant from the endpoint's metadata, registers itself, and opens a browser login. Tokens refresh automatically. Use this on your own machine.

API key (header). Create a key with c3 apikey create <name> or on the dashboard settings page and send it as Authorization: Bearer c3_key_.... No browser is involved. Use this for headless agents, CI, or hosts that cannot run a browser. A configured key never triggers a login; a revoked key returns a 403 so you see the problem instead of a login prompt.

Both paths give the agent exactly the access the same credential has in the CLI, including the verified-email requirement and any account restriction.

Host setup

Verification status is recorded after each host is exercised against staging.

HostURL-only (OAuth)API-key headerVerified
Claude CodeyesyesAPI-key header, 2 Sep 2026 (OAuth not yet)
claude.ai and Claude Desktopyesno (connector settings have no header field)not yet
Cursoryesyesnot yet
ChatGPTyesnonot yet
Codexyesyesnot yet
VS Codeyesyesnot yet

Claude Code

URL only:

claude mcp add --transport http c3 https://api.cthree.cloud/mcp
# then, inside a session, run /mcp and choose "Authenticate" for c3

API key:

claude mcp add --transport http c3 https://api.cthree.cloud/mcp \
--header "Authorization: Bearer c3_key_..."

Headless use works with the API-key form, for example claude -p "List the hardware C3 offers and deploy run.sh on an l40".

claude.ai and Claude Desktop

Open Settings → Connectors → Add custom connector, enter https://api.cthree.cloud/mcp, and complete the login when prompted. Connectors use OAuth only.

Cursor

Add to .cursor/mcp.json (project) or ~/.cursor/mcp.json (global). Omit headers to use OAuth instead of a key.

{
"mcpServers": {
"c3": {
"url": "https://api.cthree.cloud/mcp",
"headers": { "Authorization": "Bearer c3_key_..." }
}
}
}

ChatGPT

Enable developer mode under Settings → Connectors, choose Create, and enter https://api.cthree.cloud/mcp. ChatGPT connectors use OAuth only.

Codex

Add to ~/.codex/config.toml. For OAuth omit bearer_token_env_var and run codex mcp login c3.

[mcp_servers.c3]
url = "https://api.cthree.cloud/mcp"
bearer_token_env_var = "C3_API_KEY"

Set C3_API_KEY in the environment Codex runs in.

VS Code

Add to .vscode/mcp.json. Delete the headers block to use OAuth.

{
"inputs": [
{ "id": "c3-key", "type": "promptString", "description": "C3 API key", "password": true }
],
"servers": {
"c3": {
"type": "http",
"url": "https://api.cthree.cloud/mcp",
"headers": { "Authorization": "Bearer ${input:c3-key}" }
}
}
}

Tools

Each tool description begins with the c3 command it mirrors. Results always include cli_equivalent. Money-moving commands (c3 topup, c3 upgrade), API-key management, and operator commands are deliberately not exposed; when a job needs credits the error names c3 topup for the user to run.

ToolMirrorsWhat it doesInputs (? = optional)
whoamic3 whoami --jsonShow the identity behind the configured credential: user id, email, organisation, permissions, admin flag and C3 access status.none
list_hardwarec3 list --json [--class <class>] [--provider <id>] [--all]List the hardware classes C3 can run jobs on with C3-billed prices and a coarse live availability signal.class?, provider?, all?
preview_routec3 deploy --dry-run --json [-p <provider>]Show how a deploy with these selectors would route (provider, region, hardware profile, hourly rate, availability) without creating a job or reserving credit.hardware?, gpu?, provider?, regions?, walltime_seconds?, capacity?, project?
balancec3 balanceShow the credit balance, subscription plan and storage usage for the account.none
list_jobsc3 squeue --json [-n <history>]List jobs the way c3 squeue does: every active job (PENDING, SCHEDULING, RUNNING) followed by the most recent terminal jobs (history of them, default 10).status?, history?, limit?, offset?, hardware_profile?
get_jobc3 squeue <job-id> --jsonShow one job in full: status, selected route (provider, region, hardware profile, rate), capacity policy, failure diagnosis and the latest attempt.job_id
job_logsc3 logs <job-id> [-v]Return one page of log lines for the job's latest attempt with a cursor for the next page.job_id, cursor?, limit?, all_streams?
wait_for_jobc3 logs <job-id> -fPoll a job every 2 seconds until it reaches a terminal state (SUCCEEDED, FAILED, CANCELED, TIMED_OUT) or the timeout elapses (default 30s, maximum 50s).job_id, timeout_seconds?
cancel_jobc3 cancel <job-id>Request cancellation of a job.job_id
deployc3 deploy [script] --json [-p <provider>]Submit a job.hardware?, gpu?, provider?, regions?, walltime_seconds?, capacity?, project, script?, files?, workspace?, job_name?, datasets?, output?, python_project_dir?, docker_image?, docker_requires_accelerator?
list_artifactsc3 pull <job-id> --jsonList the files a job produced with sizes and short-lived download URLs (about one hour).job_id
read_artifactc3 pull <job-id>Return one artifact's content inline (text, or base64 for binary) when it is at most 1 MiB; larger files return the size and a download URL only.job_id, path
data_lsc3 data ls <path>List datasets, a dataset's versions, the files in a version, uploaded workspace versions, or a job's artifacts.path
data_duc3 data du <path>Report storage used by datasets: total bytes across versions, unique bytes after content-addressed dedup, file and version counts.path?
data_rmc3 data rm [-r] [-n] -f <path>Remove a dataset version or a whole dataset with recursive, cleaning up empty datasets and repointing latest to the newest survivor.path, recursive?, dry_run?
data_uploadc3 data cp <local-dir> /datasets/<name>/Create a new version of a dataset from inline files (at most 20 MiB decoded and 500 files; for anything larger, hosts with a shell use prepare_upload and hosts with the CLI use c3 data cp).dataset, files
prepare_uploadc3 data cp <local-dir> <dest> (upload phase; c3 deploy for a workspace)For hosts that can run a shell but not the CLI.target, files, script?, python_project_dir?, docker_image?, docker_requires_accelerator?, output?

Two read-only resources are also served in-band: c3://docs/mcp (this page) and c3://docs/cli-reference (the reference printed by c3 docs).

Workspaces are data

Workspace file selection happens on the client host. The hosted MCP server cannot read your local .c3ignore, .gitignore, or .c3 ignore_file setting. Apply those rules before sending file contents or digests to a tool. If the host has the CLI, c3 deploy --list-files --json returns the selected paths locally without uploading or authenticating. Otherwise, explicitly select the files needed by the job. The built-in workspace exclusions still apply to submitted lists. See Workspace upload exclusions for comment syntax, imports, and .env examples.

The CLI automatically removes api_key from uploaded .c3 copies. Hosted MCP uploads do not re-encode config files. Inline workspace .c3 files must be valid YAML without a top-level api_key (including keys supplied by YAML merges); otherwise the tool rejects the request before storage calls. Digest-only prepare_upload cannot inspect or redact file contents. Omit .c3 files from MCP workspaces when they are unnecessary, or prepare credential-free copies before hashing or sending them. Hash and upload the same prepared bytes; --list-files only lists paths and does not produce redacted copies.

A workspace is the code and configuration a job needs: the submission script, a .c3, a pyproject.toml, your source files. C3 stores it the way it stores a dataset, as content-addressed blobs plus a manifest, and c3 deploy is that upload followed by a job that points at the manifest hash. Data belongs in datasets (mounted with datasets:), environments are rebuilt from pyproject.toml or a Docker image, and caches are excluded. Uploaded workspace versions are visible with data_ls /projects/<project>/workspace/.

There are two ways to get a workspace to C3 from an agent, chosen by what the host can do:

Hosts with a shell (Claude Code, Cursor, Codex, VS Code)

Never paste file contents into a tool call. Hash the files, send the digests, run the returned script, deploy the returned version:

# 1. digests only (paths, sha256, size); the model never sees the bytes
find . -type f -not -path './.git/*' -exec sh -c 'printf "%s %s %s\n" "$(sha256sum "$1" | cut -d" " -f1)" "$(stat -c %s "$1")" "${1#./}"' _ {} \;

The agent calls prepare_upload with target: /projects/<project>/workspace/, the digests and script: run.sh. The result has presigned PUT URLs for only the files storage does not already hold, the manifest, and a shell field that is a complete bash script of curl PUTs (no credential in it). The agent runs that script, then calls deploy with workspace: /projects/<project>/workspace/@<hash>. Lock files, large sources and anything up to 5 GiB per file go this way. Datasets use the same flow with target: /datasets/<name>/.

Hosts without a shell (claude.ai, ChatGPT)

These hosts only have the small scripts the agent wrote, so deploy and data_upload accept the files inline: a list of { path, content } entries, encoding: "base64" for binary files, executable: true for scripts. The standard exclusions apply. The limit is what a Worker request can hold:

LimitValue
Decoded inline payload20 MiB
Inline files500
Files per prepare_upload call2000, up to 5 GiB each
Inline artifact read1 MiB (larger files return a download URL)

Whichever path is used, the workspace manifest hash is identical to what c3 deploy produces for the same prepared file bytes and metadata, including credential-free .c3 copies.

Worked examples

Run a script on an H100 on a named provider

Ask the agent to preview first, then deploy:

Preview an h100 route on nebius, then deploy train.py from this directory with run.sh as the script, project matmul, and wait for it.

The agent calls preview_route with hardware: "h100", provider: "nebius", then deploy with the files, script: "run.sh", project: "matmul", the same selectors, and finally wait_for_job. The deploy result carries the CLI form, for example:

cli_equivalent: c3 deploy run.sh --json -p nebius
c3_config:
project: matmul
script: run.sh
hardware: h100
provider: nebius

Deploy a real repository from Claude Code without the CLI

Hash this directory, prepare the upload for project matmul, run the upload script, then deploy the returned workspace on an h100 and wait for it.

The agent runs the find/sha256sum one-liner above, calls prepare_upload, runs the returned script with bash, and calls deploy with workspace: /projects/matmul/workspace/@<hash>. Only files not already in storage are uploaded, and the lock file travels as bytes over HTTPS, never through the model.

Follow a job and read its results

wait_for_job returns after at most 50 seconds (under the tool timeout hosts enforce) with terminal: false if the job is still running; the agent calls it again. job_logs returns a page of lines and a cursor; passing the cursor back returns only new lines. When the job succeeds, list_artifacts lists the output files with download URLs and read_artifact returns a small result file (a metrics JSON, say) inline.

Upload a dataset and mount it

data_upload with dataset: "my-data" and a few files creates a new version. A later deploy mounts it with datasets: [{ ref: "/datasets/my-data", mount: "/data" }], exactly as datasets: in .c3 would.

Troubleshooting

SymptomMeaningFix
Host opens a browser login you did not expectNo credential was sent. This is the OAuth path working as designed.Complete the login, or configure an API key header if you want no browser.
403 Invalid or revoked API keyThe c3_key_ header is wrong or the key was revoked.Create a new key with c3 apikey create <name> and update the host.
EMAIL_VERIFICATION_REQUIREDThe account's email is not verified.Run c3 verify-email, click the link, retry.
INSUFFICIENT_CREDITSThe balance cannot cover the job's reservation.Run c3 topup <amount> or top up on the dashboard. The agent cannot do this for you.
GPU_OUT_OF_STOCK or PROVIDER_UNAVAILABLEThe requested class or pinned provider has no capacity.Call list_hardware, choose another class or provider, or set capacity.on_unavailable: "wait".
CONCURRENCY_LIMITYour plan's concurrent job limit is reached.Wait, cancel_job, or c3 upgrade.
429 RATE_LIMITEDMore than 120 tool calls in a minute from one identity.Slow the loop; retry after the Retry-After interval.
WORKSPACE_TOO_LARGEInline payload over 20 MiB or 500 files.With a shell, use prepare_upload; with the CLI, run c3 deploy from the project directory.

Tokens issued by the OAuth login expire; hosts holding a refresh token renew silently. If a host shows 401 invalid_token and does not recover, remove and re-add the server to trigger a fresh login.