@rayrun/cli
v0.13.1
Published
Build and deploy MCP servers, connect clients, execute tools, and manage Rayrun
Readme
@rayrun/cli
Build and deploy complete MCP servers, connect supported MCP clients, execute approved tools through OAuth, and manage Rayrun through its public API.
pnpm --package @rayrun/cli@latest dlx rayrun setup https://copy-the-endpoint-from-rayrun.example/mcpThe command detects Claude Code, Codex/ChatGPT desktop, Cursor, VS Code, Windsurf/Devin Local, and OpenCode v2. In a terminal, choose user or project scope, use arrow keys and Space to select clients, then press Enter. Review the target files and confirm before writing. Ctrl+C cancels any prompt without changing files. Node.js 20.12 or newer is required.
Repeat --client to select targets, or use --all --yes to configure all detected clients.
--yes skips the wizard. Redirected input or output and --no-input never open prompts; provide
--client, --all, or --yes to select clients in those environments.
It writes only the public Rayrun endpoint. OAuth stays in each client; no token or API key enters a
config file or shell history. Before writing, it creates private byte-for-byte backups under
~/.rayrun/backups/setup. Configuration writes are atomic and roll back together on failure.
rayrun setup <endpoint> --dry-run --all
rayrun setup <endpoint> --client cursor --client opencode --yes
rayrun setup <endpoint> --client codex --yes --login
rayrun setup rollback <backup-id>Use --project for project-local configuration in Claude Code, Codex, Cursor, VS Code, Devin, and
OpenCode. Legacy Windsurf remains user-scoped. Claude Code asks you to approve a project MCP server
when the project opens. Codex loads .codex/config.toml after you trust the project; restart Codex
after doing so.
Interactive setup runs supported OAuth login commands. Codex requests only the mcp:tools scope.
--yes and non-interactive setup skip login unless you pass --login; use --no-login in scripts.
For Codex, setup --login runs codex mcp login rayrun --scopes mcp:tools. Its success message
means Codex is authenticated; it does not authenticate the Rayrun CLI, so rayrun status can still
report missing CLI credentials. Run rayrun login separately; members receive tool access and
workspace administrators also receive deployment and management access.
Choose the right authentication
The CLI has three credential paths:
- Interactive CLI commands use one browser approval stored privately by the Rayrun CLI. The CLI
keeps one rotating login and requests audience-bound tokens as needed. Members receive tool
access; workspace administrators also receive deployment and management access. Check it with
rayrun statusand remove it withrayrun logout. - CI and unattended automation use
RAYRUN_API_KEYfrom the environment. When it is set, it takes precedence over browser credentials for deployments and management. - MCP clients keep separate OAuth credentials. For supported clients,
rayrun setup --loginruns their login command; its success describes that client only.
rayrun login
rayrun status
rayrun doctor
rayrun status --jsonstatus verifies the available audience-bound access tokens and reports the management credential,
user, workspace, granted scopes, endpoints, and the .rayrun/project.json deployment target.
doctor prints the same facts as explicit checks. The identity check itself does not require a
resource-specific API scope, so a narrow CI key can inspect its own identity safely.
Discover and automate commands
Root help stays short; group and leaf help contain the detailed syntax:
rayrun --help
rayrun deployments --help
rayrun help deployments rollback
rayrun completion zsh
rayrun schema > rayrun-command-schema.jsonCompletion is available for Bash, Zsh, Fish, and PowerShell. schema publishes every command,
option, compatibility alias, structured error shape, and exit-code meaning for agents and wrappers.
Existing flat commands remain valid; resource-oriented aliases include tools call,
connections add mcp, and connections add openapi.
Global automation options are accepted anywhere: --no-input prevents prompts and browser launch,
--quiet suppresses progress, --debug includes failure diagnostics, --paginate follows every
cursor page, and --fields name,uid selects fields from --json output. JSON failures go only to
stderr as { "error": { "code", "message", "requestId", "retryable", "next" } }; stdout stays
clean. Exit codes are 1 for command failure, 2 for usage, 3 for authentication, 4 for authorization,
5 for conflicts, and 6 for retryable service failures.
Execute tools
Execution commands connect to https://ray.run/mcp/direct and authenticate the person in a browser
with OAuth. Run rayrun login explicitly before the first call. They do not use
RAYRUN_API_KEY. Override the endpoint with --endpoint or RAYRUN_MCP_URL for a local or
self-hosted Rayrun gateway.
rayrun tools describe linear.create_issue
rayrun call linear.create_issue '{"title":"Fix the release"}'
rayrun call linear.create_issue --input arguments.jsonTool names are service-qualified and must match the Direct Mode catalog exactly. Use --input - to
read a JSON object from stdin, --json for machine-readable output, and --no-open to print browser
URLs without launching them.
Direct Mode also proxies approved resources, templates, prompts, and completion. Prompt names use
service.name; resource URIs use rayrun:connection/<service>/<upstream-uri>. Aggregate listings
are limited to 16 eligible connections, 5,000 items, 4 MiB, and 60 seconds. One failed upstream
fails the aggregate request instead of returning an ambiguous partial catalog.
If an exact call needs approval or a service authorization, the CLI opens the authenticated Rayrun page and saves a private continuation:
Execution paused: 12345678-1234-1234-1234-123456789abc
Resume: rayrun resume --execution-id 12345678-1234-1234-1234-123456789abcApprove in the browser, then run the printed command. resume replays only the saved call with the
server-issued continuation state; its accept response cannot authorize the call without the
authenticated browser decision. Pending files can contain the exact tool arguments and are stored
under ~/.rayrun/execution/pending. The resume window expires after 10 minutes; expired files are
pruned by the next execution command or rayrun logout. OAuth tokens use the same private,
per-host storage; keep RAYRUN_CONFIG_HOME on a local filesystem. POSIX systems enforce 0700
directories and 0600 files; Windows uses the current profile's ACL. logout removes local tokens
and attempts revocation.
Build and deploy an MCP server
rayrun init creates a complete TypeScript MCP server using Node.js 24 and
ViteMCP. Use --template fastmcp for Python 3.13 and
FastMCP. Give the generated directory to Codex, Claude Code, or another
coding agent and ask it to implement the server—not just one tool. The server may expose as many
tools as the project needs, up to the release limit documented below.
Both templates are ready to be published to your own customers. When one of them calls a tool,
Rayrun signs who they are in the Rayrun-Identity header: the ViteMCP template checks it with
verifyIdentityAssertion from @rayrun/sdk and passes the end user to tools as context.auth, and
the FastMCP template checks it in rayrun_identity.py. Calls from your own workspace carry no
identity and are served anonymously.
rayrun init hacker-news-digest
# Python alternative: rayrun init hacker-news-digest --template fastmcp
cd hacker-news-digest
rayrun loginGive the project to the coding agent:
Build a complete Hacker News digest MCP in this project.
Add get_top_stories({ limit }) using the public Hacker News API.
Return each story's title, URL, score, and author.
Add tests, run them, and deploy the server to Rayrun.The agent finishes by validating and deploying the server. Use the command for the selected template:
# ViteMCP
npm test
# FastMCP
uv run pytest
rayrun deploy --waitrayrun.json contains the project's stable UUID and is committed with the source. The CLI deploys
with an idempotent PUT, so a retry after a lost response finds the existing service and queued or
active build instead of creating or rebuilding it. .rayrun/project.json caches only the current
workspace and UUID connection ID and remains ignored. Use project status to restore that cache on
a new laptop or clean CI checkout. Use project adopt to associate the committed project ID with a
known existing source deployment; Rayrun never silently replaces an existing association.
rayrun deploy --secret GITHUB_TOKEN=<workspace-secret-uid> --wait
rayrun project status
rayrun project adopt <connection-id>
rayrun deployments list --connection <connection-id>
rayrun deployments logs <build-uid>
rayrun deployments releases <connection-id>
rayrun deployments rollback <connection-id> <release-uid> --waitThe generated rayrun.json is the deployment contract. Visible configuration belongs in
environment; required secret environment names belong in secrets and are bound to
workspace-secret UIDs at deploy time:
{
"entrypoint": "dist/server.js",
"environment": {
"INVOICE_API_URL": "https://api.example.com"
},
"name": "invoice-automation",
"projectId": "8ec6e172-0e3d-4dd0-b086-4afdf70bb6e9",
"runtime": "node",
"secrets": ["INVOICE_API_TOKEN"],
"version": 2
}Create and rotate secret values through stdin so they never enter shell history or process arguments. Rayrun never returns a secret value:
printf %s "$GITHUB_TOKEN" | rayrun secrets create GITHUB_TOKEN --stdin
rayrun secrets list
printf %s "$NEW_GITHUB_TOKEN" | rayrun secrets rotate <secret-uid> --stdin --redeploy
rayrun secrets archive <secret-uid>--redeploy requires deployments:write in addition to secrets:write and exits nonzero if any
bound service fails to restart. The secret rotation itself remains applied.
Bind the returned workspace-secret UID at deploy time with --secret.
Node projects must contain package.json with "type": "module" and a build script, plus
package-lock.json. Python projects set "runtime": "python", use a .py entrypoint, and contain
pyproject.toml plus uv.lock. Every server must listen on PORT and expose Streamable HTTP MCP
at /mcp. NODE_ENV, PORT, and RAYRUN_DEPLOYMENT_UID are reserved.
The recommended scaffolds are golden paths, not framework lock-ins. Rayrun accepts locked Node and
Python projects whose configured entrypoint serves Streamable HTTP MCP at /mcp on PORT. Source
uploads reject symlinks, a Dockerfile, .env files, obvious private-key material, more than 512
files, or more than 16 MiB. Source is encrypted at rest. Builds use fixed Node 24 or Python 3.13
definitions and non-root runtime users. Activation happens only after the candidate returns at least
one tool from tools/list; a failed candidate leaves the active release untouched.
Build logs are encrypted with the workspace data key and erased after 30 days. CI API keys need the
separate deployments:logs:read scope to read them; interactive login requests it explicitly.
The Dashboard's Deploy services key preset grants only the three deployment scopes required by
this CLI workflow.
Rotating a bound workspace secret marks the source service for redeploy. Running rayrun deploy
--wait again reuses the already-built image, verifies a candidate with the new secret version, and
activates it without uploading the secret value or rebuilding unchanged source.
--wait follows the exact release UID returned by Rayrun when a built image is reused. Source
validation, upload, build, and release transitions stream to stderr while the final human or JSON
result stays on stdout. Interactive terminals also stream accessible build-log additions; narrow
CI keys without deployments:logs:read continue with status updates. The CLI prints the build UID
and exact resume command before polling, so interrupting the wait does not lose recovery context; a
successful human-readable deploy also prints the service dashboard URL. Source images are limited
to 512 MiB, workspace image retention is limited to 5 GiB, and inactive images remain available for
rollback for 30 days.
A workspace can hold 25 source-deployed services, run 10 source builds concurrently, and start 50 isolated builds in 24 hours. One service can build at a time. A release may advertise up to 1,000 tools; build logs are capped at 1 MiB and erased after 30 days.
Private package-manager credentials are not accepted: .npmrc and Yarn credential files are
excluded locally and rejected by the API. Build dependencies must be readable without placing a
registry token in the uploaded project.
Deploy every GitHub push
Install the Rayrun GitHub App once, deploy the current branch head, and keep the service current on future pushes:
rayrun login
rayrun deploy github rayrun/hacker-news-mcp --wait
rayrun deployments github <repository-uid>Use --root servers/support for a monorepo, --branch main to override the repository default,
and the same --secret NAME=<workspace-secret-uid> bindings as local source deployment. Rayrun
creates isolated preview services for same-repository pull requests by default; use --no-previews
to disable them. Fork pull requests are ignored because previews may receive workspace secrets.
Each signed webhook commit goes through the same source validation, isolated build, MCP
verification, activation, logs, and rollback pipeline.
Re-running the command reconfigures the branch and only the settings you pass. --secret replaces
all bindings, --clear-secrets clears them, and --previews re-enables previews. Disconnect with
rayrun deployments github disconnect <repository-uid>; this stops future pushes and removes
previews while retaining the primary deployed service.
Manage a workspace
Run rayrun login for interactive management. For CI, create a scoped key in Dashboard →
Settings → API keys, then export it without putting it in a command argument or configuration
file. RAYRUN_API_URL is optional and defaults to https://ray.run.
rayrun login
rayrun connect mcp https://mcp.example.com --name 'Internal tools'
rayrun connect openapi https://api.example.com/openapi.json --name 'Example API'
rayrun connections list
printf %s "$CONNECTION_CREDENTIAL_JSON" | \
rayrun connections credential <connection-id> --stdin
rayrun connections authorize <connection-id>
rayrun connections index <connection-id>
rayrun connections disable <connection-id>
rayrun connections enable <connection-id>
rayrun connections delete <connection-id> --confirm <connection-id>
rayrun connections history <connection-id>
rayrun connections diff <connection-id> <version-uid>
rayrun connections restore <connection-id> <version-uid> --reason 'Restore reviewed settings'
rayrun access-profiles list
rayrun access-profiles history <profile-uid>
rayrun access-profiles diff <profile-uid> <version-uid>
rayrun access-profiles restore <profile-uid> <version-uid> \
--reason 'Restore reviewed policy' --confirm-risk
rayrun webhooks list
rayrun webhooks create --file webhook.json
rayrun webhooks update <webhook-uid> --file webhook-update.json \
--reason 'Pause deliveries during maintenance'
rayrun webhooks replay <webhook-uid> <delivery-uid>
rayrun webhooks delete <webhook-uid> --confirm <webhook-uid>
rayrun webhooks history <webhook-uid>
rayrun webhooks diff <webhook-uid> <version-uid>
rayrun webhooks restore <webhook-uid> <version-uid> \
--reason 'Restore reviewed destination'
rayrun publications create acme-issues --name 'Acme issues' --services <connection-id>
printf %s "$OIDC_CLIENT_SECRET" | \
rayrun publications sign-in set <publication-uid> \
--issuer https://login.example.com --client-id rayrun-acme-issues --client-secret-stdin
rayrun publications publish <publication-uid> --reason 'Launch'
rayrun publications show <publication-uid>
rayrun publications update <publication-uid> --tool-access read-only \
--reason 'Read-only while we review'
rayrun publications history <publication-uid>
rayrun publications diff <publication-uid> <version-uid>
rayrun publications restore <publication-uid> <version-uid> --reason 'Restore reviewed settings'
rayrun publications end-users <publication-uid>
rayrun publications end-users remove <publication-uid> <end-user-uid>
rayrun publications approvals <publication-uid>
rayrun publications domain set <publication-uid> mcp.example.com
rayrun publications domain check <publication-uid>
rayrun publications delete <publication-uid> --confirm-slug acme-issues
rayrun clients list
rayrun tools search create issue --connection <connection-id>
rayrun policy inspect <client-uid> --query issue
rayrun approvals list
rayrun activity listList commands show a compact table. Add --json for stable machine-readable output, --limit to
request up to 100 rows, and --cursor with the printed next cursor to continue. Add --paginate to
follow all cursor pages and --fields name,uid to select fields from every returned item. The CLI
calls the same hosted API and policy evaluator as the dashboard; it does not run a local gateway or
store API keys.
Connection history versions the service name, slug, description, timeout, enablement, and payload
capture together. Credentials, headers, OAuth state, authority policy, health, and index state never
enter the snapshot. history is cursor-paginated, diff compares with the immediate predecessor,
and restore creates a new attributed version using optimistic concurrency.
Access-profile history versions the name, description, default, and exact tool rules together.
Client assignments and archival remain separate authorization events. Restore refuses references to
tools that are no longer available; pass --confirm-risk only after reviewing every Destructive or
Unknown tool allowed by the restored default or an exact rule. Immutable access-profile history is
limited to 64 MiB per workspace.
Webhook history versions the destination URL, description, event selection, delivery mode, batch
size, enablement, and payload forwarding together. Signing secrets, deliveries, retry state, and
health never enter the snapshot. Restore revalidates the historical destination, creates a new
attributed version, and preserves the current signing secret. Immutable webhook history is limited
to 64 MiB per workspace. Destination paths and query strings can contain provider tokens, so
diff requires webhooks:read; add webhooks:write to reveal full destination URLs. Read-only
API keys receive null instead of current or historical URLs.
A publication offers hosted services to your own customers, who sign in with your OpenID Connect
provider instead of joining your workspace. show prints the address their AI clients connect to
and the callback URL your provider must allow. sign-in set defines the whole provider: scopes
default to openid,email,profile and the auth method to client_secret_basic. It reads a client
secret only from stdin; leave the secret out to keep the stored one, which works only while the
issuer and client ID stay the same. Publication history versions the name, description, services,
sign-in provider, tool access, profile, and limits together; client secrets, publishing, end users,
and the custom domain never enter the snapshot. domain set prints the TXT and CNAME records to
create, and domain check looks for them now instead of waiting for the scheduled check. Changing a
publication needs publications:write, which the Manage publications key preset holds.
Publish Workspace Skills
Workspace Skills are Agent Skills packages delivered by compatible clients through Rayrun's
SEP-2640 MCP extension. The package root must contain SKILL.md; local reads reject symlinks,
special files, unsafe paths, more than 512 files, or more than 16 MiB.
rayrun skills validate ./skills/incident-response
rayrun skills push ./skills/incident-response --publish \
--mode both --all-clients --reason 'Reviewed for production use'
rayrun skills list
rayrun skills show <skill-uid>
rayrun skills audience <skill-uid> --mode direct --profiles <profile-uid>
rayrun skills history <skill-uid>
rayrun skills diff <skill-uid> <version-uid>
rayrun skills pull <skill-uid> --output ./recovered-skill
rayrun skills restore <skill-uid> <version-uid> --reason 'Recover reviewed instructions'
rayrun skills disable <skill-uid>
rayrun skills archive <skill-uid>
rayrun skills delete <skill-uid> --confirm-name incident-responsepush saves a draft unless --publish is explicit. A new draft never moves the existing published
pointer. Pass --profiles as comma-separated access-profile UIDs to narrow discovery, or explicitly
pass --all-clients. Later publish and audience commands preserve the current audience when both
flags are omitted. Script, binary-file, and possible-secret warnings require
--confirm-risk before publication. allowed-tools is guidance, not access policy.
pull refuses to overwrite an existing path and creates account-private directories and files.
Restore creates another immutable draft revision and does not publish it. Permanent deletion removes
all content and delivery summaries, cannot be undone, and keeps the Skill name reserved so an old
skill:// URI cannot resolve to unrelated content.
Author hosted tool hooks
Interactive login requests the hook scopes. For CI, use a key with hook read and write scopes. Hook source executes in Rayrun’s hosted sandbox, not in the CLI process.
rayrun hooks pull <connection-id> <tool-uid> \
--output hook.ts --types-output rayrun-hooks.d.ts
rayrun hooks test <connection-id> <tool-uid> \
--file hook.ts --arguments '{"query":"release"}' --mock-result '{"items":[]}'
rayrun hooks save <connection-id> <tool-uid> \
--file hook.ts --reason 'Normalize release results'
rayrun hooks history <connection-id> <tool-uid>
rayrun hooks diff <connection-id> <tool-uid> <draft-version-uid>
rayrun hooks restore <connection-id> <tool-uid> <draft-version-uid> \
--reason 'Restore the reviewed mapping'
rayrun hooks reset <connection-id> <tool-uid> --confirm <hook-uid>@<version> \
--reason 'Remove obsolete experiment history'
rayrun hooks deploy <connection-id> <tool-uid> --shadow
rayrun hooks logs <connection-id> <tool-uid>
rayrun hooks rollback <connection-id> <tool-uid> <revision-uid>
rayrun hooks deactivate <connection-id> <tool-uid> --shadowpull can write the draft and generated TypeScript declarations separately. test compiles and
runs before plus an optional after stage using your mock result; it never contacts the upstream.
Each material save creates an immutable, attributed draft version; identical saves do not create
noise. history is paginated, diff compares with the previous version, and restore creates a
new draft head. Draft history and deployment revisions remain separate. Source and config history
is limited to 64 MiB per workspace.
reset permanently removes the hook's history and deployment revisions using the current hook UID
and version. A connection cannot be deleted while it owns hook history. deploy creates an
immutable revision and defaults to active; rollback and deactivate also accept --shadow. Hook
payloads follow the service capture setting, while timing, outcome, revision, request ID, and shadow
comparison remain available. Captured details require a full-control key.
Version-controlled access policies
Draft, review, and activate
rayrun policy create --name "Support" --description "Support team tools"
rayrun policy export <profile-uid> --output support.json
# Edit support.json, then save the first server-side draft.
rayrun policy draft --file support.json --draft-revision 0 --reason "Support rollout"
rayrun policy review <profile-uid> <draft-uid>
rayrun policy activate <profile-uid> <draft-uid> --reason "Reviewed rollout"
rayrun policy history <profile-uid>Creation starts with a block-all profile. Drafts are immutable proposed configurations, separate from
the live policy. Review validates and compares an exact saved draft; it does not record an approval or
require a second person. Activation applies that exact draft after checking the saved live version and
current tools again. Add --confirm-risk only after reviewing a reported high-risk grant.
Use rayrun policy drafts <profile-uid> for draft revisions and attribution. Supply its newest revision
with --draft-revision when saving the next draft. For older pages, use --before-revision with the
last returned revision. Live policy history supports the existing --cursor pagination. A live policy
change makes older drafts stale: export again, review your edits, and save a new draft. Both draft and
live history remain readable after archival.
rayrun policy clone <profile-uid> --name "Support trial" --expected-version 4
rayrun policy deactivate <profile-uid> --expected-version 4
rayrun policy archive <profile-uid> --expected-version 5Clone creates an unassigned block-all profile and saves the source configuration as its first draft; it copies neither assignments, owners, nor budgets. Deactivate atomically sets block-all and removes every explicit exception, retaining assignments and recording a revision. This is a fail-closed policy change, not a separate inactive flag. Restore a historical configuration or activate a fresh draft to resume access. Archive retires an unused profile and refuses profiles assigned to clients or Skills. All three operations require the caller's explicitly reviewed live version.
Apply a reviewed JSON document
Export an existing access profile, edit its JSON configuration, and review the validated difference:
rayrun policy export <profile-uid> --output policy.json
rayrun policy plan --file policy.json
rayrun policy apply --file policy.json --reason "Reviewed in PR 42"Use rayrun access-profiles list to find the profile UID. Authenticate with rayrun login, or set
RAYRUN_API_KEY in CI. Export and plan require policies:read; apply requires policies:write.
Commit policy.json for pull-request review. --json returns machine-readable plans and results.
The document contains the workspace, profile, expected version, and complete configuration. Apply replaces the name, description, default mode, and explicit tool rules atomically. Removing a rule restores the profile default for that tool. Client assignments remain managed separately. A successful change creates one immutable history entry; applying an identical current document creates none.
Plan validates live tool references and reports whether high-risk access requires the existing
--confirm-risk option. Apply repeats validation and rejects stale versions or another workspace.
After applying, export the new version before the next edit; export refuses to overwrite an existing
file. If another administrator changes the policy, re-export and review your edits against the new
version rather than changing expectedVersion blindly. This workflow manages existing access profiles;
it is not a Terraform provider or a multi-resource deployment.
