Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 27 additions & 20 deletions docs/0-requirements.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,28 @@ GitHub --POST--> Cloudflare Worker --> TenantRegistry DO
| v
| WebhookStore DO (SQLite) [per-tenant]
| |
+-- /mcp (Streamable HTTP) +-- SSE real-time stream
+-- /mcp (Streamable HTTP) +-- WebSocket / SSE real-time stream
| WebhookMcpAgent DO +-- REST endpoints
| [per-tenant] /pending-status
| +-- tools -> WebhookStore /pending-events
| /webhook-events
+-- /events (SSE) /event
+-- /events (WebSocket/SSE) /event
| +-- WebhookStore DO /mark-processed
|
+-- /oauth/authorize --> github.com/login/oauth/authorize
+-- /oauth/callback <-- github.com redirect_uri (Worker-hosted)
+-- /oauth/token (web_authorization_poll, refresh_token)
|
+-- /webhooks/github (POST)
+-- TenantRegistry -> WebhookStore DO /ingest

+-----------------------------+
| Local MCP Bridge (.mcpb) |
| stdio <- Claude Desktop/CLI |
| -> proxy tool calls to /mcp |
| -> SSE listener -> channel |
| -> WebSocket listener -> channel |
| -> browser: /oauth/authorize |
| -> poll: /oauth/token |
+-----------------------------+
```

Expand Down Expand Up @@ -144,27 +150,28 @@ WebhookMcpAgent DO が以下のツールセットを提供する。ローカル
| 3 | フルペイロードが必要なイベントのみ `get_event(event_id)` で取得 |
| 4 | 処理完了後 `mark_processed(event_id)` でマーク |

### F7. OAuth 認証(Device Authorization Grant, RFC 8628
### F7. OAuth 認証(Worker-hosted web OAuth

Worker は OAuth 2.1 Device Authorization Grant を自前実装する(`@cloudflare/workers-oauth-provider` は v0.11.0 で撤去。GitHub App の device flow を upstream として利用し、localhost callback に依存しない
Worker は GitHub の web OAuth flow をホストする独自実装を備える(v0.11.0 の device authorization grant は v0.11.1 で撤去。GitHub 標準のログイン + 2FA UX に回帰しつつ、v0.10.x の chronic auth loop 原因である localhost callback 依存と refresh rotation desync を構造的に解消する)

| ID | 要件 |
|----|------|
| F7.1 | `GET /.well-known/oauth-authorization-server` で RFC 8414 メタデータを返す |
| F7.1 | `GET /.well-known/oauth-authorization-server` で RFC 8414 メタデータを返す(`authorization_endpoint` / `token_endpoint` / `grant_types_supported=[urn:ietf:params:oauth:grant-type:web_authorization_poll, refresh_token]`) |
| F7.2 | `POST /oauth/register` で RFC 7591 dynamic client registration を行う(public client、secret 発行なし) |
| F7.3 | `POST /oauth/device_authorization` で GitHub に device code を要求し、RFC 8628 §3.2 形式の JSON(device_code / user_code / verification_uri / verification_uri_complete / expires_in / interval)を返す |
| F7.4 | `POST /oauth/token` で `grant_type=urn:ietf:params:oauth:grant-type:device_code` を処理し、GitHub への polling 結果に応じて `authorization_pending` / `slow_down` / `access_denied` / `expired_token` を RFC 8628 §3.5 準拠で返す |
| F7.5 | `POST /oauth/token` で `grant_type=refresh_token` を処理し、access token と refresh token を rotate する |
| F7.6 | 旧 `GET /oauth/authorize` および `GET /oauth/callback` は **HTTP 410 Gone** を返す(localhost callback flow は v0.11.0 で廃止) |
| F7.3 | `GET /oauth/authorize?client_id=<cid>&state=<state>[&scope=...]` で `web_auth_state:{state}` レコードを `pending` として作成し、`redirect_uri=https://<worker>/oauth/callback` を固定して `https://github.com/login/oauth/authorize` に 302 リダイレクトする |
| F7.4 | `GET /oauth/callback?code=<gh_code>&state=<state>` で GitHub authorization code を confidential client として access token に交換し、`fetchGitHubProps()` で user profile + installations を取得、Worker 独自 bearer token pair を発行して `web_auth_state` を `approved` に遷移させる。ユーザにはタブを閉じるよう案内する HTML を返す |
| F7.5 | `POST /oauth/token` で `grant_type=urn:ietf:params:oauth:grant-type:web_authorization_poll` を処理する。`pending` → `400 authorization_pending`、`approved` → `200` で bearer pair を返し state レコードを消費、`denied` → `400 access_denied`、期限切れ → `400 expired_token`(RFC 8628 §3.5 のエラー形式を再利用) |
| F7.6 | `POST /oauth/token` で `grant_type=refresh_token` を処理し、access token と refresh token を rotate する(ブリッジ側 RC1 修正と組み合わせて desync を解消) |
| F7.7 | 保護対象 API ルート(`/mcp`, `/events`)は `Authorization: Bearer <access_token>` ヘッダによる独自 token 検証 middleware で認可する |
| F7.8 | KV schema は自前設計: `client:{client_id}` / `device:{device_code}` / `user_code:{user_code}` / `token:{access_token}` / `refresh:{refresh_token}` / `grant:{grant_id}` |
| F7.9 | ローカルブリッジは device authorization 応答受信直後に `verification_uri_complete`(なければ `verification_uri`)を platform 既定のブラウザで自動オープンする。Windows は `cmd /c start`、macOS は `open`、Linux は `xdg-open` を使う。オープン失敗は fatal にしない(stderr に警告を残し、URL は応答と stderr で伝える) |
| F7.10 | ローカルブリッジは初回ツール呼び出しで device flow が完了していない場合、polling をバックグラウンドに維持したまま、`user_code` / `verification_uri_complete` / `verification_uri` / 残り有効秒数を本文に含む `isError: true` の構造化ツール応答を即座に返す。2 回目以降の同一ツール呼び出しは、承認完了なら通常処理、未完了なら同じ auth-required 応答を返す(ポーリングは 1 本に serialize) |
| F7.8 | KV schema は自前設計: `client:{client_id}` / `web_auth_state:{state}` / `token:{access_token}` / `refresh:{refresh_token}` / `grant:{grant_id}`。device flow 時代の `device:` / `user_code:` キーは撤去 |
| F7.9 | ローカルブリッジは authorize URL を platform 既定のブラウザで自動オープンする。Windows は `cmd /c start`、macOS は `open`、Linux は `xdg-open` を使う。オープン失敗は fatal にしない(stderr に警告を残し、URL は応答と stderr で伝える) |
| F7.10 | ローカルブリッジは初回ツール呼び出しで web flow が完了していない場合、polling をバックグラウンドに維持したまま、authorize URL と残り有効秒数を本文に含む `isError: true` の構造化ツール応答を即座に返す。2 回目以降の同一ツール呼び出しは、承認完了なら通常処理、未完了なら同じ auth-required 応答を返す(ポーリングは 1 本に serialize) |
| F7.11 | ローカルブリッジは refresh 時に `invalid_grant` を受けた場合、直ちに全面 re-auth に遷移せず tokens file を再読み込みする。別プロセスが既に rotation を完了していれば、その最新 refresh_token を採用して再試行する(RC1: refresh desync の最小 fix。file lock は導入しない) |

