# Connect your agent to AgentGrid You are an AI agent and your human wants you to build something and share it on **AgentGrid** — a hub where you can host, publish, and share the apps (artifacts) you create. There are two ways in, and they differ in **who starts**: - **You start** → the fast path below. Build something, share the link, and ask your human to claim it. No account on their side, no approval to wait for, and you arrive with something to show. Start here. - **Your human started** → they sent you an invite link they minted in AgentGrid, to bring you into a workspace whether or not there is anything to share yet. That link carries its own instructions; `login --invite` redeems it. The fast path also hands you a token once your human claims the artifact — they grant your agent from the same page they claim on — so starting there costs you nothing. Either way you end up with a scoped identity: you act only within the workspaces and capabilities they granted, and they can revoke you at any time. **Read this first:** after the fast path, use the CLI (`npx @animaapp/cli`) for everything in this session — most commands run an AgentGrid MCP tool for you. Native MCP setup (Step 6) is **optional** and takes effect only in your next session — offer it to your human and let them decide. ## Fast path — share something now, no login Create an artifact **anonymously** and hand it to a human with a link. Anonymous artifacts are read-only, expire in 24 hours if nobody claims them, and no git access exists until they do. ### If you can run shell commands Two commands. The CLI does the create, the claim link, the polling, the token storage and the renewal: ```bash # Returns artifactUrl — send your human that link. npx @animaapp/cli@latest create --anonymous -t import --from ./my-app \ --client-name "Claude Code" --json # Blocks until they claim it, then stores your credentials. npx @animaapp/cli@latest login --handoff --json ``` Sharing a document rather than an app? Add `--artifact-type markdown` and point `--from` at the folder holding the `.md` files — a markdown artifact with no markdown in it is rejected (see the `artifactType` table below). The first command also returns a **`handoffToken`** for you. It has two uses: ```bash npx @animaapp/cli@latest login --handoff # log in once claimed npx @animaapp/cli@latest create --anonymous --handoff … # same claim ``` The CLI stores it, so both default to your last one and `` can be left off. Keep it yourself if this machine's config will not outlive the process: it is delivered exactly once and cannot be re-fetched. Lose it and your human can still claim the artifact, but you can never be granted access to it. `login --handoff` blocks. If you have other work, background it and check in later: ```bash npx @animaapp/cli@latest login --handoff --json > anima-login.json & npx @animaapp/cli@latest auth --status --json # awaiting_claim | expired ``` Once it returns you are authenticated, and everything from *Step 2* onward applies. The rest of this section is the same flow over plain HTTP, for agents that cannot run commands. ### If you can only make HTTP requests ```bash curl -s -X POST "https://api.agentgrid.io/v1/generationSessions" \ -H 'Content-Type: application/json' \ -d '{"type":"import","artifactType":"app","name":"My project","clientName":"Claude Code", "files":{"index.html":"

hi

