Repository navigation
feat: add remote mcp access to runtimes through alera cloud - #924
Conversation
Runtimes started from the desktop or the CLI can opt in to MCP Control and become reachable from any MCP client through one OAuth-protected endpoint, https://api.alera.build/v1/mcp. Each call names its runtime by name or id. - cloud: OAuth 2.1 authorization server (DCR, client metadata documents, consent, PKCE, refresh rotation, revocation), device sign-in for headless runtimes, runtime naming and capability reports, gateway that resolves the runtime, records a metadata-only audit row, and signs a per-call grant - edge: stateless Streamable HTTP MCP endpoint, sign-in rate limiters, and Durable Object routing of calls over the runtime's relay link (~mcp frames) - runtime: MCP Control setting (off, read, full), call grant verification, 31-tool catalog backed by typed `alera --json` invocations, `alera mcp`, `alera account`, and `alera runtime rename`, plus `alera mcp serve` (stdio) - desktop: MCP Access settings with access level, runtime name, endpoint, link status, and connected apps - infra and docs: optional web OAuth clients, MCP switch, docs/remote-mcp.md
There was a problem hiding this comment.
✅ No new issues found.
Remote MCP Control is opt-in (off / read / full, default off) and the trust boundaries hold.
OAuth and device sign-in. Public clients only, PKCE S256, exact redirect match (https, loopback http, reverse-domain and known-app schemes; javascript/data/file refused). Authorization errors render a page instead of redirecting once the client is known. Codes are single-use and a replay revokes the grant. Device flow is runtime-only; a runtime with a live refresh family cannot start another sign-in. User codes are 8 chars from a 32-char alphabet, bounded by the edge browser limiter.
Call gateway. POST /v1/mcp/calls is not on the public edge route. The grant binds account, runtime, tool, access, and jti (120s, aud: alera-runtime-mcp). MCP access tokens use a distinct audience and are rejected by other cloud routes. Arguments are not in the grant; the edge is inside the trust boundary and the runtime re-verifies before spawn. Audit rows are metadata only.
Edge endpoint. Stateless Streamable HTTP. Tool execution rechecks the live grant, so a revoked 15-minute JWT still fails the call. list_runtimes is edge-only; the runtime argument is stripped before the runtime sees the payload. ~mcp frames are not forwarded to phones. Frame cap is 1 MiB.
Runtime. Each tool is one alera --json invocation with --name=value (no shell). Context env vars are stripped. jti is recorded until exp. Local settings.mcp.access is checked again before spawn. mcp.* and account. stay off the mobile allowlist and the hub reverse channel.
Desktop. Settings > MCP Access is local-client-only. Unknown wire access falls back to off. mcpSettingsChanged is in runtimeHostEventNames.
Production follow-ups are documented, not defects: create the ALERA_WEB_* OAuth clients, and bind OAUTH_LIMITER / BROWSER_LIMITER / MCP_LIMITER on the edge.
grok-4.7 | 𝕏
Keep the confirmation token while a device sign-in is pending, refuse expired approved codes, cancel queued and running MCP calls when the relay link ends, accept long metadata-document client ids in call grants, and cap runtime names at the 160-byte sign-in label limit.
send_message and delegate_task now take the sender or coordinator terminal the CLI requires, a cancelled call never starts its command even while queued, calls stop when the socket they arrived on closes, and request traces record only the path so OAuth codes and state never reach the logs.
…filters Call grants are rejected if they expire while signing keys load, queued stdio calls never start once cancelled, an idle runtime session no longer blocks device sign-in, and list_tasks accepts stalled and cancelled.
…loud The stdio server cancels running and queued calls when the client closes stdin or stdout, a wait that ends with nothing new is no longer reported as a tool error, and renaming a runtime needs a signed-in account so the cloud reserves the name first.
… on 429 cancel_task runs as an audited administrative cancellation tagged [mcp], a device sign-in cancelled while its approving poll is in flight revokes the session instead of saving it, and edge rate-limit responses keep the route's CORS headers.
… polls The edge forwards callback and device query strings to Cloud Run as form bodies and the Worker runs without invocation logs, so codes and state never reach request logs. A cancelled device sign-in now lets its in-flight poll finish and revokes any session it returns.
…of logs Device code redemption takes a per-runtime advisory lock and rechecks for an active session, so a second approved code cannot sign in an already active runtime. The edge forwards /oauth/authorize queries as form bodies like callbacks and device pages.
…y its own outcome Device redemption creates its session inside the transaction that holds the per-runtime lock, so concurrent redemptions cannot exhaust the pool. The runtime records how the latest sign-in attempt ended and the CLI reports that instead of treating an older session as success.
…directing declined logins The replay record of MCP call grants survives relay restarts, a provider callback without a code renders a page instead of redirecting to the client, and a cancelled sign-in records its outcome at once so the CLI never reports it as a success.
…e browser runs Device redemption builds the signed response inside its transaction, so a signing failure leaves no orphaned session that would block the next device sign-in. alera account login no longer waits for the browser opener to exit before polling.
A signing failure during refresh no longer spends the client's refresh token, and database failures surface as server_error instead of invalid_grant. Native rotation keeps its behavior through a wrapper.
There was a problem hiding this comment.
ℹ️ No critical issues. One edge case in cancelled device sign-in is noted inline.
Reviewed changes
Re-reviewed the twelve commits since a340aa4. They harden OAuth logging, device redemption, refresh rotation, and MCP call lifetime.
- Moved authorization, callback, and device query strings into POST bodies so Cloud Run request logs record only the path.
- Stopped redirecting a declined provider login, because that callback is reachable without authentication.
- Redeemed one device code per runtime under an advisory lock, and signed the session before that transaction commits.
- Rotated MCP refresh tokens and signed the replacement access token in the same transaction, so a signing failure does not spend the token.
- Kept consumed call grants for the process lifetime, and rechecked expiry after signature verification.
- Stopped in-flight MCP calls when the relay socket or stdio client disconnects, and kept queued calls from starting after cancel.
- Required a coordinator and a sender handle on orchestration tools, and recorded MCP task cancellation as an audited force cancel.
- Listed every project's workspaces when
projectIdis omitted, and reserved runtime names in the cloud with a 160-byte cap. - Polled device sign-in while the browser opens, and judged the attempt by its own outcome rather than an existing session.
- Kept CORS headers on edge 429 responses.
grok-4.7 | 𝕏
…q, and never drop device sessions The edge catalog check compares parsed JSON with sorted keys, since workspace builds enable serde_json preserve_order. wrangler.jsonc drops its comment so the deploy script's jq can read it. A cancelled device sign-in whose session cannot be revoked keeps it and says so, and a session that cannot be saved is revoked.
Autovacuum can hold the lock CREATE INDEX CONCURRENTLY waits for after the million-row insert, so the 100ms timeout fired before the invalid index existed on slow runners.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
Re-reviewed the two commits since 583e6e2. They close the cancelled device sign-in gap and fix two checks that were failing around that change.
- Kept device sessions that could not be revoked. A cancelled approval now revokes the cloud session, and if that revoke fails the session is saved and the error tells the person to run
alera account logout. A session whose local save fails is revoked instead of dropped. - Made the tool catalog check order-free. The edge copy is compared as parsed JSON, and keys are sorted before a rewrite, so a workspace build with
serde_json'spreserve_orderno longer fails the string compare. - Let deploy scripts parse the Worker config. The comment in
edge/wrangler.jsoncis gone sojqcan read it. Invocation logs stay off, and the docs now include/oauth/authorizeamong the queries forwarded as form bodies. - Stopped autovacuum from racing the interrupted-index contract.
auth_transactionshas autovacuum disabled around the million-row insert, so the 100ms timeout still leaves an invalid index for the repair check.
grok-4.7 | 𝕏

