> ## Documentation Index
> Fetch the complete documentation index at: https://docs.api.rask.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Rask MCP server: connect, tools and confirmation

> Connect Claude Code, Codex CLI, VS Code, Cursor or Claude Desktop to Rask's hosted MCP server by signing in through your browser or with an API key: its tools, how spending is confirmed, long jobs, uploads and limits.

> [!NOTE]
>
> **Not on in production yet.** Rask has not switched this server on at `api.rask.ai`, so the steps below do not work with the
> production API today. This page describes its first release. `GET /v3` says when it is on: its `mcp.url` then holds the server's
> address. Until then, use [the ready prompt and the REST API](https://docs.api.rask.ai/v3/agents.md).

The Rask MCP server lets an AI agent localize video and audio with Rask: import media, translate, dub, lip-sync, translate
on-screen text and download the results.

It is hosted at `https://api.rask.ai/v3/mcp`, so there is nothing to install or update, and only Claude Desktop with a key needs
Node. You connect by signing in through your browser, or with an API key for an agent that runs unattended. Your MCP client then
sends that credential on every request, and each tool calls the public API v3 with it like any other client. So the access and
monthly limits you chose, your plan and the API's limits all apply.

The steps below cover Claude Code, Codex CLI, VS Code, Cursor and Claude Desktop.

## Connect

Sign in when a person is at the keyboard: it takes one approval in the browser and no key. Use a key for an agent that runs
unattended or in CI, where nobody is there to approve.

**Settings -> API keys** also shows these snippets, with the address of its environment.

### Sign in

Follow the steps for your client, then approve in the browser.

> [tab] Claude Code
>
> ```bash theme={null}
> claude mcp add --transport http rask https://api.rask.ai/v3/mcp
> claude mcp login rask
> ```
>
> The first line only adds the server, and it opens no browser. The second line opens the browser, and it needs a terminal you can
> type in. Inside a session, `/mcp` is the other way in.

> [tab] Codex CLI
>
> ```bash theme={null}
> codex mcp add rask --url https://api.rask.ai/v3/mcp
> ```
>
> This opens the browser by itself. If it says "may or may not require login", run `codex mcp login rask`.

> [tab] VS Code
>
> ```bash theme={null}
> code --add-mcp '{"name":"rask","type":"http","url":"https://api.rask.ai/v3/mcp"}'
> ```
>
> Then start the server in VS Code and sign in when it asks.

> [tab] Cursor
>
> Cursor has no such command, so add this to `~/.cursor/mcp.json`. The file holds no key.
>
> ```json theme={null}
> {
>   "mcpServers": {
>     "rask": { "url": "https://api.rask.ai/v3/mcp" }
>   }
> }
> ```
>
> Then start the sign-in from the server's entry in Cursor's MCP settings. In the Cursor CLI, the command is
> `agent mcp login rask`.

> [tab] Claude Desktop
>
> Open **Settings**, then **Connectors**, choose **Add custom connector** and paste `https://api.rask.ai/v3/mcp`. This way needs no
> Node.
>
> This client has no shell, so [it imports media by link only](#uploads).

The browser opens a Rask page. Sign in to Rask if you are not signed in yet. The page names the app and says where Rask will send
you after you approve. The name is what the app says about itself, and Rask cannot verify it, so go on only if you started this
yourself. Choose the teamspace, the access, monthly limits if you allow spending, and when the approval expires. Then click
**Approve**.

The approval appears under **Settings -> API keys**, in **Connected apps**, where you can limit it or revoke it. Once it has expired
or you have revoked it, sign in again to reconnect.

An approval works like a key on the rest of this page. Its access sets the scopes, its monthly limits are the key's caps, and where
the page says "the key" it also means your approval. It works only through this server: the REST API still needs a key.

### With a key

[Create a key](https://app.rask.ai/account/api-keys) under **Settings -> API keys**. For an agent that runs unattended, choose
a capped or "without spending" key, as [Choose the key](https://docs.api.rask.ai/v3/agents.md#choose-the-key) explains. Then put the key in the environment
as `RASK_API_KEY`.

Only Claude Desktop's snippet holds the key in a file: the others read it from the environment or ask you for it.

> [tab] Claude Code
>
> ```bash theme={null}
> claude mcp add --transport http rask https://api.rask.ai/v3/mcp \
>   --header 'Authorization: Bearer ${RASK_API_KEY}'
> ```
>
> Keep the single quotes: `~/.claude.json` then holds the reference, and Claude Code reads the variable when it connects. With
> double quotes your shell writes the key itself into that file.
>
> Claude Code does not document this reading of a variable for `--header`. If the server answers 401 and says the header holds
> a variable's name, see [Troubleshooting](#troubleshooting).

> [tab] Codex CLI
>
> ```bash theme={null}
> codex mcp add rask --url https://api.rask.ai/v3/mcp \
>   --bearer-token-env-var RASK_API_KEY
> ```
>
> Codex takes the key from `RASK_API_KEY` when it connects, so its config holds only the variable's name.

> [tab] VS Code
>
> Add this to `.vscode/mcp.json`. VS Code asks for the key when the server first starts and keeps it out of the file.
>
> ```json theme={null}
> {
>   "inputs": [{ "type": "promptString", "id": "rask-api-key", "description": "Rask API key", "password": true }],
>   "servers": {
>     "rask": {
>       "type": "http",
>       "url": "https://api.rask.ai/v3/mcp",
>       "headers": { "Authorization": "Bearer ${input:rask-api-key}" }
>     }
>   }
> }
> ```

> [tab] Cursor
>
> Add this to `~/.cursor/mcp.json`. Cursor reads the key from `RASK_API_KEY` when it connects, so the file holds only the
> reference.
>
> ```json theme={null}
> {
>   "mcpServers": {
>     "rask": {
>       "url": "https://api.rask.ai/v3/mcp",
>       "headers": { "Authorization": "Bearer ${env:RASK_API_KEY}" }
>     }
>   }
> }
> ```

> [tab] Claude Desktop
>
> With a key, Claude Desktop reaches a hosted server through a bridge, `mcp-remote`, so this way needs Node. Add this to
> `claude_desktop_config.json` and put your key in place of the placeholder:
>
> ```json theme={null}
> {
>   "mcpServers": {
>     "rask": {
>       "command": "npx",
>       "args": [
>         "-y",
>         "mcp-remote@0.16.0",
>         "https://api.rask.ai/v3/mcp",
>         "--header",
>         "Authorization:${RASK_AUTH_HEADER}"
>       ],
>       "env": { "RASK_AUTH_HEADER": "Bearer <your API key>" }
>     }
>   }
> }
> ```
>
> The key sits in that file as plain text, so give this client a key of its own with a low monthly cap. Write no space after
> `Authorization:`: that is the bridge's workaround for a Windows argument bug.
>
> This client has no shell, so [it imports media by link only](#uploads).

### Check the connection

Ask the agent to call `rask_get_account`. It names the workspace, the plan, the balance and what the key may still spend. If it
fails, see [Troubleshooting](#troubleshooting).

## Environment

| Variable | Required | Meaning |
| - | - | - |
| `RASK_API_KEY` | Only with a key | The API key. Your client reads it and sends it as `Authorization: Bearer <key>` on every request. |

Signing in needs no variable. Claude Desktop does not read it either: its key is in the config file. The server never sees the
variable, only the header your client builds from it. It keeps no session and never logs a key or a token. Use the address
**Settings -> API keys** shows: a key sent to the address of another environment answers
[`token_wrong_environment`](https://docs.api.rask.ai/errors/token_wrong_environment).

## Tools

The client lists only the tools the key's scopes allow, so a key without `credits:spend` shows no spending tool. The scope rules
of [Authentication](https://docs.api.rask.ai/authentication.md#scopes) apply: `X:write` implies `X:read`, and every `*:write` implies `jobs:read`. The list
does not look at the plan, so a listed tool can still answer [`feature_not_in_plan`](https://docs.api.rask.ai/errors/feature_not_in_plan).

| Tool | What it does | Scopes | Effect |
| - | - | - | - |
| `rask_get_account` | Get account, plan and balance | `account:read` | Read-only |
| `rask_get_usage` | Get usage | `account:read` | Read-only |
| `rask_list_languages` | List languages | Any valid key | Read-only |
| `rask_create_upload` | Create a file upload | `media:write` | Changes data |
| `rask_import_media` | Import media | `media:write` | Changes data |
| `rask_create_project` | Create localization projects | `projects:write`, `credits:spend`; by request `lipsync:write`, `onscreen:write` | Spends credits, with a [confirmation](https://docs.api.rask.ai/v3/mcp.md#how-spending-is-confirmed) |
| `rask_list_projects` | List projects | `projects:read` | Read-only |
| `rask_get_project` | Get project | `projects:read` | Read-only |
| `rask_get_download_links` | Get download links | `projects:read` | Read-only |
| `rask_get_transcript` | Read transcript | `transcripts:read` | Read-only |
| `rask_edit_transcript` | Edit transcript | `transcripts:write` | Changes data, with the current `etag` |
| `rask_retranslate_segments` | Retranslate segments | `transcripts:write` | Changes data |
| `rask_list_versions` | List transcript versions | `transcripts:read` | Read-only |
| `rask_save_version` | Save transcript version | `transcripts:write` | Changes data |
| `rask_restore_version` | Restore transcript version | `transcripts:write` | Changes data, with a [confirmation](https://docs.api.rask.ai/v3/mcp.md#how-spending-is-confirmed) |
| `rask_get_voice_setup` | Get speakers and voices | `voices:read` | Read-only |
| `rask_list_voices` | List voices for a language | `voices:read` | Read-only |
| `rask_set_speaker_voice` | Set a speaker's voice | `voices:write` | Changes data, with the current `etag` |
| `rask_create_voice_clone` | Create a voice clone | `voices:write` | Changes data, once the user confirms in a [dialog](https://docs.api.rask.ai/v3/mcp.md#clients-without-a-dialog) |
| `rask_estimate_cost` | Estimate cost | Any of `projects:write`, `dubbing:write`, `lipsync:write`, `onscreen:write` | Read-only |
| `rask_start_dub` | Dub or redub | `dubbing:write`, `credits:spend` | Spends credits, with a [confirmation](https://docs.api.rask.ai/v3/mcp.md#how-spending-is-confirmed) |
| `rask_start_lipsync` | Lip-sync video | `lipsync:write`, `credits:spend` | Spends credits, with a [confirmation](https://docs.api.rask.ai/v3/mcp.md#how-spending-is-confirmed) |
| `rask_start_onscreen` | Translate on-screen text | `onscreen:write`, `credits:spend` | Spends credits, with a [confirmation](https://docs.api.rask.ai/v3/mcp.md#how-spending-is-confirmed) |
| `rask_start_localization` | Dub, lip-sync and on-screen as one job | `dubbing:write`, `credits:spend`; by request `jobs:write`, `lipsync:write`, `onscreen:write` | Spends credits, with a [confirmation](https://docs.api.rask.ai/v3/mcp.md#how-spending-is-confirmed) |
| `rask_generate_segments` | Voice chosen segments into clips | `dubbing:write`, `credits:spend` | Spends credits, with a [confirmation](https://docs.api.rask.ai/v3/mcp.md#how-spending-is-confirmed) |
| `rask_get_job` | Get job status | `jobs:read` | Read-only |
| `rask_cancel_job` | Cancel job | `jobs:write` | Changes data |
| `rask_api_operations` | Find API operations | Any valid key | Read-only |
| `rask_api_read` | Call a read-only API operation | Any valid key | Read-only |

`rask_api_operations` finds any operation of the REST API, and `rask_api_read` calls the GET ones, for which the key needs the
scopes of that operation. Writes these tools do not cover stay in [the REST API](https://docs.api.rask.ai/v3/concepts/operation-table.md). No tool buys
anything: top-ups and plan changes exist only in the web app.

### Prompts and subtitles

The client lists a prompt only when the key can see the tools it names.

| Prompt | What it walks through | Arguments |
| - | - | - |
| `localize_video` | Import a video by URL, quote it and create dubbed projects in one or more target languages, up to the download links. | `source_url`, `target_languages`, `auto_dub` (optional) |
| `edit_and_redub` | Read a project's transcript, apply changes after showing them, and redub the changed segments for a quoted price. | `project_id`, `instructions` (optional) |
| `lipsync_video` | Check a dubbed project for a face and a free run, quote the lip-sync and start it, up to the download link. | `project_id`, `model` (optional) |

Subtitles are also a resource, `rask://projects/{project_id}/subtitles/{language}.{format}`, of at most 2 MB: the language is
`source` or `target`, and the format `srt` or `vtt`. That keeps them out of the conversation until someone asks for them, and
`rask_get_download_links` gives the address of each one that exists.

## How spending is confirmed

A spending tool acts only with a confirmation. `rask_restore_version` asks the same way, without a price.

**1. The agent gets a quote**

It calls `rask_estimate_cost` with the tool's name in `for_tool` and that tool's `arguments`. This is free and changes
nothing. The answer holds the cost, the balance after it, what the key's monthly cap leaves and any blocking `reasons`. When
the action can run, it also holds a `confirmation`: a `token`, a `summary` and the values to approve.

A spending tool called without a token answers the same way, with `status: confirmation_required`, or with
`status: not_eligible` and the estimate's `reasons` when the action cannot run now.

**2. The agent calls the tool with the same arguments**

It adds `confirmation_token` and `approved_cost_minutes`, and `approved_projects` on `rask_create_project`. These are tool
arguments, so a client that asks permission for tool calls shows the price in its prompt.

**3. The server checks, then charges at most the quote**

The token holds for this key, tool and arguments, for 10 minutes. Anything else answers `confirmation_invalid`,
`confirmation_expired` or `confirmation_mismatch`. The request to the API sends the quote as `expected_cost_minutes`, so a
higher price fails with [`cost_changed`](https://docs.api.rask.ai/errors/cost_changed) instead of being charged. A later step priced above the quote,
such as the auto-dub after translation or a stage of a localization run, is skipped and not charged. A skipped stage fails
its run.

Of the clients above, only Claude Code shows you a dialog before it spends. Codex CLI, VS Code, Cursor and Claude Desktop show
none: there, you are asked only if the client asks permission for tool calls, and what bounds the money is the monthly caps of
the key or the approval.

The dialog comes from the server. A client on the 2026-07-28 revision of MCP that supports form elicitation gets it: the server
asks the user on every such call, with a token or without one. The model cannot answer that dialog. A no returns
`status: declined`, and nothing runs. A client on a 2025 revision has no dialog here, whatever it supports elsewhere, because it
declares what it supports only when it connects, and this server keeps no session. Of the clients above, Claude Code is on the
2026-07-28 revision.

### Clients without a dialog

A client without a dialog, signed in or with a key, can read what the key may read, quote any action, upload, import, edit, cancel
and spend with a confirmation. Two things are refused, because no person can be asked:

* **Spending with no monthly cap.** A call that costs minutes needs a monthly cap on minutes, and a call that costs project
  credits needs one on project credits. Without it the call answers `spend_cap_required` and names the cap that is missing,
  `spend_limit_minutes_per_month` or `spend_limit_projects_per_month`, and `rask_estimate_cost` says the same in `reasons`, at no
  cost. A sign-in that allows spending starts with both limits, so this reaches it only if you clear one later. Set the cap under
  **Settings -> API keys**, on the key or in **Connected apps**: caps can change, so you do not need a new key or a new sign-in.
  A change can take up to a minute to apply.
* **Voice clones.** `rask_create_voice_clone` answers `operation_not_allowed`. The rights statement is a person's to make, and
  `rights_confirmed` from a model does not stand in for it. Use [`POST /v3/voice-clones`](https://docs.api.rask.ai/operations/createVoiceClone) over REST, or
  a client that can ask you.

`rask_restore_version` has no price, so no monthly cap applies to it: the confirmation is enough.

### What a confirmation proves

> [!WARNING]
>
> A confirmation proves that the model saw the price, not that a person approved it. In a client that auto-approves tools no person
> stands in the way, and what bounds the money is the monthly caps of the key or the approval. Use a capped or "without spending" key
> for unattended agents.

It does prove that the server quoted exactly these arguments, for this tool and this key, within the last 10 minutes, and that the
charge in minutes will not pass that quote. It does not prove that a person read the quote or agreed to it, or that the arguments
are what you wanted: the model chose them.

Only the dialog puts a person in the path, and only in a client that has it. The limits the API enforces whatever the model
does are in [What the API enforces](https://docs.api.rask.ai/v3/agents.md#what-the-api-enforces).

A call that was lost on the way is safe to send again. While its outcome is unknown or its jobs still run, for up to 24 hours, the
same call reuses the same confirmation and replays instead of charging twice. Once those jobs have all ended, the same call is a
new request: it needs a new confirmation, and its quote names the earlier jobs in `previous_attempt`. After 24 hours it is a new
request in any case.

`rask_create_voice_clone` has its own guard. `rights_confirmed` must be true, which records that the user has the speaker's
permission to clone this voice, and the user confirms that in the dialog.

## Long jobs

Tools that start long work return a job at once. `rask_get_job` takes up to 10 `job_ids`. While none of them has ended, it
waits up to 20 seconds per call (`wait_seconds`) and returns as soon as the first job in the list changes; `changed_job_ids`
names what changed during the call. It reports `expected_remaining_seconds` and `next_check_after_seconds`. A call to any tool
ends within about 22 seconds. The server sends no progress notifications: each job's progress is in what `rask_get_job` returns.

When more than about 3 minutes remain, `yield_to_user` is true and the agent is told to give you the job ids and stop. Ask it to
check again later: finished jobs can be read for 90 days. Dubs usually take minutes, while lip-sync, on-screen jobs and
localization runs can take hours ([How long a job takes](https://docs.api.rask.ai/v3/concepts/jobs.md#how-long-a-job-takes)), so for them this is the normal
way to work.

Canceling a tool call never stops the job. `rask_cancel_job` cancels only media imports, and on-screen jobs that are still queued
and not part of a localization run; those are refunded. Other jobs, localization runs included, run to completion.

## Uploads

The server cannot read your computer. A local file goes straight to storage, from the agent's own shell, and never through the
server or the model's context:

**1. Create the upload**

`rask_create_upload` takes a `filename`, a `content_type` and a `size_bytes`, and returns one presigned `url` and a curl command.

**2. Send the file**

The agent runs that command with the local path. The request takes no `Authorization` header. Sending a large file is slow, so
the agent should run it in the background.

**3. Import it**

`rask_import_media` with the `upload_id` starts a `media_import` job, whose result holds the `media_id`.

* **What it takes.** A video or audio file of at most 5 GB, or of the plan's `max_upload_bytes` when that is lower:
  `rask_get_account` shows it under `plan.limits`.
* **Expiry.** The `url` expires after 1 hour: call `rask_create_upload` with the `upload_id` alone for a new one. The upload itself
  expires after 24 hours.
* **Voice samples.** With `purpose: voice_sample`, a sample can be audio up to 11 MB, kept as uploaded, or video up to 500 MB, whose
  audio is extracted to MP3. As stored, each sample must be 11 MB or less.

A file that is already online does not need this: `rask_import_media` takes a public link from YouTube, Google Drive, Vimeo or an
allow-listed host. **That is the only way in for a client with no shell**, such as Claude Desktop, because nothing there can run
the command. Put the file online first.

## Limits

* **One key, one workspace.** No tool takes a workspace. The workspace owner's balance pays.
* **The API's limits apply.** Plan features, rate limits and concurrent jobs work as in [Capabilities](https://docs.api.rask.ai/v3/concepts/capabilities.md).
  Every request to the server counts once against the key's rate limits, and every API call a tool makes counts as it does over REST.
* **Request size.** A request above 1 MiB answers 413 [`request_too_large`](https://docs.api.rask.ai/errors/request_too_large). Subtitles given to
  `rask_create_project` are at most 200,000 characters each, and a larger file goes through
  [`POST /v3/projects`](https://docs.api.rask.ai/operations/createProject).
* **Paid text.** A project's text stays closed until a dub of it is charged. Reading it answers
  [`dub_payment_required`](https://docs.api.rask.ai/errors/dub_payment_required), and the result names the call that starts the dub. See
  [Paid text](https://docs.api.rask.ai/v3/concepts/paid-text.md).
* **Result size.** A result stays within about 20,000 characters. Lists drop whole items and give a `next_cursor`, and never cut
  your text. A single item above the limit answers `result_too_large`.
* **Transcript edits.** `rask_edit_transcript` applies up to 50 operations in order. It is not atomic: it stops at the first
  conflict and reports what was applied. A list with a delete, merge or split, or with 10 or more operations, saves a restore
  point first. A list longer than the key's rate limit lets one call finish is refused before anything is applied, with
  `argument_invalid` and a hint that says how many operations fit: split it into calls of that size. A list that fits but stops
  partway on the rate limit finishes when the same call is sent again, and the operations already applied report `replayed`.
* **Your content is data.** Fields that hold user or media text are named in `untrusted_fields`, so the agent can treat them as
  data and never as instructions.
* **Rask can limit access.** Rask can switch MCP access to read-only, or off, and `mcp.mode` of `GET /v3` says which applies now.
  While it is read-only the client lists only read-only tools, and the others answer `mcp_disabled`. While it is off every
  request answers HTTP 503 [`api_unavailable`](https://docs.api.rask.ai/errors/api_unavailable) with `Retry-After`. A call already running may finish.

## Errors

The API's errors come through as tool errors: one line with the code, the detail and the hint, then the compact JSON of the whole
problem. The server's own errors have the same shape, with `source: rask-mcp` and no HTTP status. Each code of the API has
[a page](https://docs.api.rask.ai/errors.md). The server adds the codes below. A code without a link is the server's own, and this table is all there is
about it:

| Code | What it means |
| - | - |
| `confirmation_invalid` | Malformed, altered or issued to another key; get a new confirmation |
| `confirmation_expired` | Older than 10 minutes; estimate again |
| `confirmation_mismatch` | Tool, arguments or approved\_\* differ from the confirmed ones |
| `spend_cap_required` | No person confirms this call, and the key has no monthly cap on what it spends; set one on the key |
| `argument_invalid` | A rule the schema cannot express; names the pointer and the valid alternatives |
| `operation_not_found` | Unknown operation\_id; use rask\_api\_operations |
| `operation_not_allowed` | Method, release, tag or effect not allowed; names the tool that serves it |
| `result_too_large` | Generic read body above 20,000 characters; lower limit or filter |
| `mcp_disabled` | The MCP server is read-only for now (`mcp.mode` of GET /v3 says read\_only) and this tool changes data; try again later or call the REST API |
| `upstream_timeout` | The REST API did not answer in time (retryable; job-starting retries are deduplicated) |
| [`internal_error`](https://docs.api.rask.ai/errors/internal_error) | A bug in this server; retry once, report it if it repeats |

## Troubleshooting

* **401, with the variable's name in the request.** The variable is unset where the client runs. Claude Code and Cursor then send
  the reference as it is written, such as `Bearer ${RASK_API_KEY}`, and the answer says the header holds a variable's name. Set
  the variable where the client starts, then restart the client. An app opened from the desktop may not see a variable set only
  in a terminal. Codex CLI sends no request at all when its variable is unset.
* **Cursor says `SSE error: Non-200 status code (405)`.** The 405 is not the problem. This server does not stream, so it answers
  the GET request Cursor also sends with 405. Cursor shows that when something failed before it, usually a 401: check the key, or
  sign in.
* **Claude Code says `Needs authentication`.** The server is added, but you have not signed in. Run `claude mcp login rask`, or
  `/mcp` inside a session.
* **`/.well-known/*` and `POST /register` in the client's log.** These are the first steps of signing in, and they are normal.
  With a key, Claude Code and Codex CLI start no sign-in, so a wrong key opens no browser: Claude Code says
  `Server rejected the configured Authorization header (HTTP 401)`, and the fix is the key.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.