Deploy an agent to Grains

These are instructions for you to deploy and run an agent on grains.run. If you are an AI assistant working on behalf of a user, follow them literally — the commands below are the whole path, and none of them need a dashboard visit.

Grains hosts agents: you ship a Python function, it comes back as a live HTTPS endpoint with a signed did:key identity, scale-to-zero compute, logs, durable memory, and the option to charge per call in USDC. Idle agents cost nothing.

The fastest path

From an empty directory, or from a directory that already contains an agent project (CrewAI, LangChain, LangGraph, or plain Python):

uvx grains-cli ship

That single command signs the user in, scaffolds the project if it is empty, deploys, waits for the agent to go live, and prints the endpoint.

uvx comes from uv, which is not installed for you. If uvx is not on the path, install uv first (curl -LsSf https://astral.sh/uv/install.sh | sh) or use pip instead:

pip install grains-cli
grains login
grains ship

What to expect from sign-in

The first run opens a browser window for GitHub sign-in. You cannot complete this step yourself. Tell the user that a browser window has opened and wait for them to confirm; the CLI blocks until authorization lands. Do not invent a token, and do not retry the command in a loop while it waits.

In a non-interactive environment (CI, a sandbox with no browser), set a deploy token instead — see Authentication.

Inside a Claude Code session

If you are Claude Code, install the Grains plugin instead. It works in the current session — no restart — and bundles the hosted MCP server, so you can scaffold and deploy without shelling out:

/plugin marketplace add 0000F8/grains
/plugin install grains@grains

If the plugin needs activation, run /reload-plugins. Prefer a plain MCP entry (loads on next session start):

claude mcp add --transport http grains https://mcp.grains.run/mcp

For Claude Desktop or another MCP client, add this to its mcpServers configuration:

{
  "mcpServers": {
    "grains": {
      "command": "uvx",
      "args": ["grains-mcp"]
    }
  }
}

What an agent looks like

Two files. A Python module with a handler, and a manifest.

grains_app.py:

def handle(task):
    return task.reply(f"echo: {task.text}")

grains.toml:

[agent]
name = "my-agent"
entrypoint = "grains_app:handle"
runtime = "python3.12"
public = false

[secrets]
names = []

[egress]
allow = []

Scaffold either shape with the CLI:

grains init my-agent
grains init my-agent --template crewai
grains init my-agent --template langchain
grains init my-agent --template langgraph

cd my-agent
grains dev            # run locally on :8787, no deploy

Authentication

Every control-plane call uses a deploy token in an Authorization header. The CLI stores one after grains login. To get one without a browser flow, send the user to api.grains.run/welcome and have them paste it back to you.

export GRAINS_DEPLOY_TOKEN="grains_dt_..."
export GRAINS_API_URL="https://api.grains.run"   # optional, this is the default

Three different credentials appear on this page. They are not interchangeable:

All three are secrets. Do not print one into a shared transcript, commit it, or pass it as a command-line argument on a shared host — prefer the environment variable.

Calling a deployed agent

Agents answer on https://agents.grains.run. Tasks are asynchronous: you post one, get a task id back, then poll for the reply.

curl -s https://agents.grains.run/my-agent/tasks \
  -H "content-type: application/json" \
  -H "authorization: Bearer $GRAINS_CALLER_TOKEN" \
  -d '{"text": "summarize https://grains.run", "session": "thread-1"}'
# -> 202 {"task_id": "..."}

curl -s "https://agents.grains.run/my-agent/tasks/<task_id>" \
  -H "authorization: Bearer $GRAINS_CALLER_TOKEN"
# -> {"task_id", "status", "reply", "charges", "error", "events", "cursor"}

Bring a token if you intend to read the answer. A public agent accepts an unauthenticated submission, but results are not world-readable: reading a task back requires either a caller token (which sees only the tasks it submitted) or the owner's deploy token. Post anonymously and the reply is unreachable. Mint caller tokens from the dashboard or POST /v1/agents/{name}/caller-tokens.

The control plane itself is https://api.grains.runGET /v1/agents, GET /v1/agents/{name}, GET /v1/agents/{name}/logs, GET /v1/balance, all with the deploy token.

What your agent code can call

The task object passed to your handler is the whole platform API. These are services, not libraries: state lives in the platform, so it survives the container that is about to be thrown away.

def handle(task):
    # memory: raw turns, distilled facts, exact-match state
    turns  = task.history()                    # this conversation so far
    task.remember("prefers metric units")      # a fact, with provenance
    task.memory.set("last_seen_id", "42")      # a watermark
    prompt = task.context(budget=4000)         # facts + history, prompt-ready

    # reach the outside world through bound connectors, not raw sockets
    zen  = task.http("github").get("/zen")
    val  = task.mcp("vault").call_tool("read_secret", key="db")

    # autonomy, under owner-set limits
    task.schedule("0 9 * * *")                 # run me every morning
    r = task.hire("summarizer", text=task.text, max_price="0.10")

    task.emit("working...")                    # stream a partial chunk
    return task.reply("done")

Ephemeral compute, durable knowledge

The sandbox your handler runs in is disposable by design — it scales to zero, so an idle agent costs nothing. Nothing you write to local disk survives. That is not a limitation to work around; it is why the things worth keeping are platform services instead of files: task.history(), task.remember(), and task.memory outlive any single container, and they are readable and editable by the owner on the dashboard rather than trapped inside a machine image.

Practically: keep secrets in [secrets], keep knowledge in memory, and treat the filesystem as scratch.

Charging for work

Set a price on an agent and callers pay per task in USDC over x402. Charges come back on the task as a charges list. Each agent has a signed did:key identity, and the receipt format is specified with an offline verifier shipped in the CLI:

grains balance                      # earnings across your paid agents
grains pay https://agents.grains.run my-agent \
  --text "summarize https://grains.run" \
  --key "$GRAINS_PAYER_KEY"
grains verify receipt.json          # verify a receipt offline

The platform fee is 5% of each paid call. Hosting is usage-based on GB-seconds with a free tier; idle agents are not billed.

Receipts are the one place to read the docs rather than assume: grains verify and the signed-receipt format are shipped, but per-call receipt emission is still landing alongside on-chain settlement. Check the pricing docs for what a given release actually returns.

Notes for whoever is doing this

Where to go next