**GitHub App 前提条件:**

- GitHub App の設定で **"Enable Device Flow"** を有効化する必要がある(未有効時は `device_flow_disabled` が返る)
- 使用する upstream endpoint: `POST https://github.com/login/device/code`, `POST https://github.com/login/oauth/access_token`
- 使用する upstream endpoint: `https://github.com/login/oauth/authorize`(web), `POST https://github.com/login/oauth/access_token`
- GitHub App の設定で `redirect_uri = https://<worker>/oauth/callback` を登録する必要がある(smgjp.com プレビュー + self-host 例示)

## 非機能要件

Expand All @@ -189,7 +196,7 @@ Worker は OAuth 2.1 Device Authorization Grant を自前実装する(`@cloudf
| N2.2 | シークレット | Cloudflare Secret `GITHUB_WEBHOOK_SECRET` | なし(検証スキップ) |
| N2.3 | チャンネル通知の有効/無効 | `WEBHOOK_CHANNEL` | 有効(`0` で無効) |
| N2.4 | カスタムドメイン | `github-webhook.smgjp.com` | Cloudflare Worker のカスタムドメインとして設定済み |
| N2.5 | 認証方式 | Worker 自前認証 | Cloudflare Access は使用しない。Worker が webhook secret + OAuth Device Authorization Grant (RFC 8628) で認証を処理する |
| N2.5 | 認証方式 | Worker 自前認証 | Cloudflare Access は使用しない。Worker が webhook secret + Worker-hosted web OAuth で認証を処理する |
| N2.6 | プレビューインスタンス | `preview` 環境 | 本番と同一構成の検証用インスタンス |

### N3. 制約
Expand All @@ -199,7 +206,7 @@ Worker は OAuth 2.1 Device Authorization Grant を自前実装する(`@cloudf
| N3.1 | WebhookStore / McpAgent DO はテナント別インスタンス(`idFromName("store-{accountId}")` / `getAgentByName("tenant-{accountId}")`)で動作する。TenantRegistry DO は単一インスタンスで全テナントの installation-account マッピングを管理する |
| N3.2 | SSE 接続は DO のメモリ内で管理される(DO eviction 時に切断) |
| N3.3 | ローカルブリッジはツール呼び出しごとに Worker セッションを再利用する(セッション失効時は自動リトライ) |
| N3.4 | Device flow 完了時に `GET /user/installations` で取得した accessible_account_ids(ユーザー + org)を GitHubUserProps に保存し、McpAgent が複数 store を並列クエリして結果をマージする。これにより org インストールのイベントもメンバーの MCP セッションから参照できる |
| N3.4 | Web OAuth callback 処理時に `GET /user/installations` で取得した accessible_account_ids(ユーザー + org)を GitHubUserProps に保存し、McpAgent が複数 store を並列クエリして結果をマージする。これにより org インストールのイベントもメンバーの MCP セッションから参照できる |

## CI/CD

Expand Down Expand Up @@ -233,7 +240,7 @@ Worker は OAuth 2.1 Device Authorization Grant を自前実装する(`@cloudf
| @modelcontextprotocol/sdk | MCP SDK |
| zod | スキーマバリデーション |

OAuth 実装は自前(`worker/src/oauth.ts` + `worker/src/oauth-store.ts`)。`@cloudflare/workers-oauth-provider` は v0.11.0 で撤去済み(device flow 非対応のため)。
OAuth 実装は自前(`worker/src/oauth.ts` + `worker/src/oauth-store.ts`)。`@cloudflare/workers-oauth-provider` は v0.11.0 で撤去済み。v0.11.1 で Worker-hosted web OAuth に切り替え(device authorization grant は撤去)。

### ローカルブリッジ (mcp-server/)

Expand All @@ -252,8 +259,8 @@ Node.js >= 18.0.0 が必要。
| `worker/src/agent.ts` | WebhookMcpAgent DO(MCP ツール定義、テナント別インスタンス) |
| `worker/src/store.ts` | WebhookStore DO(SQLite + SSE、テナント別インスタンス) |
| `worker/src/tenant.ts` | TenantRegistry DO(installation-account マッピング、クォータ管理) |
| `worker/src/oauth.ts` | OAuth Device Authorization Grant (RFC 8628) 自前実装(metadata / register / device_authorization / token / 独自 token 検証 middleware) |
| `worker/src/oauth-store.ts` | OAuth KV schema helper(client / device / user_code / token / refresh / grant レコード操作) |
| `worker/src/oauth.ts` | Worker-hosted web OAuth 自前実装(metadata / register / authorize / callback / token / 独自 token 検証 middleware) |
| `worker/src/oauth-store.ts` | OAuth KV schema helper(client / web_auth_state / token / refresh / grant レコード操作) |
| `worker/wrangler.toml` | Worker デプロイ設定 |
| `shared/src/types.ts` | 共有型定義 |
| `shared/src/summarize.ts` | イベントサマリー生成 |
Expand Down
Loading
Loading