Your agent ships. The platform holds the lines.
Connect Claude Code, Codex or any MCP client to one endpoint. Your agent gets 24 tools to deploy apps, read logs, set secrets, branch Postgres and work in computers and sandboxes. Its key is held to what you made it for by the platform, not by the prompt.
1 own org2 credentials3 app list4 class
One key, one command, then ask.
The MCP server is at https://api.rytiv.com/v1/mcp. Streamable HTTP, a bearer header, no sessions.
-
Create an agent key
In the console under API keys, or with the CLI. The value is shown once.
--agent sandboxlets the key do what your organization's keys do, except create, reveal or revoke credentials. Add--app <name>to limit it to those apps.terminal ry keys create claude-code --agent sandbox -
Add Rytiv to your agent
With your key in place of
<your key>. Add--scope userto use it in all your projects. For Codex, setRYTIV_TOKENin the environment Codex starts from.claude mcp add --transport http rytiv https://api.rytiv.com/v1/mcp --header "Authorization: Bearer <your key>"codex mcp add rytiv --url https://api.rytiv.com/v1/mcp --bearer-token-env-var RYTIV_TOKEN{ "mcpServers": { "rytiv": { "type": "http", "url": "https://api.rytiv.com/v1/mcp", "headers": { "Authorization": "Bearer ${RYTIV_TOKEN}" } } } }
-
Check the connection
The list shows
rytivwith its status. Inside Claude Code,/mcpshows the same.terminal claude mcp list -
Let read-only tools run without asking
In Claude Code the tools are called
mcp__rytiv__<tool>. Allow the ones that only read in.claude/settings.json; leave the ones that change something on asking until you trust the setup..claude/settings.json { "permissions": { "allow": ["mcp__rytiv__search_docs", "mcp__rytiv__list_apps", "mcp__rytiv__get_logs"] } }
Keep the key out of shared files. To share the setup through a project's .mcp.json, reference ${RYTIV_TOKEN} as above; Claude Code expands it, and each person sets their own. With the CLI installed, ry mcp install --claude (or --cursor, --codex) writes the entry for you. Any MCP client that speaks Streamable HTTP with a bearer header works.
The agent picks the tools.
A few first prompts from the quickstart. Before guessing a field name or a limit, your agent can ask search_docs.
- List my apps on Rytiv.
- Deploy nginx:alpine on Rytiv as an app called hello (it listens on port 80) and give me its URL.
- Show me the logs of hello.
- Create a Postgres database called appdb on Rytiv, branch it, and try the migration on the branch.
- Create a computer on Rytiv, clone my repository into it and run the tests there.
24 tools, annotated.
Annotations tell your client which calls to confirm with you. openWorldHint is false on every tool: none of them reaches outside your own organization. This table is the one the server answers tools/list with.
| Tool | Effect | readOnly | destructive | idempotent | What to know |
|---|---|---|---|---|---|
search_docsSearch the Rytiv documentation | reads | true | A knowledge base inside the platform. No request leaves Rytiv. | ||
list_appsList apps | reads | true | Every app of your team with the URL it answers on. | ||
deploy_appDeploy an app | changes or replaces | false | true | false | A rolling deploy: a new machine comes up before an old one goes. |
get_logsRead the logs of an app | reads | true | One entry per machine. A gap in the output is named, never silently joined. | ||
set_envSet environment variables | changes or replaces | false | true | true | Answers with the keys only. No value is ever returned. |
create_postgresCreate a Postgres database | creates | false | false | false | The password is not returned; it stays in the database app's secrets. |
create_branchBranch a Postgres database | creates | false | false | false | A copy-on-write copy, optionally with a lifetime up to 30 days. |
execute_sqlRun SQL against a Postgres database | changes or replaces | false | true | false | Read-only unless you name a branch. The database refuses the write. |
computer_createCreate a computer | creates | false | false | false | A machine with checkpoints stored outside the host. |
computer_execRun a command in a computer | changes or replaces | false | true | false | An argument list, not a shell line. Up to 300 seconds. |
computer_snapshotSnapshot a computer | creates | false | false | true | Disk alone, or with memory so a restore resumes the processes. |
sandbox_createCreate a sandbox | creates | false | false | false | A short-lived machine for one task, from a template. |
sandbox_listList sandboxes | reads | true | State, deadline and metadata of each. | ||
sandbox_getRead a sandbox | reads | true | State, deadline, URLs, ports, metadata and egress policy. | ||
sandbox_execRun a command in a sandbox | changes or replaces | false | true | false | Answers its exit code, stdout and stderr. |
sandbox_files_readRead a file in a sandbox | reads | true | Text as text, anything else as base64. | ||
sandbox_files_writeWrite a file in a sandbox | changes or replaces | false | true | true | Replaces a file of the same path. |
sandbox_files_listList a directory in a sandbox | reads | true | Name, type, size, mode and modification time. | ||
sandbox_pausePause a sandbox | changes or replaces | false | true | true | Its state is stored outside the host and its deadline stands still. |
sandbox_resumeResume a sandbox | creates | false | false | true | It gets back exactly the time its deadline had left. |
sandbox_forkFork a sandbox | creates | false | false | false | Copies a running sandbox into 1 to 20 new ones. |
sandbox_snapshotSnapshot a sandbox | creates | false | false | false | Keeps memory, processes and files as a template. |
sandbox_extendSet a sandbox's deadline | changes or replaces | false | true | true | Now plus ttlSeconds. It does not add to the time left. |
sandbox_deleteDelete a sandbox | changes or replaces | false | true | true | Its machine and its disk are gone, and nothing can be brought back. |
The token does not forget what it is for.
An agent told by its prompt "this is production, only read" is one injected sentence away from forgetting it. An agent key carries its class and its app list, and the platform judges every request by them.
sandbox For trying things out
Everything an API key may do, inside the apps on its list.
twin For a per-task copy
The same, for a copy of production: a preview app, a database branch, a computer.
production Read only
Status, releases and logs are open. Files and query results need prodDataRead. A shell or tunnel is refused either way. Any change answers agent-token-production-readonly.
Judged on every request, in this order
- Its own organization only.
X-Rytiv-Orgis refused (agent-token-delegation). - Credentials are never reachable. Creating, revealing or revoking keys, SSH keys, webhooks and Git installations, approving a device login, administering the organization: refused for every class (
agent-token-credential). - The app list, when there is one. A request about another app, or a change that names no app, is refused (
agent-token-app-scope). - The class.
sandboxandtwinmay change things;productionmay read.
Read what the agent did
Every refusal and every allowed request of an agent key beyond a plain read is written to an audit before it runs; a change that cannot be recorded does not run. ?token=<id> narrows it to one key. A key narrowed with scopes cannot read it.
curl "https://api.rytiv.com/v1/agent-actions?limit=100" \ -H "Authorization: Bearer $RYTIV_TOKEN"
ry keys create claude-code-prod --agent production --read-prod-data The database itself says no.
Without a branch, execute_sql runs with default_transaction_read_only, so a write is refused by Postgres, not only by a rule of ours. To change data or a schema, the agent branches first with create_branch and names the branch.
A branch is a copy-on-write copy with the same data at that moment, ready in seconds, and can be dropped or expire on its own. None of the tools ever returns a database password.
execute_sqldatabase: appdbreads onlycreate_branchappdb → appdb-try, ttlSeconds 86400a copyexecute_sqldatabase: appdb, branch: appdb-trymay write- appdb is untouched. You decide what goes to production.
Computers that keep their files. Sandboxes that end.
Some work needs a place whose files are still there tomorrow. Some needs a clean machine for one task and nothing after. Both are machines, billed by memory, CPU and disk.
A machine of its own
A home disk at /data that outlives the machine, and a desktop you can watch or take over with ry computer view dev-1 and ry computer control dev-1. Node.js, Codex, OpenCode, git, the GitHub CLI, Python and a browser come with the image. rytiv-setup claude runs Anthropic's own installer inside, with your own Claude account.
| Size | vCPU | Memory | Home |
|---|---|---|---|
| small | 1 | 2048 MB | 50 GB |
| base | 2 | 4096 MB | 100 GB |
| large | 4 | 8192 MB | 128 GB |
After 10 minutes without activity a small computer sleeps with its memory and a base or large one shuts down; the home stays. A computer the agent creates with computer_create takes snapshots, hourly ones automatically, and comes back from its last checkpoint after a host failure.
ry computer view dev-1 ry computer control dev-1 ry computer release dev-1
A machine with a deadline
Starts from the image an app of yours runs, or a build. Run commands, read and write files, pause and resume it. By default it is removed at its deadline with everything on its disk. Up to 100 at once.
- Lifetime 10 seconds to 24 hours, 5 minutes by default
- Access signed by default: its URLs answer only with a token
- Network allow all, deny all, or an allowlist of domains
- Nothing inherited no environment, no secrets of the template app
ry sandbox create app:web --ttl 30m curl -X POST https://api.rytiv.com/v1/sandboxes \ -H "Authorization: Bearer $RYTIV_TOKEN" \ -H "Content-Type: application/json" \ -d '{"template": "app:web", "ttlSeconds": 1800}'
const res = await fetch("https://api.rytiv.com/v1/sandboxes", { method: "POST", headers: { Authorization: `Bearer ${process.env.RYTIV_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ template: "app:web", ttlSeconds: 1800 }), }); const sandbox = await res.json(); console.log(sandbox.id, sandbox.state, sandbox.url);
Hosted connectors sign in with OAuth.
A hosted client has no place to paste a key, for example a custom connector in claude.ai. It needs nothing but the address https://api.rytiv.com/v1/mcp. You approve it once in the console, and no key is pasted anywhere.
In Claude Code and Codex you do not need OAuth: add the server with your key as a bearer header.
- Discover. A request without a token answers
401and names the discovery documents. - Register. The client registers itself (RFC 7591). No client secret: every client is public and bound to PKCE.
- Approve. You land in the console, signed in, see which client wants which organization, and approve once.
- Connect. An access token for one hour and a refresh token for 30 days, rotated on every use. It is a key of your organization with the member role, revoked like any other.
Give your agent somewhere to ship.
One command connects Claude Code.
claude mcp add --transport http rytiv https://api.rytiv.com/v1/mcp --header "Authorization: Bearer <your key>"