givemeanode • docsdocs | login | signup

Docs

givemeanode is on-demand GPU nodes and batch GPU jobs for AI agents, over MCP. Your agent gets a container on an H100 with a persistent encrypted disk, billed per minute. Stop it and it parks: files intact, billing stopped. The next command wakes it.

Quick start

Add the MCP server (Claude Code shown; other clients below):

claude mcp add --transport http givemeanode https://mcp.givemeanode.com

Your client opens a browser window once to sign in. Then ask your agent for a GPU:

Get me a GPU node, clone github.com/you/yourrepo on it, and run the benchmark.

That's the whole product. Behind the scenes the agent makes three tool calls:

create_node(gpu_type: "h100")       → {name: "tulip", status: "provisioning", rate: "$/min"}
run_command("tulip", "nvidia-smi")  → {exit_code: 0, stdout: "..."}
stop_node("tulip")                  → parked: files intact, billing stopped

Billing starts when the node is ready and stops when you stop it, at the rate in the create_node response. The next run_command against a stopped node wakes it with the disk exactly as you left it.

Installation

The server is the same for every client — streamable HTTP with OAuth 2.1 at:

https://mcp.givemeanode.com

Clients discover auth automatically; a human approves once in the browser. No API keys.

Claude on the web

Go to Settings → Connectors → Add custom connector and enter the remote MCP server URL:

https://mcp.givemeanode.com

Claude Code (CLI)

claude mcp add --transport http givemeanode https://mcp.givemeanode.com

Then approve the connection when Claude first uses it (/mcp shows status).

OpenAI Codex

codex mcp add givemeanode --url https://mcp.givemeanode.com

Or in ~/.codex/config.toml:

[mcp_servers.givemeanode]
url = "https://mcp.givemeanode.com"

Run codex mcp login givemeanode if Codex doesn't prompt for OAuth on first use.

opencode

In opencode.json (project or global):

{
  "mcp": {
    "givemeanode": {
      "type": "remote",
      "url": "https://mcp.givemeanode.com",
      "enabled": true
    }
  }
}

Nodes

A node is a container on an H100 with a persistent disk. gpu_type is h100 for one GPU or 8xh100 for a full 8-GPU machine.

create_node(name: "tulip", gpu_type: "h100")

create_node never blocks. It returns one of:

The lifecycle:

stop_node("tulip")     # stop paying now; disk parks, files intact
run_command("tulip", "ls")   # wakes a stopped node automatically
delete_node("tulip")   # destroy the disk (crypto-erase, irreversible);
                       # stopped nodes only — stop_node first

list_nodes() shows everything you have, get_node(name) shows one node's state, rate, and queue position. It's the poll target while a node is queued, provisioning, or waking.

Reproducible benchmarks: pass clock_lock: true at create. The platform pins the GPU clock (free, immutable once set) so timings compare across runs.

Running commands

There is no SSH. Commands run over MCP in the node's container, and logs are the interface:

run_command("tulip", "git clone https://github.com/you/yourrepo && cd yourrepo && pip install -e .")
→ {exit_code: 0, stdout: "...", stderr: "...", truncated: false}

Secrets go in env, never the command string — command lines are recorded, env values aren't:

run_command("tulip", "huggingface-cli download meta-llama/Llama-3.1-8B",
            env: {HF_TOKEN: "hf_..."})

Use short-lived, minimally scoped tokens and treat them as burned.

Long-running commands

Anything that outlives a tool-call timeout (training, a server) runs detached:

run_command("tulip", "python train.py", detach: true)
→ {command_id: "cmd_...", log_path: "..."}   # returns immediately

get_command("tulip", "cmd_...")              # status + incremental output
get_command("tulip", "cmd_...", offset: 4096) # just the new bytes
list_commands("tulip")                       # all detached commands, newest first
kill_command("tulip", "cmd_...")             # TERM, then KILL after 10s

Files survive stop/wake; processes don't — exactly like tmux on a machine you can power off. get_command never wakes a stopped node, so checking on finished work is free.

Files in and out

Small files in (configs, entry scripts — not secrets, not datasets):

write_file("tulip", "/root/run.sh", "#!/bin/sh\npython train.py\n", executable: true)

Large inputs: pull them from inside the node with run_commandcurl, git, huggingface-cli. The node has internet access.

Results out: export_file returns a presigned download URL; a directory arrives as a tar.

export_file("tulip", "/root/yourrepo/checkpoints")
→ {url: "https://...", expires_at: "..."}

Exposing ports

Put a port on a running node at a public HTTPS URL — TensorBoard, Jupyter, a vLLM endpoint serving a model to a judge:

