# Connect an AI tool with MCP

> Connect Claude, ChatGPT or another MCP client to Rootsy, then create and manage computers, run commands, read files back, and manage schedules and domains through its 29 tools.

This page covers Rootsy's MCP server: how to connect a client, the access it gets, every tool, and the limits and errors. An AI tool outside your computer can use it to create, restart, reset and delete computers, run shell commands on them, move files in and out, and manage schedules and custom domains. Any client that supports remote MCP servers with OAuth can connect.

## The server

| | |
|---|---|
| Server URL | `https://app.rootsy.io/mcp` |
| Transport | Streamable HTTP, stateless |
| Auth | OAuth 2.1 with a bearer token in the `Authorization` header |
| Resource metadata | `https://app.rootsy.io/.well-known/oauth-protected-resource/mcp` |
| Scope | `rootsy:access` |
| Largest request | 524,288 bytes (512 KiB) |

## What a connection can do

There is one permission, `rootsy:access`. It lets the client act as you, with your current Rootsy permissions. Every tool needs it. There is no read-only mode.

A connection reaches every computer in your personal account and in every organization you belong to. It follows your memberships as they change. Leave an organization and the connection loses its computers on the next call. Rootsy checks your identity, your memberships and whether MCP access is paused on every call.

A connected client can read and change any file on those computers, and it can delete them. Connect only clients you trust.

A token without `rootsy:access` gets HTTP 403 with `insufficient_scope`. Reconnect the client and approve the access it asks for.

## Connect a client

1. Add a custom MCP server (some clients call it a connector or an app) with the URL `https://app.rootsy.io/mcp`.
2. When the client opens the Rootsy sign-in page, sign in with your Rootsy account.
3. On the consent screen, approve the access the client asks for.

In Claude, add it as a custom connector. Give it a name and paste the server URL. In ChatGPT, turn on Developer mode before adding a custom MCP server. Rootsy doesn't issue client IDs or secrets. The URL is all you need.

## Pause every connection

To cut off all of your MCP connections at once, open **Account** in the app. Choose **Manage account** in the account menu, or **Settings** in the sidebar while you're in your personal account. Then choose **Pause MCP access** under **MCP access**. Every connection is refused from the next request on. That includes ones you've forgotten about and ones that refresh their token later. A call already in progress may finish. Paused calls fail with this message:

```text
Access is paused. Resume in Rootsy personal settings > MCP access. Reconnecting does not resume access.
```

Choose **Resume MCP access** to allow connections again. Resuming lets old connections work again too. If you think a token has leaked, keep access paused.

Pausing stops new calls. It doesn't stop schedules, scheduled requests already sent, or work Rootsy already accepted, such as a deletion in progress.

## The tools

Every tool except `get_identity` and `list_accounts` takes an explicit `account_id`. Tools that act on a computer also take its `computer_id`. Get both from `list_accounts` and `list_computers`.

