diff --git a/cloud/apps/sandbox-control/src/api/openapi.ts b/cloud/apps/sandbox-control/src/api/openapi.ts index 1d4d78051eb..fdaa8eef326 100644 --- a/cloud/apps/sandbox-control/src/api/openapi.ts +++ b/cloud/apps/sandbox-control/src/api/openapi.ts @@ -186,6 +186,16 @@ export const OPERATIONS: Record> = { '/v1/api-keys/{id}': { delete: { tags: ['API keys'], sessionOnly: true, summary: 'Revoke an API key', params: [idParam('id', 'Key id')], responses: { 200: 'Revoked' } } }, + '/v1/oauth-approvals': { + post: { + tags: ['API keys'], + sessionOnly: true, + summary: 'Mint a one-time MCP approval code', + description: 'A single-use code (10 minutes) the user pastes on the MCP OAuth approval page instead of an API key. Defaults to every scope the role allows.', + body: obj({ scopes: { type: 'array', items: { type: 'string', enum: [...API_SCOPES] } } }), + responses: { 201: { description: 'Created', schema: obj({ code: { type: 'string' }, expiresAt: { type: 'integer' }, scopes: { type: 'array', items: { type: 'string' } } }) } } + } + }, '/v1/settings': { get: { tags: ['Account'], summary: 'Programmatic-access settings', responses: { 200: { description: 'Settings', schema: obj({ settings: obj({ vmCliEnabled: { type: 'boolean' } }) }) } } }, put: { tags: ['Account'], sessionOnly: true, summary: 'Update settings (owner/admin)', body: obj({ vmCliEnabled: { type: 'boolean' } }, ['vmCliEnabled']), responses: { 200: 'Updated' } } @@ -393,8 +403,10 @@ curl -X POST ${o}/v1/workspaces/$ID/wake -H "Authorization: Bearer $ORCA_API_ curl -X POST ${o}/v1/workspaces/$ID/archive -H "Authorization: Bearer $ORCA_API_KEY" curl -X DELETE ${o}/v1/workspaces/$ID -H "Authorization: Bearer $ORCA_API_KEY"

MCP

-

Point any MCP client at ${o}/mcp. Clients that support OAuth sign in through the browser; others can send an API key as a bearer token.

-
claude mcp add --transport http orca-cloud ${o}/mcp
+

Point any MCP client at ${o}/mcp (Streamable HTTP). Clients that support OAuth register themselves and open an approval page in the browser: approve with a one-time code from Orca → Settings → API keys → Approve an app, or with one of your API keys. The app never gets more than that key may do. Clients without OAuth can send an API key as a bearer token.

+
claude mcp add --transport http orca-cloud ${o}/mcp
+claude mcp add --transport http orca-cloud ${o}/mcp --header "Authorization: Bearer $ORCA_API_KEY"
+

Tools: whoami, list_projects, list_workspaces, get_workspace, create_workspace, sleep_workspace, wake_workspace, archive_workspace, destroy_workspace, send_message, get_message, list_sessions, list_routines, run_routine. They use the same scopes, rate limits and audit log as the REST API.

Limits and errors

Rate limits120 requests/min per key, 30 messages/min per user, 10 workspace creates/min per org. 429 responses carry Retry-After.
diff --git a/cloud/apps/sandbox-control/src/mcp/consent.ts b/cloud/apps/sandbox-control/src/mcp/consent.ts index 5456205068c..05844e4dd79 100644 --- a/cloud/apps/sandbox-control/src/mcp/consent.ts +++ b/cloud/apps/sandbox-control/src/mcp/consent.ts @@ -94,7 +94,7 @@ export function consentPage(view: ConsentView, csrf: string, formAction: string, `

It will be able to

` + `` + `` + - `

In Orca, open Settings → Orca Cloud → API access and choose Approve an app for a one-time code, or paste one of your API keys. The app gets at most what that key may do; the code or key itself is never shared with it.

` + + `

In Orca, open Settings → API keys and choose Approve an app for a one-time code, or paste one of your API keys. The app gets at most what that key may do; the code or key itself is never shared with it.

` + `
` + `` + `

Client ${esc(view.clientId)}

`