run_command("tulip", "python -m vllm.entrypoints.openai.api_server --port 8000", detach: true)
expose_port("tulip", port: 8000)
→ {url: "https://<endpoint-id>.<edge>", expires_at: "..."}

Anyone holding the URL reaches the port — the URL is the secret, so treat it like one. auth: "bearer" additionally requires an Authorization: Bearer token at the edge (the token rides the response once and is never shown again).

Endpoints are bound to the node's current container: a stop kills them, and a wake never resurrects them — re-call expose_port for a fresh URL. Traffic through the endpoint is free and does not keep the node awake; the server process (a detached command) is what holds the node running. list_endpoints(node) shows what's live, unexpose_port(node, port) takes it down.

Batch jobs

Nodes are interactive; jobs are fire-and-forget. submit_job builds your image, queues fairly against your org's share, runs your command, and bills only the running attempt — queued and preempted time is free:

submit_job(
  gpu_model: "h100",
  command: "python eval.py",
  context: {"Dockerfile": "FROM pytorch/pytorch\nCOPY eval.py .", "eval.py": "..."},
  idempotency_key: "eval-2026-07-25")
→ {job_id: "job_...", rate: "$/min", cost_ceiling: "..."}

Or skip the build with a prebuilt image ref instead of context. idempotency_key makes resubmission safe: the same key returns the same job.

Inside the container, declare results instead of grepping logs: write your verdict as JSON to $GMN_RESULT_PATH and put files worth keeping in $GMN_OUTPUT_DIR. Both come back on get_job when the attempt completes — the result inline, the output dir as a download URL.

get_job("job_...")                   # status; add stream:"run" for logs
list_jobs(label: "lr-sweep", summary: true)  # rollup with every declared result
cancel_job("job_...")                # immediate; queued/building stops free

Sweeps

Up to 256 variants of one job in one call — one build, N runs:

submit_jobs(
  shared: {gpu_model: "h100", context: {...}},
  variants: [{env: {LR: "1e-4"}}, {env: {LR: "3e-4"}}, {env: {LR: "1e-3"}}],
  label: "lr-sweep",
  idempotency_key: "lr-sweep-1")

Validation is all-or-nothing; list_jobs(label: ..., summary: true) is the comparison table when they finish.

Large build contexts

Inline contexts cap at 4 MiB. Past that, presign an upload:

create_context(sha256: "...", size_bytes: 123456789)  → upload URL
# PUT the tar to the URL, then:
finalize_context(context_id)
submit_job(gpu_model: "h100", command: "...", context_id: "...", idempotency_key: "...")

From inside a node

Code running on a node can introspect and stop itself with no credential — identity is placement. Fixed address http://169.254.42.1 (also metadata.givemeanode.internal); every request needs the header Metadata-Flavor: givemeanode:

curl -s -H 'Metadata-Flavor: givemeanode' http://169.254.42.1/v1/node
curl -s -H 'Metadata-Flavor: givemeanode' http://169.254.42.1/v1/events
curl -s -X POST -H 'Metadata-Flavor: givemeanode' http://169.254.42.1/v1/stop

/v1/node is identity and money (name, state, rate, grace window); /v1/events carries structured notices with deadlines (machine retirement, spend-cap warnings) — a checkpoint-aware training script polls it like cloud spot metadata. POST /v1/stop ends the node from inside with stop_node semantics: a training run's last line can be that curl.

Teams and organizations

Every account is an org of one until you invite someone:

invite_member(email: "student@lab.edu", role: "member")

Roles: admin (everything), billing (money pages), member (their own nodes and usage). The same management lives in the browser at /team — roster, invites, role changes, workspaces with spend caps, and a stop button for any org node (the runaway-student-node case, also stop_org_node over MCP).

Members of a workspace share its nodes. switch_active_org(org) picks where your next node bills if you belong to several.

Usage and billing

Per-minute rates, listed on the front page. A node bills while running or idling in its grace window; stopped nodes cost nothing. The rate in every create_node/get_node response is the rate you pay.

get_usage()               # this month's spend per node
get_usage(month: "2026-06")
get_billing()             # dunning state, MTD spend, caps

In the browser: /usage is the rendered ledger (per-workspace, per-member, per-node; CSV export at /usage.csv), and /billing is the Stripe portal — invoices, receipts, payment method.

Referrals: claim a code at /referrals (or claim_referral_code over MCP); activations and credits earned are listed there and on list_referrals().

Scopes

Authorizing the server is a consent checklist (OAuth scopes); the tool list a connection sees is exactly its grant. Each scope comes in two strengths: the bare word grants read and write, a :read suffix grants the read-only slice.

A token that names no scopes acts with your full authority. Give a CI agent infra and nothing else; give a finance script org:read.

Getting help

© 2026 givemeanodestatus · billing · contact · terms