| Group | Tools | Covered in |
|---|---|---|
| Accounts and computers | `get_identity`, `list_accounts`, `list_computers`, `get_computer` | This page |
| Computer lifecycle | `create_computer`, `restart_computer`, `reset_computer`, `delete_computer`, `get_computer_operation` | This page |
| Commands | `run_command`, `read_command`, `cancel_command` | This page |
| Files | `import_file`, `read_file_import`, `export_file` | This page |
| Schedules | `list_schedules`, `get_schedule`, `preview_schedule`, `create_schedule`, `update_schedule`, `set_schedule_enabled`, `delete_schedule`, `run_schedule`, `list_schedule_requests` | [Scheduled tasks](https://rootsy.io/docs/reference/scheduled-tasks.md#manage-schedules-over-mcp) |
| Domains | `list_domains`, `get_domain`, `connect_domain`, `verify_domain`, `remove_domain` | [Domains](https://rootsy.io/docs/reference/domains.md#manage-domains-over-mcp) |

`run_command`, the lifecycle tools, `import_file`, `create_schedule` and `run_schedule` take a `request_id`, a UUID you generate for each action you mean to take. If you don't know whether a call went through, repeat it with the same `request_id` and the same input. Rootsy returns the original result and doesn't act twice. The same `request_id` with different input fails with `request_conflict`. Never pick a new `request_id` to retry an action whose outcome is unknown.

## Read accounts and computers

These tools only read. They don't wake a computer or spend Runtime Credits.

| Tool | Input | Returns |
|---|---|---|
| `get_identity` | nothing | `user_id`, `display_name` |
| `list_accounts` | optional `limit` (1–100, default 50), optional `cursor` | `accounts[]`, `next_cursor` |
| `list_computers` | `account_id`, optional `limit` (1–100, default 50), optional `cursor` | `account`, `computers[]`, `next_cursor` |
| `get_computer` | `account_id`, `computer_id` | One computer's details |

Each account has `id`, `type` (`personal` or `organization`), `name`, `slug`, and `lifecycle` with `deleting` and `suspended` booleans. Each computer in `list_computers` has `id`, `name`, `slug`, `status`, `public_url` (its address) and `created_at`. `status` is one of `provisioning`, `active`, `stopped`, `resetting`, `restoring`, `failed`, `deleting`. When `next_cursor` isn't `null`, pass it back as `cursor` to get the next page.

`get_computer` adds `workspace_url`, `provisioning_step`, `observed_at`, `unreachable_since`, `reachability_checked_at`, `site_fault_at`, `hosting`, `capabilities`, `actions` and `simulated`. `hosting.platform_request_body_limit_bytes` is the request body limit for your apps, 1,048,576 bytes. It can be `null`. The limit is the same either way. See [Serve an app](https://rootsy.io/docs/reference/serving.md#uploads-and-request-size). `capabilities` has booleans for `commands`, `files`, `file_transfer`, `schedules` and `reset`. `files` means the workspace Files window. `file_transfer` means the MCP file tools below. `actions` says whether `restart`, `reset` and `delete` are available now, with a `reason` when they aren't. `status: "active"` doesn't mean the computer is awake. `awake` is always `null`.

## Create, restart, reset and delete computers

| Tool | Inputs besides `account_id` and `request_id` | What it does |
|---|---|---|
| `create_computer` | `name` | Creates a computer in the account. The slug comes from the name. |
| `restart_computer` | `computer_id` | Restarts the computer. Files stay. Running processes stop. |
| `reset_computer` | `computer_id`, `confirm_reset: true` | Resets the computer. It keeps `/home`, `/root` and `/srv`, stops running processes and pauses every schedule. See [Backups and Reset](https://rootsy.io/docs/reference/backups-and-reset.md). |
| `delete_computer` | `computer_id`, `confirm_slug` | Deletes the computer, its files and its backups, and removes its domains from Rootsy. `confirm_slug` must equal the computer's slug exactly. |
| `get_computer_operation` | nothing else | Reads the receipt for a lifecycle `request_id` |

Each change tool returns a receipt. Call `get_computer_operation` with the same `account_id` and `request_id` until `state` is final, waiting `poll_after_ms` between calls. Receipts last as long as the account, so you can read one after the computer is deleted.

| `state` | Meaning |
|---|---|
| `accepted`, `running` | In progress. Not finished. |
| `succeeded` | Done. For a create, the computer is ready. For a delete, the computer is gone. `cleanup_pending: true` means Rootsy is still finishing the remaining external cleanup. |
| `failed` | Didn't happen. `reason` says why. |
| `unknown` | Rootsy can't prove the outcome. Read the receipt again. Don't send a new request. |

`needs_review: true` means the operation has been unresolved for 15 minutes and Rootsy has been alerted. For a restart, `acknowledged: false` means Rootsy can't confirm the restart happened.

Creating a computer follows the account's plan and Runtime Credits. An organization without a plan gets `org_subscription_required`. A personal account without a plan or credits gets `runtime_exhausted`. Pick a plan with **Create computer** in the app first. A full account gets `box_limit_reached` with the plan's `limit` in `error.details`. Too many creations in 24 hours gets `creation_rate_limited` with `retry_after_seconds` and `retry_at`. A name that makes a bad slug fails with `too_short`, `too_long`, `invalid_format` or `reserved`, and a slug in use fails with `slug_unavailable`. [Limits](https://rootsy.io/docs/reference/limits.md) lists the slug rules.

Deleting never edits DNS at your domain provider. Remove the CNAME records yourself.

## run_command

`run_command` runs a non-interactive bash script on one computer. The command keeps running after the call returns and after your client disconnects.

| Field | Required | Limits |
|---|---|---|
| `account_id` | yes | UUID of the account that owns the computer |
| `computer_id` | yes | UUID from `list_computers` |
| `request_id` | yes | a UUID you generate, new for each command you mean to run |
| `command` | yes | 1 to 16,384 bytes of UTF-8 bash, counted after JSON decoding. No NUL bytes. |
| `cwd` | no | absolute path, up to 4,096 bytes. Default `/home/dev`. |
| `timeout_seconds` | no | 1 to 900. Default 300. |
| `completion_mode` | no | `detached` (default) or `check_once` |

With `detached`, the call returns as soon as the command starts. With `check_once`, Rootsy checks once more before answering, so a quick command can come back finished in the same call. If it's still running, the call returns `running` and the command carries on. The check never stops the command or extends its time limit. If the check itself fails, the result has `state: "unknown"` and an `observation_error.code`. The command was still accepted. Read it with `read_command` and the same `command_id`.

A command that's too long or has a bad `cwd` is refused before anything runs. To run a larger script, upload it with `import_file` or the workspace Files window, then run it with a short command.

The command runs as the `dev` user with `bash`, with no terminal and no stdin. Its environment is empty apart from:

```sh
HOME=/home/dev
USER=dev
LOGNAME=dev
PATH=/usr/local/bin:/usr/bin:/bin
LANG=C.UTF-8
```

Your shell profile and `~/.bashrc` don't load. If a tool lives somewhere else, use its full path or set `PATH` at the top of the command, for example `export PATH="$HOME/.local/bin:$PATH"`.

Save the returned `command_id`. `read_command` and `cancel_command` need it.

## read_command and cancel_command

Both take `account_id`, `computer_id` and `command_id`, and return the same fields as `run_command`.

`read_command` returns the command's current state and output. Reading again never restarts it. `cancel_command` stops the command and every process it started. Canceling a finished command changes nothing and returns its result.

## Command results

Every command tool returns `account_id`, `computer_id`, `command_id`, `request_id`, `state`, `exit_code`, `output`, `truncated`, `output_info`, `submitted_at`, `finished_at` and `simulated`.

| `state` | Meaning |
|---|---|
| `running` | Still running. Call `read_command` later. |
| `completed` | Finished. `exit_code` holds the real exit code, including non-zero failures. |
| `timed_out` | Hit its time limit and was stopped. `exit_code` is `null`. |
| `cancelled` | Stopped by `cancel_command`. `exit_code` is `null`. |
| `unknown` | Rootsy couldn't confirm what happened, usually because the computer didn't answer in time. Call `read_command` with the same `command_id`, or `run_command` with the same `request_id`. Don't start it again under a new `request_id` unless it then reads `lost`. |
| `lost` | The computer no longer has a result for this command. On its own it doesn't tell you whether the command ran or succeeded. |

Check `state` and `exit_code` together. Empty output doesn't mean success.

`output` is stdout and stderr together, as text. It holds the last 1,000 journal records, then the last 65,536 bytes of those. A long line can be split across records, and a newline can appear where your program wrote none. Output written faster than 200 lines a second can be dropped. `truncated` doesn't tell you whether anything is missing either way. `output_info` states the same limits: `max_journal_records: 1000`, `max_bytes: 65536`, `clipping: "unknown"` and `byte_preserving: false`.

Never pass JSON payloads, base64 or binary files through `output`. Use `export_file`. For a full log, write it to a file and read the end of it:

```sh
npm test > /home/dev/test.log 2>&1; result=$?; tail -n 40 /home/dev/test.log; exit "$result"
```

## Command limits

| Limit | Value |
|---|---|
| Commands running at once on one computer | 4 |
| Time limit | default 300 seconds, at most 900 |
| How long a finished result stays readable | 24 hours, unless a reset or restore removes it first |
| Results kept per computer | 1,000, running ones included |

Up to 4 commands run at once on one computer. Cancel or wait for a running command before starting another. A computer keeps at most 1,000 command results and takes new commands again once older results expire.

When a command's time limit runs out, Rootsy stops it and every process it started, background processes included, after a 2-second grace period. Work that needs more than 900 seconds can't run through `run_command` directly. [Jobs](https://rootsy.io/docs/reference/jobs.md) shows how to start it as a separate unit.

## Edit files with run_command

Only ChatGPT can send a file in with `import_file`. From other clients, write text files with shell commands through `run_command`. To read a file:

```sh
cat -n /home/dev/notes.md
```

To create or replace a file, use a quoted heredoc. The quotes in `<<'EOF'` stop the shell from expanding `$` and backticks:

```sh
cat > /home/dev/notes.md <<'EOF'
# Notes
Price is $5.
EOF
```

The whole command, file text included, must fit in 16,384 bytes. Files you write this way belong to the `dev` user.

## Move files in and out

`import_file` writes one file onto a computer. `export_file` reads one back. Each moves one regular file of 1 to 262,144 bytes, with its exact bytes. To move a folder, pack it into an archive with a command first. Importing an archive doesn't unpack it. The tools work only when `get_computer` reports `capabilities.file_transfer: true`. Files are read and written as the `dev` user, with its permissions.

| Tool | Inputs besides the computer | What it does |
|---|---|---|
| `import_file` | `request_id`, `destination`, `expected_bytes`, `expected_sha256`, `file` | Writes an attached file to `destination` |
| `read_file_import` | `request_id` | Reads the import's receipt |
| `export_file` | `path`, `expected_bytes`, `expected_sha256`, optional `representation` | Returns the file's bytes |

### Import a file

`file` is the client's attachment, with `download_url` and `file_id`, and optionally `mime_type` and `name`. Rootsy accepts only download URLs on ChatGPT's file host (`*.oaiusercontent.com`), so import works from ChatGPT. Other clients can't attach a file this way. Use `run_command` or the workspace Files window instead.

`destination` is an absolute path of up to 4,096 bytes, and its folder must already exist. Import never overwrites. If a file is already there, the import fails with `failure_reason: "destination_exists"` and the existing file stays. Import never creates folders, so create them with a command first. The attachment's name never chooses where the file goes.

`expected_bytes` and `expected_sha256` (lowercase hex) must match the attachment exactly. Compute them from the real bytes in the client. If the client can't read the bytes, say so. Don't guess a hash.

`request_id` here must be a UUIDv7 generated now. It can be at most 5 minutes in the future, and it expires 24 hours after its timestamp. After any error, disconnect or uncertain answer, call `read_file_import` with the same `request_id` before you try again. A retry needs the same `request_id`, destination, size and hash. Rootsy allows 3 attempts per `request_id`.

The receipt's `phase` says where the import stands:

| `phase` | Meaning |
|---|---|
| `reserved`, `staging`, `finalizing` | In progress |
| `completed` | The file is in place with the expected bytes |
| `failed` | It didn't happen. `failure_reason` says why when Rootsy knows. |
| `expired` | The `request_id` expired before the import finished |
| `unknown` | Rootsy can't prove whether the file was written. Check the destination with a command. Don't import again under a new `request_id`. |

### Export a file

`export_file` reads `path` once and checks it against `expected_bytes` and `expected_sha256`. Get those with ordinary commands:

```sh
wc -c < /home/dev/site.tar.gz; sha256sum /home/dev/site.tar.gz
```

With `representation: "resource"` (the default), the result holds the original bytes as one embedded resource. With `"image"`, it returns a PNG as an image, for PNGs up to 4,096 pixels a side and 16,777,216 pixels in all. A file that changes while it's read fails the check and isn't returned. Create screenshots or archives with a command first, then export them.

### File limits

| Limit | Value |
|---|---|
| File size | 1 to 262,144 bytes |
| Path | absolute, up to 4,096 bytes |
| Time for one call | 60 seconds |
| Imports in progress per computer | 4 |
| Import receipts kept | 256 per computer, 2,048 per account |

## Sleep and Runtime Credits

A sleeping computer wakes when a command, a file call or a restart reaches it. A command holds it awake until the command ends or hits its time limit. Commands fail with `computer_unavailable` while a computer is resetting, restoring or being deleted.

Time awake spends Runtime Credits, whatever woke the computer. `run_command` and `read_command` check the account's credits first. If the account can't fund the full time limit, Rootsy shortens the limit to what it can fund. If it can fund nothing, the call fails with `runtime_exhausted`. On a computer that's parked because the account ran out, calls fail with `box_parked_runtime`. `cancel_command` works without credits. See [Billing](https://rootsy.io/docs/reference/billing.md).

## Errors

A failed tool call returns `isError: true` and a JSON body with `error.code`, `error.message` and `error.retryable`. Lifecycle errors can add `error.details`. Import errors carry the `request_id` to read back with `read_file_import`. These are the codes you're most likely to see. Schedule and domain tools have their own codes, explained in their messages.

| `error.code` | What to do |
|---|---|
| `invalid_arguments` | Fix the input: a missing field, a non-UUID ID, or a value out of range. |
| `not_found` | The account or computer doesn't exist, isn't yours, or the `computer_id` belongs to a different `account_id`. |
| `insufficient_scope` | Reconnect the client and approve the access it asks for. |
| `access_paused` | Resume MCP access in the app. |
| `request_conflict` | This `request_id` was used with different input. Read the original instead. |
| `computer_unavailable` | The computer isn't `active`. Check `status` with `get_computer`, and wait for a create, reset, restore or deletion to finish. If its `actions` show `restart` as available, use `restart_computer`. |
| `unsupported_runtime` | This computer can't run the tool. Check `capabilities` in `get_computer`. |
| `runtime_exhausted`, `runtime_dust` | The account has no usable Runtime Credits, and the computer isn't parked. Calls work again when credits arrive. |
| `box_parked_runtime` | The computer is parked because the account ran out of Runtime Credits. Calls work again once credits arrive and Rootsy unparks it. See [Billing](https://rootsy.io/docs/reference/billing.md#when-credits-run-out). |
| `box_dormant` | The computer is dormant because the plan has fewer computers than the account holds. See [Billing](https://rootsy.io/docs/reference/billing.md#when-a-plan-shrinks-or-ends). |
| `storage_over_limit` | The computer is over its plan's storage. Delete files with the workspace Files window, then try again. |
| `account_suspended`, `account_deleting` | The account can't run anything. Write to support@rootsy.io if you don't know why. |
| `temporarily_unavailable` | Rootsy couldn't complete the call. Try again after a few seconds with the same `request_id`. For an import, call `read_file_import` first. |

## What the MCP server doesn't do

It doesn't create, restore or pin backups. It doesn't change plans or billing, or manage organizations and members. It has no interactive terminal and no stdin, and it doesn't stream output while a command runs. Do those things in the app. It never edits DNS at your domain provider. Create and remove those records yourself.

---
Page: https://rootsy.io/docs/reference/mcp/ · Updated: 2026-10-21 · All docs: https://rootsy.io/llms.txt