Summary
Runtimes (desktop sidecar or
alera runtime start) can opt in to MCP Control and become reachable from any MCP client (Claude, ChatGPT, Cursor, Claude Code) through one OAuth-protected endpoint,https://api.alera.build/v1/mcp. Several runtimes can be granted at once; every tool call picks one by name or id (list_runtimeslists them). Full design and contract:docs/remote-mcp.md.mcp:read/mcp:execute, PKCE, single-use codes, rotating refresh tokens, revocation, device authorization for headless runtimes,PUT /v1/runtime/nameand/v1/runtime/capabilities, and a gateway that resolves the runtime, writes a metadata-only audit row, and signs a 120 s per-call grant. Migration0022_remote_mcp.sql.~mcpframes. Phones cannot use~client ids, and runtimes with Remote Access off are hidden from phones.off/read/full) in runtime metadata; the relay link runs when Remote Access or MCP Control is on; call grants are verified against JWKS with replay protection before a tool runs. 31 tools, each a typedalera <group> --jsoninvocation (values passed as--name=value, long text on stdin). New commands:alera account status|login [--device]|logout,alera runtime rename,alera mcp status|enable [--read-only]|disable|apps|revoke|tools|serve.mcp_enabledin OpenTofu,OAUTH_LIMITER/BROWSER_LIMITER/MCP_LIMITERbindings, updated cloud rules, relay and backend docs, and thealera-cliskill.Validation
cloud: fmt, clippy-D warnings,cargo test --workspace(43 unit tests) and the Postgres contracts with--include-ignored(OAuth flow, device flow, gateway, revocation) against a throwaway Postgres 17.edge:bun run check,bun test90/90.rust: clippy-D warnings; fullcargo test -p alera-cli1915/1916. The one failure,output_that_keeps_streaming_is_still_coalesced, is a PTY timing test unrelated to this change and passed 3/3 when rerun.flutter analyzeclean, 23 new unit and widget tests, max-lines ratchet ok.wrangler dev --local, an isolated runtime, and a stub GitHub provider. Covered:alera mcp serveover stdio.fetchwith the wrongthis(workerd "Illegal invocation"), and the runtime's MCP level went stale in the cloud after it was turned off.Risks and follow-ups
https://api.alera.build/oauth/callback(GitHub and Google) must be created and wired through the new variables; until then the native clients are reused and may reject the web callback. The edge needs the new rate-limit namespaces.