# 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 ("Native MCP tools for your NEXT session") 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 <token>                # log in once claimed
npx @animaapp/cli@latest create --anonymous --handoff <token> … # same claim
```

The CLI stores it, so both default to your last one and `<token>` 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 *Understand the model* 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":"<!doctype html><h1>hi</h1>"}}'
```

**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. **Everything below the login step applies to you
as well** — the model, changing files, reviews, publishing and MCP setup, none of
which assume how you got your token. Logging in is the only part you can skip.

## 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 <url-or-code>` | 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 (see the sections below).
- **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,
  change it with `explore` and `edit`, or clone it (see "Change the files").

Don't guess — ask first, then act. Mention the optional MCP setup 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.
> `<command> --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.)

<!-- The door-agnostic half of the agent guide: everything that applies once you
hold a token, however you got one. Served on its own at GET /guide.md (what the
CLI formats into a skill) and appended to GET /connect after the login doors. -->

## 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://app.agentgrid.io/artifacts/<sessionId>`, the
  artifact's page in AgentGrid and the link you share with your human. (An app
  artifact also answers on the Playground host — `playgroundUrl` to edit,
  `previewUrl` to render — but a markdown or asset artifact has neither, so
  `artifactUrl` is the one link that always works.) 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`, `artifact-duplicate` (the artifact to
  copy FROM) and `artifact-get_asset_upload_url`. `review-list` takes one too,
  optionally — omit it to see every review addressed to you across artifacts.
  The rest take no `sessionId`: `artifact-create` (none exists yet; it returns
  one), `artifact-get_zip_upload_url`, `workspace-list_artifacts` (`{}`, lists
  the whole workspace), and `review-reply` / `review-resolve`, which name a
  `commentId` instead — the comment already knows which artifact it is on.
- **Two doors reach an artifact's SOURCE FILES, and both commit to the same
  repository.** `artifact-explore` + `artifact-edit` need no `git` and no
  clone — they are the default, and the only option in a sandbox that cannot
  run git. Over MCP they need no shell either; from the CLI they are
  `explore` and `edit`. `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 "Put your work on AgentGrid".)
- 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 <new url>`.
- 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.

## 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 <gitRemoteUrl> 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 (see "Change the files").

### 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` the original; `artifactUrl` is the copy's page. When the
copy is an app it also carries `playgroundUrl` to edit it and `previewUrl` to
render it.

Or from the CLI:

```bash
npx @animaapp/cli@latest duplicate <artifact-url-or-session-id> [--name <name>]
```

**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.

## Change the files

**Without a clone** — read what is there, then commit a change. No git, no
checkout, and the live artifact updates from the new commit immediately:

```bash
npx @animaapp/cli@latest explore <sessionId> --search "primaryColor"
npx @animaapp/cli@latest explore <sessionId> --read src/App.tsx
npx @animaapp/cli@latest edit <sessionId> -m "Blue buttons" \
  --replace src/App.tsx --old "red" --new "blue"
```

Everything in one `edit` lands as ONE commit: either every change applies or
none does. Reach for the clone below when the job actually needs one — a large
refactor, running the project, branches, or rewriting history.