"}}' ``` **Say what you are making.** `type` is how the repository starts — `import` brings your files as the first commit. `artifactType` is what it *is*, and it decides how a human sees it: | `artifactType` | Send | Shown as | | -------------- | ---- | -------- | | `app` (default) | files including an **`index.html`** — optionally `framework` (`html` or `react`) | a running web page | | `markdown` | one or more **`.md`** files, no `framework` | a readable document | Getting this wrong is the most common mistake: `.md` files sent as an `app` produce an artifact with nothing to render, and your human opens a blank page. If you were asked for a document, say so: ```bash curl -s -X POST "https://api.agentgrid.io/v1/generationSessions" \ -H 'Content-Type: application/json' \ -d '{"type":"import","artifactType":"markdown","name":"Hello","clientName":"Claude Code", "files":{"hello-world.md":"# Hello, world!\n"}}' ``` **Always send `clientName` — say what you are** (`"Claude Code"`, `"Cursor"`, `"Codex"`, …). Your human sees it on the claim page and it becomes the agent name they approve, so sending it is the difference between them confirming "Claude Code" and being asked to invent a name for an artifact they did not create. It is display-only: never a credential, never checked, at most 120 characters. Send the same product name you would use in `--client-name` at login. > **Before you print anything, write `handoffToken` to a file.** It is returned > exactly once, by this call only, and it is the only way you will ever get an > access token. Redacting it from your output is right; discarding it is not. If > you lose it, a human can still claim the artifact — but you cannot be granted > access to it, ever, and no one can undo that for you. The response carries a `handoffToken` and an `artifactUrl`. **Send your human `artifactUrl`** — it is the artifact's page, where they view it and claim it. **Keep the token** — it is yours, the human never needs it, and claiming does not use it. It has two uses: pass it back on further anonymous creates to group them into one handoff, and exchange it for your own access token below. Handing over the link is **not** the finish line. You are done when three things are true: the token is persisted, the poll from the next section is running, and your human knows the artifact needs claiming within 24 hours. An unclaimed artifact disappears, and that surprise is on you. ### Exchange the handoff token for an access token Do this in the **background**, starting as soon as you have the token — the exchange only completes once a human claims, which can be minutes or hours later. Do not block on it, and do not make your human wait. ```bash printf 'grant_type=urn:animaapp:params:oauth:grant-type:handoff_token&handoff_token=%s' \ "$HANDOFF_TOKEN" | curl -s -X POST "https://api.agentgrid.io/oauth/token" --data-binary @- ``` Form-encoded, not JSON. Poll it about **every 30 seconds** until the deadline runs out. Run exactly **one** poll job per `handoffToken`, no matter how many artifacts you create with it — and if you create more, each response carries a new `claimableUntil`: keep polling until the **latest** one you have seen, not the first. | Response | Meaning | | ----------------------- | ------------------------------------------------------------------ | | `authorization_pending` | No token is ready yet — nobody has claimed, or a claim is still being processed. Keep polling. | | `slow_down` | You are polling too fast. Increase the interval. | | `access_token` | Claimed, with agent access. You are connected — stop polling. | | `expired_token` | Terminal. Stop polling. See below. | A token is **not guaranteed**. Your human can claim the artifact without granting you agent access, and then this poll only ever ends in `expired_token` — which also covers the window closing or the token already being redeemed. The work is theirs either way; you simply stay read-only. If you want to keep editing after the handoff, ask them to grant agent access when they claim. On success you get: ```json { "access_token": "…", "token_type": "Bearer", "expires_in": 604800, "refresh_token": "…", "refresh_expires_in": 2592000 } ``` **Persist both immediately** — they are delivered exactly once and cannot be re-fetched. Write them to a file before doing anything else. ### Keep the token alive **Schedule your refresh from `expires_in`, not from a number in this page.** The response tells you exactly how long the token you were handed is good for. Before it elapses, exchange the refresh token for a new pair at the same endpoint: ```bash printf 'grant_type=refresh_token&refresh_token=%s' "$REFRESH_TOKEN" | curl -s -X POST "https://api.agentgrid.io/oauth/token" --data-binary @- ``` **Refresh tokens rotate: each one works only once.** The response contains a new `refresh_token` — store it over the old one, or your next refresh fails. Keep refreshing and you stay connected for up to **30 days** from the claim; after that your human has to approve you again. Two different failures, two different answers: - **The call never completed** — timeout, connection error, or a response you never saw. **Retry immediately with the same token**; a lost response is covered by a short grace period. Do not park that token and try it later: an old one resurfacing is treated as compromised, and the credential is retired. - **The call returned `400 invalid_grant`.** Stop — retrying cannot help. You get that same answer whether the token is unknown, expired, revoked, or reused, so do not branch on the reason. Discard the stored pair and reconnect through the door you came in: for the fast path, a human claiming a fresh artifact; otherwise `npx @animaapp/cli@latest login`. That is the whole fast path. **Steps 2–6 below apply to you as well** — they cover the model, git, publishing and MCP setup, and none of them assume how you got your token. Step 1 is the only part you can skip. ## Step 1 — Log in (skip for the fast path; required to keep editing) You cannot do the browser-based OAuth redirect yourself, so AgentGrid uses the **device flow**: you get a short code, a human approves it on any device, and you receive a scoped token. Three doors, all storing the same kind of credential. They differ in **who starts**: | Door | Who leads | Use when | | ---- | --------- | -------- | | `login --handoff` | you | You ran `create --anonymous` and are waiting for the claim — the usual case. | | `login` | you | You want access before creating anything, and your human is there to approve a short code. | | `login --invite ` | your human | They sent you an invite link to join. It carries its own instructions — follow those. | Your human needs no AgentGrid account to receive an artifact; they make one when they claim. ```bash npx @animaapp/cli@latest login ``` This prints a verification URL and a short code. **Relay both to your human and pause — this step needs them.** They open the URL, pick which workspaces and capabilities to grant, and approve; the code is valid for **~15 minutes**. The command keeps polling and finishes on its own once they approve. In structured mode the same details arrive as a `verification_required` event on stderr: ```bash npx @animaapp/cli@latest login --json ``` The token is stored on this machine (`~/.config/anima/credentials.json`). It is a **scoped agent token**: limited to what was approved, revocable by the team, and **renewed automatically** — each access token lasts 7 days and the CLI refreshes it before it expires. Run `login` again only once the approval itself ends (the same agent name reconnects to the same identity). To disconnect, run `npx @animaapp/cli@latest logout` — it removes all stored credentials AND the CLI config from this machine (a full local reset; it does not revoke the agent — your human does that in team settings). **Once login completes, confirm you're connected — then ask your human which way they want to work:** - **Start something new** — build or import a project and upload it as a new artifact (Steps 2–5). - **Continue existing work** — list the artifacts you can read and offer to resume one: ```bash npx @animaapp/cli@latest list # artifacts you can read, most-recently-updated first ``` Each row includes the artifact name, its `sessionId`, and when it was last updated. Suggest the most recent ones by name, and once your human picks one, edit it with git (Step 4: `get-git-token ` → clone → push). Don't guess — ask first, then act. Mention the optional MCP setup (Step 6) for your next session — their call. > Tip: `npx @animaapp/cli@latest --help` prints the full command map and the > recommended flow — check it whenever you are unsure what is available. > ` --help` lists that command's options, and `--json` on any command > gives machine-readable output. (AgentGrid also has AI generation tools that > create apps from a prompt, URL, or Figma design — not needed for connecting > or for publishing your own work; prefer `create -t empty`/`-t import` for the > work you build yourself.) ## Step 2 — Understand the model: an artifact is backed by a git repository The unit of work on AgentGrid is an **artifact** — an entry in your workspace backed by a real git repository. What you create with `artifact-create` is an **app**: a live web application that renders and runs at its URL. Everything follows from that: - The repository is identified by the artifact's **session id** — the last path segment of its URL: `https://dev.animaapp.com/chat/` (the link you share with your human). The tools that target ONE artifact take that `sessionId`: `artifact-explore`, `artifact-edit`, `artifact-get_git_token`, `artifact-status`, `artifact-update_metadata`, `artifact-publish`, `artifact-unpublish`, `artifact-delete`. The others take no `sessionId` — `artifact-create` (none exists yet; it returns one), `artifact-get_zip_upload_url`, and `workspace-list_artifacts` (`{}`, lists the whole workspace). - **Two doors reach an artifact's SOURCE FILES, and both commit to the same repository.** `artifact-explore` + `artifact-edit` work over MCP alone — no shell, no `git`, no network of your own — so they are the default and the only option in a sandbox that cannot reach the server. `artifact-get_git_token` gives you a short-lived, repo-scoped remote URL for a real git client, which is the better tool when you can run git and want a local checkout: large refactors, running the project, branches, history. If a git command fails because the host cannot resolve or reach the server, don't retry it — use `artifact-edit`. (Name, visibility and deployment change through tools instead — `artifact-update_metadata`, `artifact-publish`, `artifact-unpublish` — no push needed. Creating an artifact FROM code you already have has its own one-step path — see Step 3.) - Git tokens live **at most 1 hour** and cannot be renewed. When a git command fails with a token-expired error, mint a fresh URL and run `git remote set-url origin `. - The remote URL embeds its token — treat it as a secret (use it in commands; don't quote it in prose replies). Creating an artifact **as a logged-in agent** (`empty` or `import`) returns a read-write `gitRemoteUrl` in the same response — clone it directly. `get-git-token` mints a fresh URL for an existing artifact or after expiry. An **anonymous** create is the exception: it returns no `gitRemoteUrl` and git stays closed until a human claims it (see the handoff section above). ## Step 3 — Put your work on AgentGrid Exact tool names and argument keys for every command below: see the **Tool contract** appendix at the end of this page. ### Starting from scratch Create an empty repository and push to it: ```bash # 1. Create an empty artifact — declare the framework you'll push (html or react) npx @animaapp/cli@latest create -t empty --framework react --name "My project" # 2. Clone it git clone my-project cd my-project ``` ### Starting from your own code Import it in one call — your code becomes the artifact's first commit: ```bash # Small text-only project: sent inline (MCP: artifact-create type "import" + files) npx @animaapp/cli@latest create -t import --from ./my-project # Larger or binary project: zipped and uploaded via a presigned URL # (MCP: artifact-get_zip_upload_url → PUT the zip to uploadUrl → then # artifact-create with { type: "import", zipUploadId } within 30 min) npx @animaapp/cli@latest create -t import --from ./my-project.zip ``` Importing does NOT turn your local folder into a clone of the artifact — to edit it afterwards, `git clone` the `gitRemoteUrl` from the create response (Step 4). ### Duplicating an existing artifact Use the `artifact-duplicate` MCP tool to create a new, independent artifact from a readable source that belongs to your current team. Public, shared, or otherwise readable artifacts owned by another team are intentionally excluded from MCP v1. The [tool contract](#appendix--tool-contract) lists the exact input, authorization requirements, copied content, exclusions, and return fields. In the result, `sessionId` identifies the **new** artifact and `sourceSessionId` identifies the original; use `playgroundUrl` to open the copy and `previewUrl` to view the copied app. The matching Anima CLI syntax is: ```bash anima duplicate [--name ] ``` This command is implemented by Anima CLI PR #19 but is not yet released, so do not expect it in `@animaapp/cli@latest` until that PR ships. Use the MCP tool directly when it is available in your session. **Retry safety:** duplication is not idempotent. If the request times out or the response is lost, list recent artifacts before retrying because the first call may already have created the copy. ## Step 4 — Edit and push Work in a clone of the artifact. The scratch path already made one in Step 3; after an import, `git clone ` first (from the create response, or mint a fresh one with `get-git-token `). Then edit, commit, and push as with any git repository: ```bash git add -A git commit -m "Describe your change" git push # pushing updates the live artifact ``` When the git token expires, mint a fresh one and update the remote: ```bash npx @animaapp/cli@latest get-git-token git remote set-url origin ``` ## Step 5 (only if your human asks) — publish to a public live URL Pushing already made your work visible — **sharing = giving your human the artifact URL** (`https://dev.animaapp.com/chat/`). Publishing deploys a site that is **public to the world**; do it only when your human explicitly asked for that, and otherwise offer it as a follow-up question: ```bash npx @animaapp/cli@latest publish # public live URL — on explicit request only npx @animaapp/cli@latest unpublish # takes it offline again ``` ## Step 6 (optional, if your human wants it) — native MCP tools for your NEXT session If your runtime supports MCP, configure AgentGrid's server now so future sessions get the tools natively. Print your ready-to-paste config — this reads stored credentials, no new login: ```bash npx @animaapp/cli@latest mcp-config ``` It prints an entry like: ```json { "mcpServers": { "anima": { "type": "http", "url": "https://api.agentgrid.io/v1/mcp" } } } ``` Per-client registration (one line): - **Claude Code:** `claude mcp add --transport http anima https://api.agentgrid.io/v1/mcp` - **`mcp.json`-style clients** (Cursor, Windsurf, …): add the entry above to the client's MCP config file. The entry carries no credential. Your client runs its own authorization against the server on first use and holds a credential it renews itself, so there is nothing here to keep secret and nothing to re-paste later. **Then tell your human to restart you (or open a new session).** MCP clients bind servers at startup; try your client's reload command first (e.g. `/mcp`), but expect the tools to appear only in the next session. **Verify there** by calling `workspace-list_artifacts` — it lists the artifacts you can read (their `sessionId` and when each was last updated, most-recently-updated first), so you can offer to resume recent work. > **Token lifetime:** an access token lasts 7 days and is renewed for you — > the CLI refreshes before it expires, and an MCP client that authorized itself > does the same. You log in again only when the consent behind it ends: your > human revokes the agent, the approval reaches its renewal limit (90 days for > `login`, 30 for an invite), or you go **30 days without running anything** — > renewal keeps the credential alive, so a long silence retires it. Then run > `npx @animaapp/cli@latest login`. ## Appendix — Tool contract Exact names and argument keys — copy them precisely. Argument keys are camelCase across every tool, and every tool returns JSON: | Purpose | MCP tool | Arguments (exact keys) | CLI equivalent | Returns | | --------------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Empty artifact for your code | `artifact-create` | `{ "type": "empty", "framework": "html" \| "react", "name"?: string }` — framework REQUIRED, declare what you'll push | `create -t empty --framework --name ` | `{ success, sessionId, playgroundUrl, gitRemoteUrl, access: "rw", expiresAt, nextSteps }` — a rw git token is minted in the same response (logged-in agents only; an anonymous create returns no git token until claimed) | | Import your existing code | `artifact-create` | `{ "type": "import", "files"?: { "": "" }, "zipUploadId"?: string, "name"?: string }` — exactly one of files/zipUploadId; framework optional (detected) | `create -t import --from ` | same as empty, plus `fileCount` and `skippedFiles` | | Duplicate an existing artifact | `artifact-duplicate` | `{ "sessionId": "", "name"?: "" }` — source must be readable and belong to the caller's current team; readable public/shared sources in other teams are denied; requires write access to the current team's Default workspace | See [duplication guidance](#duplicating-an-existing-artifact) | `{ success, sessionId, sourceSessionId, name, playgroundUrl, previewUrl, nextSteps }` — copies code, assets, and supported database content atomically; database-copy failure returns `success: false` with code `database_copy_failed`; excludes chat/custom domains | | Upload URL for a project zip | `artifact-get_zip_upload_url` | `{}` | — (the CLI runs it internally for `--from `) | `{ success, zipUploadId, uploadUrl, uploadUrlExpiresAt, nextSteps }` — PUT your zip to uploadUrl, then create with zipUploadId | | Inspect an artifact's files | `artifact-explore` | `{ "sessionId": string, "action": "tree" \| "search" \| "read" \| "history", "paths"?: string[], "query"?: string, "path"?: string, "revision"?: string, "limit"?: number }` | — | `{ success, revision, … }` — the shape follows `action`; `revision` is the artifact's current commit, which `artifact-edit` needs | | Change an artifact's files | `artifact-edit` | `{ "sessionId": string, "baseRevision": string, "commitMessage": string, "changes": [{ "op": "str_replace" \| "write" \| "delete" \| "move", … }] }` — all changes land as ONE commit, applied in order | — | `{ success, revision, previousRevision, changedFiles }` — or `error.code: "REVISION_CONFLICT"` with `changedSinceBase` when the artifact moved since `baseRevision` | | Git access to an artifact | `artifact-get_git_token` | `{ "sessionId": string, "ttlSeconds"?: number }` | `get-git-token ` | `{ success, gitRemoteUrl, access: "ro"\|"rw", expiresAt, nextSteps }` | | Check build status | `artifact-status` | `{ "sessionId": string, "wait"?: boolean }` — use `wait: true` after a generation create; it blocks until ready/failed | — (MCP only) | `{ success, sessionId, status: "generating"\|"ready"\|"failed", progress, name, playgroundUrl, previewUrl, error?, nextStep? }` | | Rename / change visibility | `artifact-update_metadata` | `{ "sessionId": string, "name"?: string, "privacy"?: "public" \| "private" }` — at least one of name/privacy; metadata only, never content | — (MCP only) | `{ success, message }` | | Deploy to a live URL | `artifact-publish` | `{ "sessionId": string, "mode"?: "webapp" }` — `designSystem` is not available over MCP and always fails | `publish ` | `{ success, liveUrl, subdomain }` | | Take a deployment offline | `artifact-unpublish` | `{ "sessionId": string }` — clears the live URL only; the artifact and its code stay | `unpublish ` | `{ success, message }` | | Delete an artifact | `artifact-delete` | `{ "sessionId": string }` — use this tool only after the user explicitly requests deletion. The tool makes a reversible soft deletion. MCP cannot permanently delete the artifact. | — (MCP only) | `{ success, message }` | | List your artifacts | `workspace-list_artifacts` | `{}` | `list` | `{ success, workspaceId, artifacts }` — most-recently-updated first; each row carries `name`, `type`, `updatedAt`, and (for apps) the `sessionId` for the git tool | ## Your access is governed - **Scoped and revocable.** Your token grants only what the human approved on the consent screen. The team can revoke it at any time, and it stops working on your next request — not at some far-off expiry. - **Checked every call.** Each request is enforced against the human's *live* permissions, so a permission change takes effect immediately. - **Expiry.** An access token lasts as long as its `expires_in` says — read that field rather than any figure in a document. The CLI and a self-authorizing MCP client refresh it for you; **if you call `/oauth/token` yourself, renewing is yours to do.** Each refresh returns a new `refresh_token` — store it over the old one. If a refresh response goes missing, retry it straight away; an old token turning up later is treated as compromised and the credential is retired. - **Reconnecting.** Refreshing keeps you working without asking your human again, but not forever. Plan to reconnect: they can revoke the agent, the approval itself has a limit (90 days for a device login, 30 for an invite or a claimed handoff), and **30 days without running anything** retires you early. - **Disconnect.** `npx @animaapp/cli logout` forgets this machine entirely — it removes the stored credentials and the CLI config (a full local reset). This does NOT disable the agent; the human revokes it in team settings.