rovo
Provides tools for delegating coding tasks to a sandboxed agent that interacts with GitHub: creates a working branch, pushes commits, opens a Draft PR, updates the PR body, and revokes the installation token. Supports task creation, status polling, waiting, result retrieval, cancellation, answering agent questions, and steering running tasks via GitHub App user authorization.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@rovoFix the failing auth tests in my repo and open a draft PR."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
rovo
Claude Code / Cowork から Remote MCP 経由でコーディングタスクを委譲するための Task Gateway(Cloudflare Workers)。
MCP サーバー(gateway)が、GitHub App のユーザー認可でログインした利用者のタスクを受け付ける。
タスクは子エージェント(agent。Cloudflare Containers の隔離された Sandbox で動く)が実行し、作業ブランチへ push して Draft PR を作る。
Web の検索・取得は、別の Worker(web)が非信頼データとして扱い、安全性を評価して返す。
状態: 実験段階。 Phase 6 まで実装済みで、自動テスト(偽の GitHub・偽のモデルを使った E2E を含む)は通っている。実機(Claude Code / Cowork、GitHub、Cloudflare)での受入試験(acceptance.md)が済むまでは、実験段階として扱ってほしい。web_browse は実験的で、既定では無効。
ライセンスは MIT。
設計は docs/specs/design.md、デプロイ手順(rovo を upstream にする下流向け)は docs/deploy.md、手動の受入試験は docs/specs/acceptance.md、Phase ごとの実装計画は docs/specs/phase*.md。
全体の構成
Claude Code / Cowork
| Remote MCP(OAuth)
v
apps/gateway --- service binding ---> apps/web (Web Research Service: search / fetch / browse)
| Durable Object ^
v | service binding
apps/agent (AgentDO -> SandboxDO + Container)
packages/audit (監査ログの共通ライブラリ。3 つの Worker が使う)ディレクトリ | 役割 |
| MCP サーバー、OAuth、タスクの状態( |
| 子エージェント( |
| Web Research Service(状態を持たない別 Worker) |
| 監査イベントを 1 行の構造化 JSON で出す |
Related MCP server: claude-code-mcp
提供するツール(Phase 6 まで実装済み)
GitHub App のユーザー認可でログインし、/mcp から次のツールを使える。
ツール | 内容 |
| タスクを作り、子エージェント( |
| 状態を返す |
| 新しいイベントを最大 |
| 終端になったタスクの成果(要約・検証結果・patch・費用)を返す。終端でなければ |
| キャンセルする(終端状態でも冪等)。子エージェントの停止を確認するまで Gateway が再送する |
| 子エージェントの質問( |
| 実行中の子エージェントに追加の指示を送る |
| Web Search API の結果(url・title・description)を返す。詳細は下の「Web Research Service」 |
| 公開ページを取得して Markdown で返す(JS は実行しない) |
| 実験的・既定で無効。 JS を描画した後のページを取得する |
制約(ツールの応答にも明記している)
Phase 3(M1: Gateway 側):
task_startが作業ブランチagent/task-<id>と Draft PR を作る。URL はtask_status・task_result(pr)・イベントdraft_pr_createdで返る(task_startの応答のdraft_pr_urlは作成前なのでnull)。終端で PR 本文の rovo 管理範囲を更新し、Installation Token を失効する。成果は patch としてもtask_resultで返る。idempotency_keyを省略すると、再試行の重複排除は保証されない。利用者ごとの同時実行は 1 件まで。前のタスクの停止を確認できるまで、次のタスクは始められない。
1 タスクにつき既定で $10(env
TASK_BUDGET_USD。0 以下・不正な値は 10)を月額の枠(envMONTHLY_BUDGET_USD、既定 100)から予約する。子エージェントの予算の上限(budgetUsd)にも同じ値を渡す。足りなければbudget_exceeded。月額の枠を 10 未満にするときはTASK_BUDGET_USDも下げる(受入試験で数ドルに抑えたいとき)。
Web Research Service(web_search / web_fetch / web_browse)
親(MCP)の web_search / web_fetch の説明。子(apps/agent の Pi)にも同名のツールがある。どちらも apps/web を service binding で呼ぶ。
ツール | 内容 |
|
|
|
|
本文(
content)・title・検索の description は、Web 上の第三者が書いた非信頼データ。親には<<<UNTRUSTED_WEB_CONTENT ...>>>と<<<END_UNTRUSTED_WEB_CONTENT>>>で囲んで返し(本文中の<<<は全角に置き換える)、子には既存の<<<BEGIN_UNTRUSTED_OUTPUT>>>で囲んで渡す。safety.verdict:allow(問題なし)/caution(不審な表現があるので慎重に)/block(注入などを検出。本文はnull)/unknown(評価できなかった。本文はnull)。検索は結果全体で 1 つの判定で、block/unknownのときは description を落とし、title も(title withheld)にして URL だけを返す。回数の上限
親: ユーザーごとに UTC の 1 日あたり search 200 回・fetch 500 回(
UserDOが数える)。超えるとquota_exceeded(retry_after_secondsは翌日 0 時 UTC まで)。envWEB_SEARCH_DAILY_LIMIT/WEB_FETCH_DAILY_LIMITで変える。web_browse は別枠: 親は 1 日 50 回(
WEB_BROWSE_DAILY_LIMIT)、子はタスクごとに 20 回(WEB_BROWSE_LIMIT_PER_TASK)。子: タスクごとに search 50 回・fetch 100 回(
AgentDOが数える)。超えるとツールのエラー。apps/agentの envWEB_SEARCH_LIMIT_PER_TASK/WEB_FETCH_LIMIT_PER_TASKで変える。回数はusageイベント(web_search/web_fetch/web_browse)とtask_resultのusage.webに出る。回数は呼び出しの前に数える(失敗しても戻さない)。web 自体には回数の上限を持たない(AI Gateway のレート制限とアカウントの上限に頼る)。
web_browse(JS を描画した後のページを Markdown で取得。Browser Run の Kitesurf)は既定で無効。apps/web・apps/agent・apps/gatewayの envWEB_BROWSE_ENABLEDを"true"にした Worker だけが有効になる(無効なら親の tools/list にも子の Pi のツールにも出ない)。{url}だけを受け、statusはok/blocked(bot チャレンジ・robots・redirect や遷移先が private・許可外・https→http・private に解決される名前)/http_error(4xx・5xx。本文なし)/unknown(判定できなかった。本文なし)。保証するのは「禁止先・判定不能の取得結果を返さない」ことまでで、禁止先への通信の発生は防げない。有効にする前に、docs/specs/acceptance.mdの 11 節「web_browse(有効化前のゲート)」の残存リスクを読み、受け入れるかを判断してほしい。 実機のゲートは任意で、作者は行わずに有効にしている。
構成
apps/web(Phase 5): Web Research Service の別 Worker。WebResearch(WorkerEntrypoint)の RPCsearch/fetchUrl(fetchは HTTP のハンドラ名なので使えない)。呼び出し元の種別は service binding のprops.caller(gateway はparent、agent はchild)から取り、props が無い・不正なら拒否する。状態を持たず、持つバインディングはaiだけ(GitHub のトークンや Anthropic のキーは持たない)。公開のルートは無い(workers_dev: false)apps/gateway: Cloudflare Worker。Hono でルーティングし、@cloudflare/workers-oauth-providerで全体を包む。/mcp(要 OAuth):agents/mcp/serverのcreateMcpHandler+@modelcontextprotocol/server。リクエストごとに McpServer を作り、ユーザー情報は共有しない/authorize(同意画面)→ GitHub App のユーザー認可 →/callback: defaultHandler。state(1 回限り・10 分)と、開始したブラウザへの Cookie 紐付けは OAuthProvider のbeginUpstream/finishUpstreamを使うUserDO(ユーザーごと): GitHub 資格(AES-GCM で暗号化)、team 所属のキャッシュ(TTL 5 分)、同時実行の予約、月額予算(タスクごとの予約・usage の冪等な精算・終了時の返却)。GitHub のトークンは DO の外へ返さないTaskDO(タスクごと): 状態とイベント。AgentDO への start / cancel の配送、イベントの取り込み、usage の精算を alarm で収束させる
apps/agent: 子エージェントの別 Worker。SandboxDO(ctx.containerをenableInternet:falseで起動し、外向き通信をOutboundGatewayに傍受させる)、OutboundGateway(許可判定はsrc/outbound-policy.tsの純粋関数)、AgentDO(PiHarnessと台本付き fake モデルでの最小実装)。Gateway からはscript_name付きの DO バインディングAGENT_DOで呼ぶ
同意画面(confused deputy 対策)
DCR(動的クライアント登録)が有効なため、/authorize は GitHub へ行く前に必ず同意画面を表示する。悪意あるクライアントが利用者に認可を開始させても、利用者が承認しない限り MCP のアクセストークンは渡らない。
表示: クライアント名(
client_name。自己申告で未検証)、redirect_uriのホスト(目立たせて表示)とフル URI、要求 scope、「このアプリにあなたの rovo タスクの作成・操作を許可しますか」。送り先のホストに見覚えがなければ拒否する。承認すると GitHub のログインへ進む。拒否すると client の
redirect_uriへerror=access_deniedで戻る。CSRF 対策: フォームの値は 1 回限りの handle で、
__Host-の Cookie(Secure・HttpOnly・SameSite=Lax)と突き合わせる。別のサイトや別のブラウザからの送信、再送は 400 になる。クリックジャッキング対策として
X-Frame-Options: DENYと CSP(default-src 'none'・frame-ancestors 'none'など)を付ける。form-actionは付けない(Chrome が POST 後の redirect 先にも適用し、承認・拒否後の redirect を壊すため)。承認の記憶はしない。ログインのたびに同意画面が出る。
開発手順
ランタイム(node / pnpm)は mise で固定している。コマンドはすべて mise run 経由で実行する。
Docker が必要: apps/agent の E2E(mise run test:e2e:docker、したがって mise run test)は wrangler dev が Docker でコンテナ(apps/agent/Dockerfile)をビルド・起動する。Docker Desktop など、ローカルで Docker が動いていること。初回はイメージのビルド(cloudflare/sandbox:1.0.1 の取得を含む)に数分かかる。
E2E の実行中は mise run dev:all を起動しない: E2E の後始末は「その起動中に増えた workerd-rovo-agent-* コンテナ」を docker rm -f で消す。E2E 同士はロック($TMPDIR/rovo-e2e-docker.lock)で直列化されるが、dev:all はロックを取らないため、並走すると開発セッションのコンテナが消えることがある。また E2E は apps/*/.dev.vars を起動中だけ書き換える(元のファイルは .dev.vars.rovo-e2e-backup に退避し、異常終了した場合も次の起動の冒頭で元に戻す)。
mise install # node / pnpm を入れる
mise run install # 依存パッケージ(pnpm install --frozen-lockfile)
mise run test # Unit + 偽 GitHub + E2E
mise run lint # Biome + 型検査タスク | 内容 |
| gateway・agent・web の Unit。vitest + |
| 偽 GitHub サーバーと GitHub クライアントの結合テスト(Node) |
|
|
|
|
| Docker 必須。 |
| Docker 必須。gateway・agent・web の 3 つの Worker を同時に |
| Docker 必須・実ネットワーク。上の E2E の実ネットワーク版。実 registry.npmjs.org への GET / |
| Biome の lint / 整形、 |
|
|
| gateway・agent・web を同時にローカル起動する(Docker 必須)。 |
| Worker のバンドル確認( |
|
|
|
|
|
|
| 実際にデプロイする( |
| ローカル起動( |
E2E が失敗したときは E2E_DEBUG=1 mise run test:e2e で wrangler dev の出力も表示される。E2E は GitHub に接続しない(偽 GitHub を使う)。
E2E の診断ログ: E2E(Docker を使うもの)は、テストを流した場所の外に診断ログを常に残す。保存先は $TMPDIR/rovo-e2e-logs/<タイムスタンプ>-xxxxxx/(テストファイルごとに 1 つ。新しい 30 個を残して古いものは消える。ROVO_E2E_LOG_DIR でルートを変えられる)。
wrangler-<設定名>.log:wrangler devの出力(stdout / stderr)。E2E の終了時に[rovo-e2e] wrangler のログ: <パス>と表示される。起動に失敗したときのエラーにもパスが入るtiming.log: 主要な段階(wrangler ready/wrangler stop、Sandbox のensure-ready/exec/stop(reaper)/terminate/destroy、Agent の各 RPC)の所要時間。失敗した段階はfailedで記録される。E2E_DEBUG=1なら stderr にも出る(タイムアウトの値は変えていない)
設定
デプロイ時に用意する値の完全な一覧は docs/deploy.md の 5 にある(食い違う場合は deploy.md を正とする)。
変数(wrangler.jsonc の vars、またはローカルは .dev.vars)
変数 | 内容 |
|
|
| 公開 URL。OAuth の issuer と |
| GitHub App の Client ID(秘密ではない) |
| ログインと全ツール呼び出しを許す org と team の slug。子 team のメンバーも許可される。対で設定する(片方だけだとログインできない)。 |
| ログインと全ツール呼び出しを許す GitHub の数値の user ID(カンマ区切り。 |
| GitHub App の App ID(秘密ではない)。Installation Access Token の発行(JWT の |
| Draft PR を作れないリポジトリ(Free プランの private リポジトリなど)での扱い。 |
|
|
Secret(wrangler secret put <NAME>、ローカルは .dev.vars)
Secret | 内容 |
| GitHub App の Client secret |
| GitHub App の秘密鍵(PKCS#8 の PEM)。GitHub がダウンロードさせるのは PKCS#1( |
| (gateway)GitHub のトークンを UserDO に暗号化して保存する鍵。base64 の 32 バイト( |
agent(apps/agent)の secret / 変数
名前 | 内容 |
| Installation Token を SandboxDO の storage に暗号化して保存する鍵。base64 の 32 バイト( |
| AI Gateway の ID。空だと |
| 子エージェントのモデル(既定 |
|
|
| WEB_SEARCH_LIMIT_PER_TASK / WEB_FETCH_LIMIT_PER_TASK / WEB_BROWSE_LIMIT_PER_TASK(変数) | 子の web_search / web_fetch / web_browse のタスクごとの回数の上限(既定 50 / 100 / 20)。0 以上の整数のみ。不正な値は既定に戻る |
| WEB_BROWSE_ENABLED(変数) | 子に web_browse を出すか("true" のときだけ。既定 "false") |
| WEB(service binding) | apps/web の WebResearch(props: {caller: "child"})。デプロイは web → agent → gateway の順 |
ローカルは apps/agent/.dev.vars.example を apps/agent/.dev.vars にコピーして埋める(mise run dev:all)。
web(apps/web)の変数
secret は無い(GitHub のトークンも Anthropic のキーも持たせない)。バインディングは ai(Workers AI)だけ。
変数 | 内容 |
| AI Gateway の ID。必須。空だと |
| 検索プロバイダ( |
| Policy Engine の閾値(既定 0.8 / 0.4)。モデルのスコアがこれ以上なら block / caution。仮の値で、受入試験で調整する |
| E2E 専用。 |
gateway の web 関連
名前 | 内容 |
|
|
| 親の |
| 親の MCP に |
デプロイの順序: web → agent → gateway。service binding は、参照される側の Worker(rovo-web)が先に存在している必要がある。agent は gateway から script_name で参照される。
デプロイ
rovo は誰が clone しても、自分の値を渡せばデプロイできるようにしてある。upstream が次の 2 つを持つ。
infra/: Cloudflare のリソース(R2 バケット 2 つ〔アーカイブ用・監査ログ用〕、KV、AI Gateway、監査ログの Logpush)を作る Terraform。使い方は infra/README.md。deploy:*の mise タスク:ROVO_DEPLOY_DIR(下流の wrangler 設定のディレクトリ。雛形は deploy.example/)を指定してmise run deploy:all(web → agent → gateway)。secret はmise run deploy:secrets。未設定なら何もせずに失敗する。
下流(デプロイする人の private リポジトリ)は、rovo を upstream として merge し、deploy/ の wrangler 設定、tfvars、backend を新しいファイルとして足すだけで使う。手順の全体(Terraform apply → AI Gateway に BYOK → GitHub App → wrangler 設定 → deploy:secrets → deploy:all)は docs/deploy.md の 4。MCP Server Portal と Access の登録はこの範囲外。
運用設定(下流向け: 監査ログ・R2 アーカイブ・単価)
rovo を upstream にする private リポジトリ(下流)が、自分の wrangler 設定とインフラで設定するもの。rovo の wrangler.jsonc は環境固有の値を持たない。まとまった手順は docs/deploy.md にある。以下は要点で、食い違う場合は deploy.md を正とする。
監査ログが「監査」として成立する条件
監査イベントは 1 行の構造化 JSON({"audit":true,"event":...})を console.log に出す(イベントの一覧は phase6.md)。Workers Logs に出るだけでは head sampling と短い保持のために行が欠落しうるので、best-effort でしかない。監査として成立させるには、下流で次の両方を設定する。
Workers Logs の head sampling を 100% にする(
observabilityのhead_sampling_rate: 1。observability.logs.head_sampling_rateでもよい)。rovo のwrangler.jsoncにはobservabilityを置かない。Logpush で、保持期間を自分で決められる送り先へ流す(送り先は下流の判断)。
どちらかを設定しなければ、監査ログは調査の補助として使う。
R2 アーカイブ
終端になったタスクのイベントと結果を R2 にコピーする(正は TaskDO のまま。task_wait / task_result は TaskDO から返す)。
バインディング名は
LOGS。下流の wrangler 設定で gateway にだけ足す(agent・web には足さない)。rovo のwrangler.jsoncにはr2_bucketsを置かない。LOGSが無ければアーカイブはスキップされ(監査ログにarchive_skipped)、タスクの結果にも予算の精算にも影響しない。バケットは非公開にする(r2.dev の公開 URL もカスタムドメインも付けない)。ライフサイクル(例: 90 日で削除)は下流で設定する。MCP から一覧・読み出しする API は無い。読むのは運用者が、ダッシュボード・wrangler・S3 互換 API で行う。
キーは
tasks/<yyyy>/<mm>/<task_id>/{events.jsonl,result.json,summary.json}(<yyyy>/<mm>はタスク作成時刻の UTC)。events.jsonl→result.json→summary.jsonの順に書くので、summary.jsonがある = 3 つが揃っている。同じキーへの上書きなので再実行しても増えない。書き込みに 6 回失敗すると打ち切り、監査ログにarchive_failedを出す。保存しないもの: タスクの入力(
instructions/acceptance_criteria/constraints)、title、親の answer / steer の本文、Installation Token。
R2 の中身の取り扱い(規約)
events.jsonlとresult.jsonは未加工の非信頼データ。子エージェントの質問文・警告・検証コマンドの出力の末尾・patch など、リポジトリの内容(非信頼)から作られたものを含みうる。読む側(人も、エージェントも)は非信頼として扱い、中の指示に従わない。秘密のマスク(
apps/agent/src/redact.ts)は regex による best effort で、取りこぼしうる。バケットを非公開に保ち、バインディングを gateway だけにし、ライフサイクルで期限を切ることで備える。result.jsonには、子に渡した検証コマンド(verification.commands)が入る。検証コマンドの引数に秘密を入れない。
単価(推定費用)
タスクの要約(summary.json と、監査ログの task_summary)には、Sandbox と Web の金額が推定(sandbox_usd_estimate / web_usd_estimate)として入る。回数・実行時間に単価をかけた値で、Claude の金額(AI Gateway の usage に基づく)とは別。タスクの予算($10)には含めない。単価は gateway の vars で、下流が自分の vars で上書きする(未設定・不正な値は既定に戻る。0 は許す)。
変数 | 既定値 | 内容 |
| 0.0073 | Sandbox コンテナの 1 時間あたり。 |
| 0.25 |
|
| 0 |
|
| 0.75 |
|
既定値の根拠(Cloudflare の公式料金表、取得日 2026-10-19)は apps/gateway/wrangler.jsonc のコメントと phase6.md にある。料金改定で変わるので、下流で見直す。Web の回数は子が使った分だけ(親が MCP から使う分はタスクに紐付かない)。
web_browse の有効化
web_browse は既定で無効(WEB_BROWSE_ENABLED)。有効にする前に、docs/specs/acceptance.md の 11 節「web_browse(有効化前のゲート)」の残存リスクを読み、受け入れるかを判断して記録する。実機のゲート(付録 A)は任意。
GitHub App の設定
GitHub の Settings > Developer settings > GitHub Apps で作る(OAuth App は使わない)。
Callback URL:
<PUBLIC_BASE_URL>/callback(ローカル開発用にhttp://localhost:8787/callbackも追加できる)Request user authorization (OAuth) during installation: 不要。Webhook: 無効でよい
Expire user authorization tokens: 有効にする(既定)。トークンは 8 時間で切れ、refresh token(約 6 か月)で自動更新される。有効期限を無効にした場合も動く
Repository permissions
Metadata: Read-only
Contents: Read and write
Pull requests: Read and write
Organization permissions
Members: Read-only(team 所属の確認に使う)
App を対象の org / リポジトリに Install する。
task_startは、利用者のトークンで見える Installation に対象リポジトリが含まれ、利用者に push 権限があるときだけ成功する(公開リポジトリでも App が未導入ならforbidden)
ローカル開発
cp apps/gateway/.dev.vars.example apps/gateway/.dev.vars # 値を埋める(.dev.vars は gitignore 済み)
mise run dev__Host- 付きの Cookie(state の紐付け)は Secure なので、ブラウザは Chrome のように loopback の http で Secure Cookie を受け付けるものを使う。
エラー
ツールのエラーは isError: true で、本文は {"code", "message", "retry_after_seconds"?} の JSON。code は
not_found / conflict / expired / concurrency_limit / budget_exceeded(月額の枠が足りず task_start を拒否。待っても空かない)/ quota_exceeded(web の1日の上限)/ not_ready(task_result の対象がまだ終端でない)/ invalid_cursor / forbidden / reauth_required / upstream_unavailable / invalid_input / internal。
reauth_required は GitHub 資格を回復できないとき(MCP のトークンが有効でも)で、再ログインが必要。internal は想定外の失敗で、詳細は応答に含めない。
セキュリティ
脆弱性は公開の Issue に書かず、GitHub の private vulnerability reporting で報告する。詳細は SECURITY.md。
コントリビューション
個人プロジェクトとして公開している。Issue と Pull Request は歓迎するが、返信や取り込みの時期は約束できない。大きな変更は、先に Issue で相談してほしい。
ライセンス
This server cannot be deployed
Maintenance
Related MCP Connectors
Remote streamable-HTTP MCP server running on a single Cloudflare Worker. Your assistant gets live Airbnb, Amazon, Booking.com, Google Flights, Maps and Reddit data, social search on X, Instagram and TikTok, the Meta Ad Library, and image/video generation without any keys. Connect your own accounts to let it send WhatsApp or Telegram messages, work an IMAP inbox, manage Meta Ads campaigns and publish to X and LinkedIn. OAuth 2.1 with PKCE; stored credentials are AES-256-GCM encrypted.
Connect Claude.ai, ChatGPT and MCP-compatible coding clients to aictrl engineering workflows. Check account readiness, find and run tasks, inspect workflow progress and approvals, review pull-request findings, and query repository context. Uses OAuth and your aictrl organisation's permissions.
AI-native git hosting — repos, PRs, issues, CI gates, and AI code review over MCP (60 tools).
Hosted coding agent: start, follow up on and stop tasks that can open GitHub pull requests.
Related MCP Servers
- AlicenseAqualityFmaintenanceEnables Claude or any MCP client to drive the GitHub Copilot cloud agent via its REST API, allowing it to research, edit, and open pull requests on repositories without manual intervention.11MIT
- AlicenseAqualityDmaintenanceWraps Claude Code as tools for MCP clients, enabling autonomous coding tasks via a 4-tool lifecycle with session management, async polling, and permission controls.434 npm20MIT
- FlicenseNot gradedqualityBmaintenanceEnables GitHub repository operations (list/read files, create branches, commit files, open/list PRs) via an authless remote MCP server that keeps your GitHub token encrypted on Cloudflare, with access limited to allowed repositories.-
- AlicenseNot gradedqualityAmaintenanceEnables AI coding agents and human managers to coordinate work on GitHub Projects v2 through 20 tools and 169 actions for autonomous task checkout, heartbeats, reviews, token budgets, PRD generation, sprint planning, issue triage, and PRD-to-issues pipelines, backed by GitHub-native storage and MCP resources/prompts.95 npmMIT