**With a clone.** Work in a clone of the artifact. The scratch path already made one in
"Put your work on AgentGrid"; after an import, `git clone <gitRemoteUrl>` first (from the create
response, or mint a fresh one with `get-git-token <sessionId>`). 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 <sessionId>
git remote set-url origin <gitRemoteUrl>
```

## Answer the comments your human left you

Humans review artifacts in AgentGrid and pin comments to places inside them — a
spot in a running app, a passage in a document. Those comments are addressed to
**you**, and nothing pushes them at you: `review-list` is how they arrive. Call
it when your human says they left comments, and at the start of work on an
artifact you have been reviewed on before.

Each comment carries an anchor saying where it points (`sourceFile` with line
numbers for an app; a `quote` to search for in a document — find it by
searching, never by the offsets, which any edit above them invalidates), its own
`status`, and the `replies` so far. **Read the replies before acting**: a
reviewer can keep talking after sending, so the comment body is where the
request starts, not necessarily where it ends. `isLatest: false` means the
artifact moved since the review was written — judge whether the comments still
apply before acting on them.

Three ways to close the loop, and picking the right one matters:

- **A commit addresses it** — put the review's `resolveTrailer` lines in your
  commit message, one per comment the commit actually addresses, dropping the
  rest. That records which commit resolved what.
- **No commit will** — a question answered, or a change you decided against:
  `review-resolve` with a `note` telling the reviewer why. This ends the
  exchange.
- **You are not finished** — you need to ask what they meant, or report a
  blocker: `review-reply`. The comment stays **open** so they can answer; their
  reply comes back on the same comment, so call `review-list` again to read it.
  Do not wait on it.

You see the comments addressed to you plus any addressed to no agent in
particular, which arrive marked `unassigned`. Those are anyone's to take —
reply first and it is yours — and another agent may be working the same one.
Comments addressed to a *different* agent are not yours to act on or report on.

## Publish to a public live URL — only if your human asks

Pushing already made your work visible — **sharing = giving your human the
artifact URL** (`https://app.agentgrid.io/artifacts/<sessionId>`). 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 <sessionId>       # public live URL — on explicit request only
npx @animaapp/cli@latest unpublish <sessionId>     # takes it offline again
```

## Native MCP tools for your NEXT session — optional

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.

The **CLI equivalent** column assumes `@animaapp/cli` **0.8.0 or newer**, which
ships alongside these tools; older versions cover a subset. Run
`npx @animaapp/cli@latest --help` to see what your version actually has.

This table covers the tools this flow uses. One more exists outside it and is
not needed to connect or to share your own work: `codegen-figma_to_code`
(Figma design to code files, no artifact).

| 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 <f> --name <n>`                    | `{ success, sessionId, revision, artifactUrl, name, 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). `revision` is the first commit: pass it to `artifact-edit` as `baseRevision`                                                                                                                             |
| Import your existing code         | `artifact-create`             | `{ "type": "import", "files"?: { "<path>": "<utf-8 text>" }, "zipUploadId"?: string, "name"?: string }` — exactly one of files/zipUploadId; framework optional (detected)                                                                                   | `create -t import --from <folder\|zip>`                         | same as empty, plus `fileCount` and `skippedFiles`                                                                                                                                                                                                                    |
| Duplicate an existing artifact  | `artifact-duplicate`          | `{ "sessionId": "<source-session-id>", "name"?: "<copy-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, artifactType, artifactUrl, nextSteps }` — plus `playgroundUrl` and `previewUrl` when the copy is an app; 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 <zip>`)               | `{ 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 }`                                                                                | `explore <sessionId> --tree\|--search\|--read\|--history`                                                               | `{ 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                                                     | `edit <sessionId> -m <msg> --replace/--write/--delete/--move`                                                               | `{ 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 <sessionId>`                                     | `{ 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                                                     | `status <sessionId> [--wait]`                                      | `{ success, sessionId, status: "generating"\|"ready"\|"failed", progress, name, artifactUrl, error?, nextStep? }` — plus `playgroundUrl` and `previewUrl` for app artifacts             |
| Rename / change visibility      | `artifact-update_metadata`                            | `{ "sessionId": string, "name"?: string, "privacy"?: "public" \| "private" }` — at least one of name/privacy; metadata only, never content                                | `update <sessionId> [--name <n>] [--privacy <p>]` | `{ success, message }`                                                                                                                    |
| Deploy to a live URL            | `artifact-publish`                                    | `{ "sessionId": string, "mode"?: "webapp" }` — `designSystem` is not available over MCP and always fails                                                                   | `publish <sessionId>`                             | `{ success, liveUrl, subdomain }`                                                                                                         |
| Take a deployment offline       | `artifact-unpublish`                                  | `{ "sessionId": string }` — clears the live URL only; the artifact and its code stay                                                                                       | `unpublish <sessionId>`                           | `{ 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.                             | `delete <sessionId>`                                      | `{ success, message }`                                                                                                                    |
| Stage a large asset             | `artifact-get_asset_upload_url`                       | `{ "sessionId": string, "filename": string }` — for an image, font or media file over 256 KB, which is the most `artifact-edit` takes inline                              | `upload-asset <sessionId> <file> --path <p>`                                      | `{ success, assetUploadId, uploadUrl, uploadFields, expiresAt, maxBytes, nextSteps }` — POST the file as multipart/form-data (every `uploadFields` entry first, the file last), then `artifact-edit` a `write` change carrying `assetUploadId` |
| Read the comments left for you  | `review-list`                                         | `{ "sessionId"?: string, "limit"?: number }` — omit `sessionId` for every open review addressed to you, across every artifact you can reach                               | `review list [artifactUrl]`                                      | `{ success, reviews }` — each review carries `resolveTrailer` (the exact commit-message lines that close its comments), and each comment its anchor, `status` and `replies` |
| Close one comment               | `review-resolve`                                      | `{ "commentId": string, "note"?: string }` — for what a commit cannot carry; prefer the `resolveTrailer` in a commit message when a commit does address it                | `review resolve <commentId> [--note <n>]`                                      | `{ success, commentId, resolved, message }` — `resolved: false` means it was already closed, which is not a failure |
| Answer one comment, leave it open | `review-reply`                                      | `{ "commentId": string, "message": string }` — use when the exchange is not finished; resolving would close it                                                            | `review reply <commentId> -m <msg>`                                      | `{ success, commentId, replied, message }` — `replied: false` means the comment is already closed and nothing was posted |
| 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.
