---
title: "Connect an AI agent (MCP)"
description: "Connect Claude Code, a file-configured client, or your own agent to the roasOS MCP server."
url: https://docs.roasos.com/mcp
---

# Connect an AI agent (MCP)

Connect an agent to roasOS, and it gets the whole product as tools. The door changes, and the rules do not: a paid tool quotes first, and the server never approves spend.

The tools cover your brand, competitor research, every model, and the roasOS agent itself. This page connects a client, names every tool, and states what the server refuses to do.

MCP works on every plan. Each paid tool uses the credits on your account. API keys for the REST API need the Growth or Scale plan.

## The server

roasOS runs one [MCP](https://modelcontextprotocol.io) server. One worker serves the REST API and the MCP server. A tool and its REST operation are therefore the same code.

| Fact                           | Value                                               |
| ------------------------------ | --------------------------------------------------- |
| Endpoint                       | `https://mcp.roasos.com/mcp`                        |
| Transport                      | MCP Streamable HTTP, stateless, JSON answers        |
| Protocol version               | `2025-11-25`                                        |
| Server name, title and version | `roasos`, `ROASOS`, `2026-07-17.phase1`             |
| Credential                     | OAuth 2.1, or `Authorization: Bearer roasos_sk_...` |

The server binds one REST operation to each tool. It forwards the call to that operation, and the operation authorizes it again. Scopes, entitlement, rate limits, spend policy, quotes and idempotency therefore cannot drift between the two doors. The rate limit is the rate limit of your key. Refer to [Authentication](/authentication).

The transport carries four headers and no others: `Authorization`, `Idempotency-Key`, `X-RoasOS-Project` and `X-RoasOS-Cost-Center`. A cookie never travels.

There is no SSE transport and no stdio transport. The server keeps no session, so each request stands alone.

## Use with Claude

Claude (claude.ai and the Claude apps) connects with OAuth. You do not need an API key.

1. In Claude, open **Settings → Connectors**.
2. Find **ROASOS** in the directory and select **Connect**. If you do not find it, select **Add custom connector** and type the URL `https://mcp.roasos.com/mcp`.
3. Sign in to ROASOS. The consent screen shows the host that the connection returns to (`claude.ai`), the name the app gives, and what the connection can do.
4. Select the workspaces, at most 25. The workspaces that you own are selected at the start. A workspace that you only administer uses the credits of its owner, so you select it yourself. Clear the ones that the connection must not use.
5. Select **Connect**. Claude returns and lists the ROASOS tools.

One connection can use all the workspaces that you selected. Credits come from the balance of each workspace owner, the same as in the app. If you own all the workspaces, they share your one balance.

* `roasos_list_workspaces` gives the id and the name of each connected workspace, and the default workspace. The default is the first one in the list: a workspace that you own, when you selected one.
* When the connection has more than one workspace, each tool takes `workspace_id`. A tool that reads uses the default workspace if you do not send `workspace_id`. A tool that writes or spends must have `workspace_id`, so that a model never spends in a workspace by default.

Each connection makes one API key in each selected workspace. Its name is `MCP · ` and the app name, for example `MCP · Claude`. Each key counts toward the limit of 10 active keys in its workspace.

To disconnect fully, revoke the `MCP · ` keys in [workspace settings](https://www.roasos.com/settings/workspace). When you remove the connector in Claude, Claude stops calling, but the keys stay active until you revoke them, or until your next connection replaces them. After a revoke, that workspace leaves the connection. When no workspace is left, the next call answers `401 invalid_token` and Claude asks you to sign in again.

Every paid tool and every tool that changes existing data is marked destructive (`destructiveHint: true`), so a client such as Claude can ask you before it runs one. A paid tool also needs a quote and then a confirm. Refer to [Money](#money).

## Connect a client

Three recipes follow for clients that send a header. Each one uses the same key as the REST API. Make the key in your [workspace settings](https://www.roasos.com/settings/workspace).

### Claude Code

```bash
claude mcp add --transport http roasos https://mcp.roasos.com/mcp \
  --header "Authorization: Bearer $ROASOS_API_KEY"
```

The command writes a local entry. Add `--scope project` to write `.mcp.json` beside your code. Add `--scope user` to reach every project.

Then type `/mcp` in Claude Code. The client lists the roasOS server and its tools.

The short forms are `-t` for `--transport` and `-H` for `--header`. Command form from the Claude Code MCP reference, read on 2026-08-22.

### A configuration file

Claude Desktop and most file-configured clients read an `mcpServers` block.

```json
{
  "mcpServers": {
    "roasos": {
      "type": "http",
      "url": "https://mcp.roasos.com/mcp",
      "headers": {
        "Authorization": "Bearer roasos_sk_..."
      }
    }
  }
}
```

Set `type` to `http`. An entry with a `url` and no `type` is a configuration error. The value `streamable-http` is an accepted alias for `http`.

The file then holds your key. Keep it out of version control.

### claude.ai

claude.ai signs in with OAuth and sends no header. Refer to [Use with Claude](#use-with-claude). Leave the OAuth client id and client secret fields empty: the connector registers itself.

### Your own client

Send JSON-RPC over HTTP POST.

```bash
curl -X POST https://mcp.roasos.com/mcp \
  -H "Authorization: Bearer roasos_sk_..." \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```

The `Accept` header must name both types. A request that names one answers `406`, with the message `Client must accept both application/json and text/event-stream`.

The answer is JSON. An `initialize` call reports the protocol version and the server name. It also carries the server instruction: paid actions are quote-first, and the server never approves spend.

A browser client must send an allowed `Origin`. The three allowed values are `https://mcp.roasos.com`, `https://api.roasos.com` and the roasOS app. Another origin answers `403`. A request with no `Origin` header passes.

## OAuth and the hostname

OAuth 2.1 is live on `https://mcp.roasos.com`. A client that supports OAuth (for example a claude.ai custom connector) signs in with your roasOS account; it needs no API key. Discovery is at `https://mcp.roasos.com/.well-known/oauth-authorization-server`, and the scopes are `api:read` and `api:write`.

An API key also works, for a client that can carry a bearer header.

* A request with no `Authorization` header answers `401`. Its `WWW-Authenticate` header is a `Bearer` challenge. The challenge names `resource_metadata` and the scopes `api:read api:write`, so an OAuth client can start sign-in.
* An unknown key or a bad token answers `401`. Its `WWW-Authenticate` header carries `error="invalid_token"`.
* A connection whose keys were all revoked or have expired answers `401 invalid_token` on every `POST`, and its refresh token answers `invalid_grant`. The client signs in again.
* Plain `http://` on `mcp.roasos.com` answers `400`. The server does not redirect. Use `https://`.

<Callout type="warn">
  Point every client at `https://mcp.roasos.com/mcp`. The OAuth provider compares the request origin with the token audience, so an OAuth token does not open `https://api.roasos.com/mcp`.
</Callout>

## The tools

Each tool carries its own marks. Read them before you call one.

| Mark                                   | Where it is | What it means                                                                                                                                                |
| -------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `annotations.readOnlyHint`             | on the tool | The tool changes nothing.                                                                                                                                    |
| `annotations.destructiveHint`          | on the tool | The tool spends credits, or it changes or removes something that exists (an overwrite, a cancel, a decline). This is true even when you can undo the change. |
| `annotations.openWorldHint`            | on the tool | The tool reaches outside your workspace: a model provider, a web page, a social platform or an app store.                                                    |
| `_meta['roasos/paid']`                 | on the tool | The tool can spend credits.                                                                                                                                  |
| `_meta['roasos/approval-recommended']` | on the tool | A person should see the action first.                                                                                                                        |
| `_meta['roasos/capability-id']`        | on the tool | The capability behind the tool.                                                                                                                              |

The lists below use three words. **Read** is a tool that changes nothing. **Paid** is a tool that can spend credits. **Free write** is a tool that writes and charges nothing.

**Account and platform (6).**

| Tool                         | REST operation                  | Mark |
| ---------------------------- | ------------------------------- | ---- |
| `roasos_list_capabilities`   | `GET /v1/capabilities`          | read |
| `roasos_get_account`         | `GET /v1/account`               | read |
| `roasos_get_limits`          | `GET /v1/limits`                | read |
| `roasos_list_usage`          | `GET /v1/account/usage`         | read |
| `roasos_list_billing_ledger` | `GET /v1/account/ledger`        | read |
| `roasos_summarize_usage`     | `GET /v1/account/usage/summary` | read |

**Catalog (3).**

| Tool                       | REST operation            | Mark |
| -------------------------- | ------------------------- | ---- |
| `roasos_get_catalog`       | `GET /v1/catalog`         | read |
| `roasos_get_catalog_model` | `GET /v1/catalog/{model}` | read |
| `roasos_list_formats`      | `GET /v1/formats`         | read |

**Jobs (9).**

| Tool                    | REST operation              | Mark                           |
| ----------------------- | --------------------------- | ------------------------------ |
| `roasos_list_jobs`      | `GET /v1/jobs`              | read                           |
| `roasos_get_job`        | `GET /v1/jobs/{id}`         | read                           |
| `roasos_show_media_job` | `GET /v1/jobs/{id}`         | read                           |
| `roasos_rerun_job`      | `POST /v1/jobs/{id}/rerun`  | paid                           |
| `roasos_get_job_status` | `GET /v1/jobs/{id}/status`  | read                           |
| `roasos_wait_for_jobs`  | `GET /v1/jobs/wait`         | read                           |
| `roasos_get_job_result` | `GET /v1/jobs/{id}/result`  | read                           |
| `roasos_get_job_events` | `GET /v1/jobs/{id}/events`  | read                           |
| `roasos_cancel_job`     | `POST /v1/jobs/{id}/cancel` | free write, only your own jobs |

`roasos_get_job` gives the exact request of the job under `inputs`, with the endpoint. `roasos_rerun_job` sends that request again through the same endpoint, with an optional new `prompt`. It quotes first. A job with no recorded request answers `409 rerun_inputs_unavailable`. A job that carries a statement of media rights answers `409 rerun_requires_new_rights_statement`, because a person must make that statement again.

`roasos_show_media_job` opens an inline viewer on a host that supports MCP Apps. The viewer is the resource `ui://roasos/media-viewer.html`. It loads media only from `cdn.roasos.com`. Another host gets the same JSON as `roasos_get_job`.

`roasos_cancel_job` cancels only a job that the same connection started. The same connection is the same API key, or the same OAuth grant. Each OAuth grant has its own key, so a reconnect starts a new connection. A job that a person started in the roasOS app, or that a different key or grant started, answers `403 job_not_cancelable_by_caller`. The person can cancel that job in the roasOS app. When the job is not yet at the model provider, it stops and all its credits come back (`cancel_honoured: true`). When the provider already has the job, it runs to the end and roasOS bills it (`cancel_honoured: false`). A job that already finished answers `409 job_not_cancelable`. These are the REST cancel rules, and the refunds are the same.

**Generations (8).**

| Tool                             | REST operation                       | Mark                       |
| -------------------------------- | ------------------------------------ | -------------------------- |
| `roasos_generate_image`          | `POST /v1/generations`               | paid                       |
| `roasos_generate_video`          | `POST /v1/generations`               | paid                       |
| `roasos_generate_audio`          | `POST /v1/generations`               | paid                       |
| `roasos_generate_lipsync`        | `POST /v1/generations`               | paid                       |
| `roasos_quote_generation_batch`  | `POST /v1/generation-batches/quotes` | paid, and it never charges |
| `roasos_create_generation_batch` | `POST /v1/generation-batches`        | paid                       |
| `roasos_list_generations`        | `GET /v1/generations`                | read                       |
| `roasos_get_generation`          | `GET /v1/generations/{id}`           | read                       |

<Callout type="warn">
  The first four tools call `POST /v1/generations`. roasOS deprecated that operation. Its `Sunset` header reads `Mon, 30 Nov 2026 00:00:00 GMT`. Use a model-endpoint tool for new work.
</Callout>

A batch is two calls. `roasos_quote_generation_batch` quotes up to eight generations and returns a `batch_quote_id`. `roasos_create_generation_batch` takes that `batch_quote_id` and admits all the jobs at once. The server remembers the key of the quote for one hour, so create the batch inside that hour. After one hour, or when the quote is unknown, the create answers `batch_quote_unknown`: quote the batch again. When the quote answers `approval_required`, a person approves it at its `authorize_url` before the create. Then wait for the jobs with `roasos_wait_for_jobs`.

**Scenes (2).**

| Tool                 | REST operation        | Mark |
| -------------------- | --------------------- | ---- |
| `roasos_list_scenes` | `GET /v1/scenes`      | read |
| `roasos_get_scene`   | `GET /v1/scenes/{id}` | read |

**Avatars (2).**

| Tool                  | REST operation         | Mark |
| --------------------- | ---------------------- | ---- |
| `roasos_list_avatars` | `GET /v1/avatars`      | read |
| `roasos_get_avatar`   | `GET /v1/avatars/{id}` | read |

**Products (4).**

| Tool                       | REST operation               | Mark       |
| -------------------------- | ---------------------------- | ---------- |
| `roasos_create_product`    | `POST /v1/products`          | free write |
| `roasos_list_products`     | `GET /v1/products`           | read       |
| `roasos_get_product`       | `GET /v1/products/{id}`      | read       |
| `roasos_build_product_kit` | `POST /v1/products/{id}/kit` | paid       |

`roasos_list_products` takes `query` for text in the name, brand or description, and `url` for one product page. The scheme, `www.`, the query string and a trailing slash do not count.

`roasos_build_product_kit` is quote-first, as every paid tool is. Send `full: true` for eight angles, or an `angles` list. With neither, the tool builds the four core angles. First call it with `quote_only: true`. The quote charges nothing. Then send the same input with the `quote_id`. The confirm charges exactly the quoted credits and starts the kit job. When a kit job already runs for the product, the confirm answers `409 kit_job_running` and charges nothing. Wait for the job with `roasos_wait_for_jobs`, then read the kit with `roasos_get_product`. Refer to [Product kits](/concepts/product-kits).

**Uploads (3).**

| Tool                        | REST operation            | Mark       |
| --------------------------- | ------------------------- | ---------- |
| `roasos_create_upload`      | `POST /v1/uploads`        | free write |
| `roasos_get_upload`         | `GET /v1/uploads/{id}`    | read       |
| `roasos_import_social_post` | `POST /v1/uploads/social` | free write |

`roasos_import_social_post` copies the media of one public TikTok, Instagram, X or Meta post into your workspace. The request must carry `media_rights`. Send it only after the person states that they may use this post. roasOS records the statement before it copies a file. A file over 16 MiB is refused.

**Projects (2).**

| Tool                    | REST operation      | Mark       |
| ----------------------- | ------------------- | ---------- |
| `roasos_list_projects`  | `GET /v1/projects`  | read       |
| `roasos_create_project` | `POST /v1/projects` | free write |

One chat is one project. Pass the `conversation_id` of a project to `roasos_agent_ask` to work inside it.

**Measured reading (5).**

| Tool                    | REST operation              | Mark       |
| ----------------------- | --------------------------- | ---------- |
| `roasos_capture_frames` | `POST /v1/media/frames`     | free write |
| `roasos_view_filmstrip` | `POST /v1/media/filmstrip`  | free write |
| `roasos_transcribe`     | `POST /v1/media/transcribe` | paid       |
| `roasos_ask_media`      | `POST /v1/media/ask`        | paid       |
| `roasos_describe_sound` | `POST /v1/media/sound`      | paid       |

A frame is a workspace image that you can use again. `roasos_view_filmstrip` measures cuts by the difference between sampled frames, and the answer states the sample interval. `roasos_analyze_ad` gives the same measurement under `measured_cuts` in its result. A read that roasOS already stored costs nothing and needs no confirm.

**Discover (2).**

| Tool                       | REST operation                | Mark |
| -------------------------- | ----------------------------- | ---- |
| `roasos_search_discover`   | `GET /v1/discover/items`      | read |
| `roasos_get_discover_item` | `GET /v1/discover/items/{id}` | read |

**Research (4).**

| Tool                         | REST operation                      | Mark |
| ---------------------------- | ----------------------------------- | ---- |
| `roasos_run_research`        | `POST /v1/research/jobs`            | paid |
| `roasos_get_research_job`    | `GET /v1/research/jobs/{id}`        | read |
| `roasos_get_research_status` | `GET /v1/research/jobs/{id}/status` | read |
| `roasos_get_research_result` | `GET /v1/research/jobs/{id}/result` | read |

Research reads the Facebook Ad Library. `roasos_run_research` takes one of two capabilities.

| `capability_id`                   | `target`                              | What it reads                                                 |
| --------------------------------- | ------------------------------------- | ------------------------------------------------------------- |
| `research.facebook-company-ads@1` | the company's Page                    | The ads that one company runs.                                |
| `research.facebook-ad-search@1`   | `{ "kind": "query", "query": "..." }` | The ads that match a keyword, for example a product category. |

A keyword search also takes `filters.media_type` (`ALL`, `IMAGE` or `VIDEO`) and `filters.sort_by` (`total_impressions` or `relevancy_monthly_grouped`). A live fetch costs 1 credit. A fresh cache hit costs nothing, and `cache_disposition` reports which one happened.

**Workflows (11).**

| Tool                            | REST operation                           | Mark |
| ------------------------------- | ---------------------------------------- | ---- |
| `roasos_analyze_ad`             | `POST /v1/workflows/analyze`             | paid |
| `roasos_get_ad_analysis`        | `GET /v1/workflows/analyses/{id}`        | read |
| `roasos_get_ad_analysis_status` | `GET /v1/workflows/analyses/{id}/status` | read |
| `roasos_get_ad_analysis_result` | `GET /v1/workflows/analyses/{id}/result` | read |
| `roasos_recast_video`           | `POST /v1/workflows/recast`              | paid |
| `roasos_extend_video`           | `POST /v1/workflows/extend`              | paid |
| `roasos_get_extend_video`       | `GET /v1/workflows/extend/{id}`          | read |
| `roasos_static_to_video`        | `POST /v1/workflows/static-to-video`     | paid |
| `roasos_remix_ad`               | `POST /v1/workflows/remix`               | paid |
| `roasos_statics`                | `POST /v1/workflows/statics`             | paid |
| `roasos_extract_brand`          | `POST /v1/workflows/extract-brand`       | paid |

An analysis costs 1 credit for a still and 3 credits for a video. A brand extraction costs 2 credits. Refer to [Workflows](/workflows).

**Agent runs (6).**

| Tool                            | REST operation                                     | Mark       |
| ------------------------------- | -------------------------------------------------- | ---------- |
| `roasos_agent_ask`              | `POST /v1/agent/completions`                       | paid       |
| `roasos_get_agent_run`          | `GET /v1/agent-runs/{id}`                          | read       |
| `roasos_get_agent_run_status`   | `GET /v1/agent-runs/{id}/status`                   | read       |
| `roasos_get_agent_run_result`   | `GET /v1/agent-runs/{id}/result`                   | read       |
| `roasos_get_agent_run_events`   | `GET /v1/agent-runs/{id}/events`                   | read       |
| `roasos_agent_decline_approval` | `POST /v1/agent-runs/{id}/gates/{gate_id}/decline` | free write |

**Skills (3).**

| Tool                 | REST operation              | Mark |
| -------------------- | --------------------------- | ---- |
| `roasos_list_skills` | `GET /v1/skills`            | read |
| `roasos_get_skill`   | `GET /v1/skills/{id}`       | read |
| `roasos_run_skill`   | `POST /v1/skills/{id}/runs` | paid |

`roasos_list_skills` also lists the roasOS production guides. A guide has an id that starts with `guide:`. Its `requires` states the MCP tools and host features that it needs. Read a guide with `roasos_get_skill`. You read a guide. You cannot run it. Send `kind: guide` or `kind: saved` to narrow the list.

**Voices (4).**

| Tool                   | REST operation            | Mark |
| ---------------------- | ------------------------- | ---- |
| `roasos_list_voices`   | `GET /v1/voices`          | read |
| `roasos_search_voices` | `GET /v1/voices/search`   | read |
| `roasos_design_voice`  | `POST /v1/voices/designs` | paid |
| `roasos_create_voice`  | `POST /v1/voices`         | paid |

`roasos_search_voices` finds voices by purpose, in the voice library and in your workspace. `roasos_design_voice` makes one speech sample from a written brief. To keep the voice, send the URL of the sample to `roasos_create_voice`. `noise_reduction: true` cleans the sample for a MiniMax clone. roasOS has no tool that removes noise from a speech file. Deleting a voice stays a REST operation.

**Saved edits (5).**

| Tool                  | REST operation                 | Mark       |
| --------------------- | ------------------------------ | ---------- |
| `roasos_create_edit`  | `POST /v1/edits`               | free write |
| `roasos_list_edits`   | `GET /v1/edits`                | read       |
| `roasos_inspect_edit` | `GET /v1/edits/{id}`           | read       |
| `roasos_change_edit`  | `POST /v1/edits/{id}/commands` | free write |
| `roasos_render_edit`  | `POST /v1/edits/{id}/render`   | free write |

A saved edit is a stored cut with versions. A batch of commands applies whole or not at all. Rendering the same version returns the same job. Refer to [Saved edits](/concepts/saved-edits).

**The brain (10).**

| Tool                                 | REST operation                          | Mark               |
| ------------------------------------ | --------------------------------------- | ------------------ |
| `roasos_get_brand_kit`               | `GET /v1/brand-kit`                     | read               |
| `roasos_update_brand_kit`            | `PUT /v1/brand-kit`                     | free write, merges |
| `roasos_list_memory_pins`            | `GET /v1/memory/pins`                   | read               |
| `roasos_create_memory_pin`           | `POST /v1/memory/pins`                  | free write         |
| `roasos_get_marketing_brief`         | `GET /v1/memory/brief`                  | read               |
| `roasos_update_marketing_brief`      | `PUT /v1/memory/brief`                  | free write, merges |
| `roasos_get_intelligence_context`    | `GET /v1/intelligence/context`          | read               |
| `roasos_list_intelligence_documents` | `GET /v1/intelligence/documents`        | read               |
| `roasos_get_intelligence_document`   | `GET /v1/intelligence/documents/{slug}` | read               |
| `roasos_refresh_intelligence`        | `POST /v1/intelligence/refresh`         | free write         |

These ten are the brain families: the brand kit, the memory and the intelligence. The two update tools merge. A field that you omit keeps its value. `jtbd_forces`, `colors` and `fonts` merge for each role, so send only the role that you change. `roasos_extract_brand` fills the brand kit and belongs to Workflows, because it spends. Refer to [Brand](/brand).

**Model endpoints (77).**

Each model endpoint carries one tool. Every one of them carries the paid mark. The name is `roasos_`, then the family, then the model id. Each `-` and each `.` becomes `_`.

* `roasos_image_nano_banana_pro` calls `POST /v1/image/nano-banana-pro`.
* `roasos_video_kling_4` calls `POST /v1/video/kling-4`.
* `roasos_assembly_compose` calls `POST /v1/assembly/compose`.

| Family     | Tools | Path prefix       |
| ---------- | ----- | ----------------- |
| Image      | 6     | `/v1/image/`      |
| Image edit | 4     | `/v1/image-edit/` |
| Scene      | 1     | `/v1/scene/`      |
| Avatar     | 1     | `/v1/avatar/`     |
| Video      | 16    | `/v1/video/`      |
| Video edit | 7     | `/v1/video-edit/` |
| UGC        | 8     | `/v1/ugc/`        |
| Lipsync    | 6     | `/v1/lipsync/`    |
| Audio      | 8     | `/v1/audio/`      |
| Dub        | 3     | `/v1/dub/`        |
| Assembly   | 9     | `/v1/assembly/`   |
| Captions   | 2     | `/v1/captions/`   |
| Reframe    | 2     | `/v1/reframe/`    |
| Music      | 4     | `/v1/music/`      |

Call `roasos_get_catalog` for the model ids, the units and the credit examples. Its `roles` list gives the default model for each job, such as identity-locked people, static ads or a music bed. Models change, so choose by job from `roles` and not by a model name you remember. You can always name another catalog model. Each model also has its own page, such as [Kling 4.0](/models/video/kling-4).

## The tools that roasOS does not bind

Three actions have no tool. Each one is absent by decision.

| Action              | REST operation                                     | Why                                                                                                                                            |
| ------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Delete a memory pin | `DELETE /v1/memory/pins/{id}`                      | A connected model may write memory and must never erase it. `roasos_delete_memory_pin` is declared, and it is never bound.                     |
| Approve an approval | `POST /v1/agent-runs/{id}/gates/{gate_id}/approve` | A connected model may refuse spend and must never approve its own. There is no approve tool.                                                   |
| Cancel an agent run | `POST /v1/agent-runs/{id}/cancel`                  | A connected model must not stop a run that a person started. There is no agent-run cancel tool. To cancel a job, refer to `roasos_cancel_job`. |

Refer to [Memory](/brand/memory) for the pin that only your own software can delete.

The manifest declares seven more tool names. The server binds none of them.

| Tool                       | Why                                                                                         |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `roasos_delete_generation` | A connected model may write and must never erase, so it is never bound.                     |
| `roasos_delete_scene`      | A connected model may write and must never erase, so it is never bound.                     |
| `roasos_delete_avatar`     | A connected model may write and must never erase, so it is never bound.                     |
| `roasos_delete_product`    | A connected model may write and must never erase, so it is never bound.                     |
| `roasos_create_scene`      | The paid scene door is not published to MCP, so it is not bound. Use a model-endpoint tool. |
| `roasos_update_product`    | The product write door is not published to MCP, so it is not bound.                         |
| `roasos_delete_voice`      | The voice family works over REST. Its MCP tool is planned, and it is not bound.             |

Every delete in the two tables follows one rule: a connected model may write, and it must never erase. A delete stays a REST operation, under your key and your own software.

## Money

### One value, on every paid call

Send either `quote_only: true` for a price, or a `quote_id` to confirm a price.

A paid call with neither `quote_only` nor a `quote_id` answers `quote_required`. That message states that the tool never auto-approves spend. The refusal happens before the action starts, so it does not charge.

The server picks the `Idempotency-Key` of each paid call for you. You do not send one.

* Each `quote_only: true` call gets a new key. The server keeps that key with the `quote_id` while the quote is valid.
* A confirm with that `quote_id` uses the same key. A retry of the confirm therefore never charges twice.
* To run the same input again, quote it again. The new quote gets a new key.

A confirm must carry the input of its quote. Only `webhook_url` and `wait_timeout_seconds` can change. A confirm that changes another field answers `quote_input_changed`. Quote the new input with `quote_only: true`.

A tool error carries `isError: true` and the REST error body. Every error carries a `request_id`. Give it to support when you report a problem.

```json
{ "error": { "code": "quote_required", "message": "...", "retryable": false, "request_id": "..." } }
```

| Code                        | Cause                                                                         | What to do                                     |
| --------------------------- | ----------------------------------------------------------------------------- | ---------------------------------------------- |
| `quote_required`            | A paid call with neither `quote_only` nor a `quote_id`                        | Quote first.                                   |
| `quote_input_changed`       | A confirm changes a field of its quote. `fields` names each changed field.    | Send the quoted input, or quote the new input. |
| `mcp_key_store_unavailable` | The server could not read or write the key of a quote. `retryable` is `true`. | Call the tool again.                           |
| `insufficient_scope`        | The connection has no grant for the scope that the tool needs                 | Reconnect roasOS and grant the scope.          |

An error that the server can repair with a new quote also carries `next_step`. It tells the model which call to make next.

A grant with `api:read` only lists the read tools. A write tool or a paid tool is not in `tools/list` for that connection.

<Callout type="info">
  Do not set an `Idempotency-Key` header in your client configuration. The server does not need it. One fixed header, or no header at all (an OAuth connector), works for any number of paid calls.
</Callout>

A read tool and a free write tool need neither value. The server checks the paid mark first, and a free tool never asks for a quote.

### What a paid tool gives back

Three shapes, and three read paths. Do not merge them.

| Tool                                                                        | It returns   | Read it with                    |
| --------------------------------------------------------------------------- | ------------ | ------------------------------- |
| `roasos_analyze_ad`, `roasos_extract_brand`                                 | an analysis  | `roasos_get_ad_analysis_result` |
| `roasos_recast_video`, `roasos_static_to_video`, a model-endpoint tool      | a job        | `roasos_get_job_result`         |
| `roasos_agent_ask`, `roasos_remix_ad`, `roasos_statics`, `roasos_run_skill` | an agent run | `roasos_get_agent_run_result`   |

To wait for jobs, call `roasos_wait_for_jobs` after you start the generations. Give it up to 12 job ids in `ids`. It returns when all the jobs are finished, or after `timeout_seconds` (default 50, maximum 55). When `all_terminal` is `false`, call it again with the ids that are not finished. Then read each result with `roasos_get_job_result`. Do not poll `roasos_get_job_status` for each job.

Each family has its own status words. Refer to [Jobs](/concepts/jobs) for the job words, and to [Runs](/agent/runs) for the run words.

### The approval card

An agent run proposes each paid generation before it starts one. The proposal opens an **approval** on the run, and the work waits there.

The run approves the generation itself when both of these hold:

1. The price is inside the credits that remain in `generation_spend_cap_credits`.
2. The price is inside `auto_approve_up_to_credits` on your key.

The run then writes a `gate_auto_approved` event and continues. In every other condition the approval waits in `pending_approvals`, and roasOS charges nothing for it.

One approval card carries at most eight quotes. A storyboard that compiles to more than eight requests waits for a person.

### Approving spend is never a tool

A connected model can **decline** an open approval with `roasos_agent_decline_approval`. It cannot approve one.

* A `roasos_agent_ask` run has a thread in roasOS. A person approves its paid action in the roasOS app, on the card in that thread. The `approve_url` of the approval names the thread.
* A skill run or a workflow run (`roasos_run_skill`, `roasos_remix_ad`, `roasos_statics`) has no thread, so it has no card. Its `approve_url` is the REST approve path.
* Your own software approves any run over REST, with `POST /v1/agent-runs/{id}/gates/{gate_id}/approve`. That call carries your key and obeys the spend policy of your key.

The approve operation stays out of the tool list on purpose. A connected model therefore cannot turn a review step into a rubber stamp. Refer to [Spend caps and approvals](/agent/ask-the-agent#spend-caps-and-approvals).

### The caps and the policy

An agent run carries two caps, and neither one replaces the other. `max_credits` bounds the tokens that the run's own thinking uses. `generation_spend_cap_credits` bounds the paid work that the run starts without a person, and its default is 0. A quote returns the sum of the two as a ceiling. roasOS reserves that ceiling, meters the true cost, and refunds the difference.

Your key holds the policy above both caps, and the policy always wins. `roasos_get_limits` reads it: `max_credits_per_request`, `auto_approve_up_to_credits`, `daily_credit_budget`, `monthly_credit_budget`, `allowed_models`, the credits used, and the two reset stamps. Both stamps are UTC midnight. `auto_approve_up_to_credits` is the value that unlocks an unattended run. A key at 0 starts no run at all: the run tools answer `402 approval_required`, and nothing charges. Refer to [Quotes and credits](/concepts/quotes-and-credits).

## The twelve pack skills

The fastest work an agent can start is a pack skill. `roasos_run_skill` takes an id, or one of these `roasos:` slugs.

| Slug                      | What it does                                                                                                          | Typed door                   |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------- |
| `roasos:analyze-ad`       | Breaks one ad into shots, transcript, character sheet, style and insights.                                            | —                            |
| `roasos:remix-ad`         | Analyzes one ad, storyboards the same structure with your product and your person, and renders it.                    | `POST /v1/workflows/remix`   |
| `roasos:discover-format`  | Reads a product page, finds the ad format in the Discover library that fits, then drafts and prices the storyboard.   | —                            |
| `roasos:localize-ad`      | Re-voices one finished ad in up to eight languages, all quoted on one card.                                           | —                            |
| `roasos:statics-factory`  | Turns one product into a set of static ads with a headline, a sub-head, a call to action, brand colours and the logo. | `POST /v1/workflows/statics` |
| `roasos:clone-static`     | Rebuilds the layout of a reference static for your product, with your brand kit and new copy.                         | —                            |
| `roasos:recast-actor`     | Reads the person in an ad, casts a replacement, and recasts the video.                                                | —                            |
| `roasos:street-interview` | Makes a vox-pop UGC ad: a stranger's honest answer sells the product.                                                 | —                            |
| `roasos:animated-story`   | Makes a claymation or animated story ad: a personified problem loses to the product.                                  | —                            |
| `roasos:song-ad`          | Makes a song ad: the product name, one benefit and the call to action are sung.                                       | —                            |
| `roasos:product-effects`  | Makes a product effect clip or ad with no person, with the label kept readable.                                       | —                            |
| `roasos:tiktok-slideshow` | Makes a TikTok photo slideshow: 5 to 8 slides with one soft product plug.                                             | —                            |

Every skill run is one agent run, so the caps, the approvals and the read paths above hold. Two of the twelve also have a typed door. `roasos_remix_ad` and `roasos_statics` start the same work from a typed body. Refer to [Skills](/agent/skills).

## Limitations

* The server keeps no session. There is no SSE stream and no stdio transport. A `GET` or `DELETE` on `/mcp` answers `405`.
* No tool deletes a record. Refer to [The tools that roasOS does not bind](#the-tools-that-roasos-does-not-bind). One tool removes data as a side effect: `roasos_create_memory_pin` drops the oldest pin in a category when the category has more than fifty pins.
* One connection covers at most 25 workspaces. A workspace that you make after you connect is not in the connection. Connect again to add it.
* A connected model cannot approve spend. A paid tool needs a quote and then a confirm with its `quote_id`.
* A tool answer is JSON text. The tools do not declare an `outputSchema`. Only `roasos_show_media_job` also sends `structuredContent`, for its media viewer.
* A connection has the rate limit, the spend policy and the credits of its workspace key. Refer to [Authentication](/authentication).
* A workspace holds at most 10 active API keys. Each MCP connection uses one in each of its workspaces. When a new connection finds the workspace full, it revokes the least recently used MCP key of yours in that workspace, and the connection that held it.
* Long jobs do not finish inside one call. Wait with `roasos_wait_for_jobs`, then read the result.
* `roasos_import_social_post` refuses files over 16 MiB.
* The four `roasos_generate_*` tools are deprecated. Their REST operation has a `Sunset` of 30 November 2026. Use a model-endpoint tool.

## Troubleshooting

| What you see                                                         | Cause                                                                                                    | Do this                                                                                  |
| -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `401 invalid_token` on every call                                    | The connection was revoked or has expired.                                                               | Remove the connector and connect again.                                                  |
| `workspace_required`                                                 | The connection has more than one workspace, and a tool that writes or spends did not get `workspace_id`. | Call `roasos_list_workspaces`, then send the `workspace_id`.                             |
| `workspace_not_connected`                                            | The `workspace_id` is not in this connection, or its key was revoked.                                    | Use an id from `roasos_list_workspaces`, or connect again and select the workspace.      |
| "Only owners and admins can connect"                                 | You are a member of the workspace, not an owner or admin.                                                | Ask an owner to connect, or use a workspace you own.                                     |
| "This workspace already holds the maximum number of active API keys" | The workspace has 10 active keys, and none of them is an MCP key of yours.                               | Revoke one key in workspace settings, then connect again.                                |
| "The connection could not be completed"                              | The browser blocked the approval, or the network failed.                                                 | Reload the consent page and try again. If it fails again, contact support with the time. |
| Only read tools are listed, or `insufficient_scope`                  | The connection has only `api:read`.                                                                      | Connect again and approve write access.                                                  |
| `quote_required`                                                     | A paid tool was called without `quote_only` or `quote_id`.                                               | Call it with `quote_only: true` first.                                                   |
| `quote_input_changed`                                                | The confirm input is not the quoted input.                                                               | Send the exact quoted input with its `quote_id`, or quote again.                         |
| Not enough credits                                                   | The workspace balance is lower than the quote.                                                           | Add credits in ROASOS, then quote again.                                                 |
| `400` on `http://mcp.roasos.com`                                     | The request used plain http.                                                                             | Use `https://mcp.roasos.com/mcp`.                                                        |

Each error carries a `request_id`. Send it to support when you report a problem.

## Data handling

* **What a tool reads and writes.** A tool reads and writes only the workspaces that you connected. It uses the same REST operation and the same records as the ROASOS app.
* **What the server logs.** Each MCP request writes one `api_requests` row: the method, the tool name, the status, the duration, the IP address and the user agent. A tool call also writes the row of the REST operation that it runs. That row has the REST path, which can contain the id of a record, for example `/v1/jobs/<id>`. No row contains the tool arguments or the results.
* **Your conversation.** The server receives only the tool calls that the client sends. It does not receive or store the rest of your conversation. A prompt that you send to `roasos_agent_ask` is stored as a message in a ROASOS project, as it is in the app.
* **Credentials.** The OAuth provider keeps no token in plain text. For the API key that a grant makes, ROASOS stores a SHA-256 hash and a short display prefix (the first 8 and the last 4 characters), never the key. No tool returns a key or a secret.
* **Quotes.** The server keeps the link between a quote and its confirm for the life of the quote, at most 24 hours.
* **Media.** Media that you generate or import is stored in your workspace, the same as media from the app.
* **Third parties.** A paid tool sends its input to the model provider that runs it. Three free tools also reach outside ROASOS: `roasos_import_social_post` (the social platform), `roasos_create_upload` (a public URL that you give) and `roasos_create_product` (an App Store listing). These tools have `openWorldHint: true`.

Refer to the [Privacy Policy](https://www.roasos.com/privacy) and the [Terms](https://www.roasos.com/terms).

## Your first five minutes

1. **Connect.** Run the `claude mcp add` command above, or paste the `mcpServers` block into your client.
2. **List the tools.** Type `/mcp` in Claude Code, or call `tools/list`. Every tool name starts with `roasos_`.
3. **Read the brand.** Ask for `roasos_get_brand_kit` and `roasos_get_intelligence_context`. Both are free, and both tell your agent who the workspace sells to.
4. **Analyze an ad.** Ask your agent to quote `roasos_analyze_ad` on one ad URL. Confirm the quote with its `quote_id`, poll `roasos_get_ad_analysis_status`, then read `roasos_get_ad_analysis_result`. The price is 1 credit for a still and 3 credits for a video.
5. **Quote a generation.** Read `roles` in `roasos_get_catalog`, pick the model for your job, and ask for its tool with `quote_only` set to `true`. Read the `credits` field. Stop there. Nothing spends until you send the `quote_id` back.

Then hand the whole job to the agent. `roasos_agent_ask` plans the work, chooses the models, and calls these same tools under your caps. Refer to [Agent](/agent).
