From aaf482a295f7e4a96230fe80335b9da4e30f07fb Mon Sep 17 00:00:00 2001 From: Lin & Lay Date: Wed, 25 Mar 2026 19:54:28 +0900 Subject: [PATCH] Update README and docs for Cloudflare Worker architecture (#56) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README を新アーキテクチャ(Cloudflare Worker + DO)に全面書き換え。 旧構成(Python receiver, NSSM, Cloudflare Tunnel, events.json)を削除。 docs/0-requirements.md も Worker/DO/SSE ベースの仕様に更新。 refs #56 Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 235 ++++++++++-------------------------- docs/0-requirements.md | 262 +++++++++++++++-------------------------- 2 files changed, 153 insertions(+), 344 deletions(-) diff --git a/README.md b/README.md index 76d30ba..dfbbd1d 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,31 @@ # github-webhook-mcp -GitHub webhook receiver and local MCP extension for GitHub notification workflows. +Real-time GitHub webhook notifications for Claude via Cloudflare Worker + Durable Object. -## Description +## Architecture -`github-webhook-mcp` receives GitHub webhook events, persists them to a local `events.json`, and exposes them to AI agents through MCP tools. -It is designed for notification-style workflows where an AI can poll lightweight summaries, inspect a single event in detail, and mark handled events as processed. - -Detailed behavior, event metadata, trigger semantics, and file responsibilities live in [docs/0-requirements.md](docs/0-requirements.md). - -## Features +``` +GitHub ──POST──▶ Cloudflare Worker ──▶ Durable Object (SQLite) + │ + ├── MCP tools (Streamable HTTP) + ├── SSE real-time stream + │ + ┌────────────────┘ + │ + Desktop / Codex: .mcpb local bridge ──▶ polling via MCP tools + Claude Code CLI: .mcpb local bridge ──▶ SSE → channel notifications +``` -- Receives GitHub webhook events over HTTPS and persists them locally. -- Exposes pending events to MCP clients through lightweight polling tools. -- Supports real-time `claude/channel` notifications in Claude Code. -- Supports direct trigger mode for immediate Codex reactions per event. -- Ships as a Node-based `.mcpb` desktop extension and as an `npx` MCP server. +- **Cloudflare Worker** receives GitHub webhooks, verifies signatures, stores events in a Durable Object with SQLite. +- **Local MCP bridge** (.mcpb) proxies tool calls to the Worker and optionally listens to SSE for real-time channel notifications. +- No local webhook receiver or tunnel required. ## Prerequisites -| Component | Required for | Install | -|-----------|-------------|---------| -| **Python 3.12+** | Webhook receiver | [python.org](https://www.python.org/downloads/) — check "Add python to PATH" during install | -| **Node.js 18+** | MCP server (`npx`) | [nodejs.org](https://nodejs.org/) | -| **NSSM** *(optional)* | Running receiver as a Windows service | [nssm.cc](https://nssm.cc/) — extract and add to PATH | - -> Only install what you need. If you only use the `.mcpb` desktop extension, Python alone is sufficient for the receiver. +| Component | Required | +|-----------|----------| +| **Node.js 18+** | MCP server | +| **Cloudflare account** | Worker deployment (self-hosting) | ## Installation @@ -33,13 +33,11 @@ Detailed behavior, event metadata, trigger semantics, and file responsibilities Download `mcp-server.mcpb` from [Releases](https://github.com/Liplus-Project/github-webhook-mcp/releases), then: -1. Open Claude Desktop → **Settings** → **Extensions** → **Advanced settings** → **Install Extension...** +1. Open Claude Desktop → **Settings** → **Extensions** → **Install Extension...** 2. Select the `.mcpb` file -3. Enter the path to your `events.json` when prompted - -### Claude Desktop / Claude Code — npx +3. Enter your Worker URL when prompted (e.g. `https://github-webhook-mcp.example.workers.dev`) -Add to your Claude MCP config (`claude_desktop_config.json` or project settings): +### Claude Code CLI — npx ```json { @@ -48,13 +46,16 @@ Add to your Claude MCP config (`claude_desktop_config.json` or project settings) "command": "npx", "args": ["github-webhook-mcp"], "env": { - "EVENTS_JSON_PATH": "/path/to/events.json" + "WEBHOOK_WORKER_URL": "https://github-webhook-mcp.example.workers.dev", + "WEBHOOK_CHANNEL": "1" } } } } ``` +Set `WEBHOOK_CHANNEL=1` to enable real-time channel notifications (Claude Code CLI only). + ### Codex — config.toml ```toml @@ -63,189 +64,71 @@ command = "npx" args = ["github-webhook-mcp"] [mcp.github-webhook-mcp.env] -EVENTS_JSON_PATH = "/path/to/events.json" +WEBHOOK_WORKER_URL = "https://github-webhook-mcp.example.workers.dev" +WEBHOOK_CHANNEL = "0" ``` -### Python (legacy) +## Self-Hosting the Worker -```json -{ - "mcpServers": { - "github-webhook-mcp": { - "command": "python", - "args": ["/path/to/github-webhook-mcp/main.py", "mcp"] - } - } -} -``` - -## Configuration - -### 1. Install receiver dependencies +### 1. Deploy to Cloudflare ```bash -pip install -r requirements.txt +cd worker +npm install +npx wrangler deploy ``` -### 2. Start the webhook receiver +### 2. Set the webhook secret ```bash -WEBHOOK_SECRET=your_secret python main.py webhook --port 8080 --event-profile notifications +npx wrangler secret put GITHUB_WEBHOOK_SECRET ``` -### 3. Set up Cloudflare Tunnel +### 3. Configure the GitHub webhook -```bash -cloudflared tunnel login -cloudflared tunnel create github-webhook-mcp -cp cloudflared/config.yml.example ~/.cloudflared/config.yml -# Edit config.yml with your tunnel ID and domain -cloudflared tunnel run -``` - -### 4. Configure the GitHub webhook - -- Payload URL: `https://webhook.yourdomain.com/webhook` +- Payload URL: `https://your-worker.workers.dev/webhooks/github` - Content type: `application/json` -- Secret: same value as `WEBHOOK_SECRET` -- Recommended event profile: - - Issues - - Issue comments - - Pull requests - - Pull request reviews - - Pull request review comments - - Check runs - - Workflow runs - - Discussions - - Discussion comments - -If your webhook is temporarily set to `Send me everything`, start the receiver with `--event-profile notifications` and it will ignore noisy events such as `workflow_job` or `check_suite`. +- Secret: same value as the Cloudflare secret +- Events: select the events you want to receive -### 5. Optional direct trigger mode +### 4. Optional: Channel notifications -Use the bundled Codex wrapper if you want the webhook to launch `codex exec` immediately. +The local MCP bridge supports Claude Code's `claude/channel` capability. When enabled, new webhook events are pushed into your session via SSE in real-time. ```bash -python main.py webhook \ - --port 8080 \ - --event-profile notifications \ - --trigger-command "python codex_reaction.py --workspace /path/to/workspace --output-dir /path/to/github-webhook-mcp/codex-runs" -``` - -If you want Codex to resume an existing app thread instead of writing markdown output: - -```text -python codex_reaction.py --workspace /path/to/workspace --resume-session -``` - -If you want webhook delivery to stay notification-only for a workspace, create a `.codex-webhook-notify-only` -file in that workspace. The bundled wrapper will skip direct Codex execution and leave the event pending. - -### 6. Optional: Run the receiver as a Windows service (NSSM) - -[NSSM (Non-Sucking Service Manager)](https://nssm.cc/) wraps any executable as a Windows service, so the webhook receiver starts automatically on boot without a console window. - -```powershell -# Install the service (opens a GUI) -nssm install github-webhook-mcp - -# Set the following in the GUI: -# Path: C:\Users\\AppData\Local\Programs\Python\Python312\python.exe -# Startup dir: C:\Users\\github-webhook-mcp -# Arguments: main.py webhook --port 8080 --event-profile notifications +claude --dangerously-load-development-channels server:github-webhook-mcp ``` -Or configure non-interactively from an **elevated** PowerShell: +## MCP Tools -```powershell -nssm install github-webhook-mcp "C:\Users\\AppData\Local\Programs\Python\Python312\python.exe" -nssm set github-webhook-mcp AppDirectory "C:\Users\\github-webhook-mcp" -nssm set github-webhook-mcp AppParameters "main.py webhook --port 8080 --event-profile notifications" -nssm set github-webhook-mcp AppEnvironmentExtra "WEBHOOK_SECRET=your_secret" -nssm start github-webhook-mcp -``` +| Tool | Description | +|------|-------------| +| `get_pending_status` | Lightweight snapshot of pending event counts by type | +| `list_pending_events` | Summaries of pending events (no full payloads) | +| `get_event` | Full payload for a single event by ID | +| `get_webhook_events` | Full payloads for all pending events | +| `mark_processed` | Mark an event as processed | -Common management commands: +## Monorepo Structure -```powershell -nssm status github-webhook-mcp # check status -nssm restart github-webhook-mcp # restart -nssm edit github-webhook-mcp # re-open the GUI -nssm remove github-webhook-mcp confirm # uninstall ``` - -> **Alternatives:** `sc create` with a `pywin32` service wrapper, or [WinSW](https://github.com/winsw/winsw) with an XML config, also work. NSSM requires no code changes. - -### 7. Optional channel push notifications - -The Node.js MCP server supports Claude Code's `claude/channel` capability (research preview, v2.1.80+). When enabled, new webhook events are pushed into your session automatically. - -```bash -claude --dangerously-load-development-channels server:github-webhook-mcp +worker/ — Cloudflare Worker + Durable Objects +local-mcp/ — Local stdio MCP bridge (TypeScript, dev) +mcp-server/ — .mcpb package for Claude Desktop +shared/ — Shared types and utilities ``` -Channel notifications are enabled by default. To disable, set `WEBHOOK_CHANNEL=0` in the MCP server environment. - -## Examples - -### Example 1: Check whether any GitHub notifications are pending - -**User prompt:** "Do I have any pending GitHub webhook notifications right now?" - -**Expected behavior:** - -- Calls `get_pending_status` -- Returns pending count, latest event time, and event types -- Uses that summary to decide whether more detail is needed - -### Example 2: Review the latest pending PR-related event - -**User prompt:** "Show me the latest pending pull request event and explain what changed." - -**Expected behavior:** - -- Calls `list_pending_events` to find the newest relevant event -- Calls `get_event` only for the selected event -- Summarizes the PR metadata and payload without dumping every event - -### Example 3: Mark an event as handled after triage - -**User prompt:** "I already handled event `EVENT_ID`. Mark it processed so it stops appearing." - -**Expected behavior:** - -- Calls `mark_processed` with the event ID -- Marks the event as processed in the local event store -- Confirms success and, if applicable, reports how many processed events were purged - ## Privacy Policy -This extension works with GitHub webhook event payloads that you choose to persist locally in `events.json`. -It may include issue titles, pull request metadata, discussion text, sender identities, and repository URLs inside that local event store. - -### Data Collection - -- Reads the local `events.json` file configured by the user -- Surfaces webhook metadata and payloads to the connected MCP client -- Can mark events as processed in the same local event store -- Does not send event contents to third-party services by itself beyond the webhook receiver and infrastructure you configure - -### Submission Note - -The extension-specific privacy policy is published at: - -- https://smgjp.com/privacy-policy-github-webhook-mcp/ +Events are stored in a Cloudflare Durable Object (edge storage). The local MCP bridge proxies tool calls to the Worker and does not store event data locally. -Keep the policy URL in `mcp-server/manifest.json` aligned with that public page before directory submission. +- Extension privacy policy: https://smgjp.com/privacy-policy-github-webhook-mcp/ ## Support - GitHub Issues: https://github.com/Liplus-Project/github-webhook-mcp/issues -- Requirements/specification: [docs/0-requirements.md](docs/0-requirements.md) -- Environment variable examples: [.env.example](.env.example) +- Requirements: [docs/0-requirements.md](docs/0-requirements.md) ## Related -- [docs/0-requirements.md](docs/0-requirements.md) — source-of-truth requirements and behavior contracts - [Liplus-Project/liplus-language](https://github.com/Liplus-Project/liplus-language) — Li+ language specification -- liplus-language #610 diff --git a/docs/0-requirements.md b/docs/0-requirements.md index e409d5e..013ff28 100644 --- a/docs/0-requirements.md +++ b/docs/0-requirements.md @@ -2,47 +2,48 @@ ## Purpose -GitHub Webhook イベントを受信・永続化し、MCP プロトコル経由で AI エージェントに提供する。 +GitHub Webhook イベントを Cloudflare Worker で受信・永続化し、MCP プロトコル経由で AI エージェントに提供する。 エージェントが GitHub の状態変化をリアルタイムに検知し、自律的に対応できるようにする。 ## Premise -- GitHub Webhook は外部から HTTPS POST で到達する(Cloudflare Tunnel 経由) -- AI エージェントは MCP stdio トランスポートで接続する -- 単一マシン上でローカル動作する(分散構成は対象外) -- イベント永続化は JSON ファイルベース(DB 不要) +- GitHub Webhook は Cloudflare Worker に直接到達する(ローカルサーバー不要) +- イベントは Durable Object 内の SQLite に永続化される +- AI エージェントは MCP stdio トランスポート(ローカルブリッジ)または Streamable HTTP(リモート)で接続する +- ローカルブリッジは Worker にツール呼び出しをプロキシし、SSE でリアルタイム通知を中継する ## Architecture ``` -GitHub ──POST──> Cloudflare Tunnel ──> FastAPI :8080 ──persist──> events.json - ^ - | - ┌─────────────────────────────┴─────────────────────────────┐ - | | - | trigger-events/.json - | ^ - | | - MCP Server (stdio) ──read/write direct trigger queue - Python or Node.js | - ^ | - | | - AI Agent (Codex / Claude) optional trigger command +GitHub ──POST──▶ Cloudflare Worker ──▶ WebhookStore DO (SQLite) + │ 署名検証 │ + │ UUID 付与 ├── SSE real-time stream + │ │ + ├── /mcp (Streamable HTTP) └── REST endpoints + │ WebhookMcpAgent DO /pending-status + │ └── tools → WebhookStore /pending-events + │ /webhook-events + ├── /events (SSE) /event + │ └── WebhookStore DO /mark-processed + │ + └── /webhooks/github (POST) + └── WebhookStore DO /ingest + + ┌─────────────────────────────┐ + │ Local MCP Bridge (.mcpb) │ + │ stdio ← Claude Desktop/CLI │ + │ → proxy tool calls to /mcp │ + │ → SSE listener → channel │ + └─────────────────────────────┘ ``` -システムは二つのコンポーネントで構成される: +システムは三つのコンポーネントで構成される: -1. **Webhook 受信サーバー(Python)** — `python main.py webhook` - - HTTP 受信、イベント永続化、optional な direct trigger queue を担当 - - trigger command が設定されている場合、保存済みイベントごとに直列実行される +1. **Cloudflare Worker** — webhook 受信、署名検証、WebhookStore DO へのルーティング +2. **WebhookStore Durable Object** — SQLite によるイベント永続化、REST/SSE エンドポイント +3. **WebhookMcpAgent Durable Object** — MCP Streamable HTTP サーバー、ツール定義 -2. **MCP ツールサーバー** — 二つの実装が存在する: - - **Python 実装:** `python main.py mcp` - - **Node.js 実装:** `mcp-server/` ディレクトリ(`npx github-webhook-mcp` または MCPB 経由) - -両 MCP 実装は同一の events.json を読み書きし、同一の5ツールを提供する。 -Python 実装は webhook 受信サーバーと同一エントリポイントで起動する。 -Node.js 実装は独立パッケージとして配布される(MCPB: Claude Desktop 向け、npx: Codex 向け)。 +ローカルブリッジ(mcp-server/)は Worker に対するプロキシであり、データを保持しない。 ## Functional Requirements @@ -50,47 +51,18 @@ Node.js 実装は独立パッケージとして配布される(MCPB: Claude De | ID | 要件 | |----|------| -| F1.1 | `POST /webhook` で GitHub Webhook ペイロードを受信する | +| F1.1 | `POST /webhooks/github` で GitHub Webhook ペイロードを受信する | | F1.2 | `X-Hub-Signature-256` ヘッダによる HMAC-SHA256 署名検証を行う | | F1.3 | 署名不一致時は HTTP 401 を返す | | F1.4 | シークレット未設定時は署名検証をスキップする | -| F1.5 | `GET /health` でヘルスチェックを提供する(`{"status": "ok"}`) | -### F2. イベントフィルタリング +### F2. イベント永続化 | ID | 要件 | |----|------| -| F2.1 | イベントプロファイルにより保存対象を制御する | -| F2.2 | `all` プロファイル: 全イベントを保存する | -| F2.3 | `notifications` プロファイル: GitHub Notifications 相当のイベントのみ保存する | -| F2.4 | フィルタ対象イベント種別と許可アクション: | - -**notifications プロファイル許可リスト:** - -| イベント種別 | 許可アクション | -|-------------|---------------| -| issues | assigned, closed, opened, reopened, unassigned | -| issue_comment | created | -| pull_request | assigned, closed, converted_to_draft, opened, ready_for_review, reopened, review_requested, review_request_removed, synchronize, unassigned | -| pull_request_review | dismissed, submitted | -| pull_request_review_comment | created | -| check_run | completed | -| workflow_run | completed | -| discussion | answered, closed, created, reopened | -| discussion_comment | created | - -### F3. イベント永続化 - -| ID | 要件 | -|----|------| -| F3.1 | イベントを UUID 付きで `events.json` に保存する | -| F3.2 | 各イベントは id, type, payload, received_at, processed フィールドを持つ | -| F3.3 | ファイルエンコーディングは UTF-8 で書き込む | -| F3.4 | レガシーエンコーディング(cp932, shift_jis, utf-8-sig)からの自動マイグレーションを行う | -| F3.5 | trigger 実行を行う場合、イベントに `trigger_status`, `trigger_error`, `last_triggered_at` を追記できる | -| F3.6 | trigger 実行用に `trigger-events/.json` を保存できる | -| F3.7 | `mark_processed` 実行時に、保持期間(`PURGE_AFTER_DAYS`、デフォルト 1 日)を超えた処理済みイベントを自動パージする | -| F3.8 | `PURGE_AFTER_DAYS=0` で即時パージ、負値でパージ無効化 | +| F2.1 | イベントを UUID 付きで WebhookStore DO の SQLite に保存する | +| F2.2 | 各イベントは id, type, payload, received_at, processed フィールドを持つ | +| F2.3 | trigger_status, last_triggered_at フィールドを保持する(将来の trigger 機能用) | **イベント構造:** @@ -99,25 +71,24 @@ Node.js 実装は独立パッケージとして配布される(MCPB: Claude De "id": "uuid", "type": "issues", "payload": {}, - "received_at": "ISO8601+timezone", + "received_at": "ISO8601", "processed": false, - "trigger_status": "succeeded|failed|skipped|null", - "trigger_error": "", - "last_triggered_at": "ISO8601+timezone|null" + "trigger_status": null, + "last_triggered_at": null } ``` -### F4. MCP ツール +### F3. MCP ツール -Python 実装と Node.js 実装の両方が、以下の同一ツールセットを提供する。 +WebhookMcpAgent DO が以下のツールセットを提供する。ローカルブリッジはこれをプロキシする。 | ID | ツール名 | 引数 | 戻り値 | 要件 | |----|---------|------|--------|------| -| F4.1 | `get_pending_status` | なし | pending_count, latest_received_at, types | 未処理イベントの軽量スナップショットを返す | -| F4.2 | `list_pending_events` | limit (1-100, default 20) | サマリー配列 | 未処理イベントのメタデータ一覧を返す(ペイロード含まず) | -| F4.3 | `get_event` | event_id | 完全イベント or error | UUID 指定で完全なペイロードを返す | -| F4.4 | `get_webhook_events` | なし | 全未処理イベント | 全未処理イベントをフルペイロード付きで返す | -| F4.5 | `mark_processed` | event_id | success, event_id, purged | イベントを処理済みにマークし、保持期間を超えた処理済みイベントを自動パージする | +| F3.1 | `get_pending_status` | なし | pending_count, latest_received_at, types | 未処理イベントの軽量スナップショットを返す | +| F3.2 | `list_pending_events` | limit (1-100, default 20) | サマリー配列 | 未処理イベントのメタデータ一覧を返す(ペイロード含まず) | +| F3.3 | `get_event` | event_id | 完全イベント or error | UUID 指定で完全なペイロードを返す | +| F3.4 | `get_webhook_events` | limit (1-100, default 20) | 未処理イベント配列 | 未処理イベントをフルペイロード付きで返す | +| F3.5 | `mark_processed` | event_id | success, event_id | イベントを処理済みにマークする | **イベントサマリー構造:** @@ -127,8 +98,8 @@ Python 実装と Node.js 実装の両方が、以下の同一ツールセット "type": "issues", "received_at": "ISO8601", "processed": false, - "trigger_status": "succeeded|failed|skipped|null", - "last_triggered_at": "ISO8601|null", + "trigger_status": null, + "last_triggered_at": null, "action": "opened", "repo": "owner/repo", "sender": "username", @@ -138,62 +109,27 @@ Python 実装と Node.js 実装の両方が、以下の同一ツールセット } ``` -### F5. Direct Trigger Execution +### F4. SSE リアルタイムイベント配信 | ID | 要件 | |----|------| -| F5.1 | webhook モードは optional な trigger command を受け付ける | -| F5.2 | trigger command は保存対象イベントごとに 1 件ずつ直列実行する | -| F5.3 | trigger command にはイベント JSON 全体を stdin で渡す | -| F5.4 | trigger command には `GITHUB_WEBHOOK_*` 環境変数でイベント要約を渡す | -| F5.5 | trigger command 成功時はデフォルトでイベントを `processed=true` にする | -| F5.6 | `keep_pending_on_trigger_success` 指定時は成功しても pending のまま残す | -| F5.7 | trigger command 失敗時はイベントを pending のまま残し、`trigger_status=failed` を記録する | -| F5.8 | trigger command が notify-only fallback を選んだ場合は `trigger_status=skipped` を記録し、pending のまま残す | - -**trigger command へ渡す環境変数:** - -- `GITHUB_WEBHOOK_EVENT_ID` -- `GITHUB_WEBHOOK_EVENT_TYPE` -- `GITHUB_WEBHOOK_EVENT_ACTION` -- `GITHUB_WEBHOOK_EVENT_REPO` -- `GITHUB_WEBHOOK_EVENT_SENDER` -- `GITHUB_WEBHOOK_EVENT_NUMBER` -- `GITHUB_WEBHOOK_EVENT_TITLE` -- `GITHUB_WEBHOOK_EVENT_URL` -- `GITHUB_WEBHOOK_EVENT_PATH` -- `GITHUB_WEBHOOK_RECEIVED_AT` - -### F6. Bundled Codex Wrapper +| F4.1 | `GET /events` で SSE ストリームを提供する | +| F4.2 | webhook ingest 時に接続中の全 SSE クライアントにイベントサマリーをブロードキャストする | +| F4.3 | 30 秒間隔でハートビートを送信する | +| F4.4 | クライアント切断時にクリーンアップする | -| ID | 要件 | -|----|------| -| F6.1 | `codex_reaction.py` を bundled helper として提供する | -| F6.2 | helper は `codex exec -C ` による direct execution を行える | -| F6.3 | helper は `codex exec resume ` による resume mode を行える | -| F6.4 | helper は event JSON path を prompt に含め、workspace の `AGENTS.md` を読む前提で起動する | -| F6.5 | workspace に `.codex-webhook-notify-only` がある場合、helper は direct execution を行わず notify-only fallback を返す | - -### F7. チャンネル通知(プッシュ配信) +### F5. チャンネル通知(ローカルブリッジ) | ID | 要件 | |----|------| -| F7.1 | Node.js MCP サーバーが Claude Code の `claude/channel` experimental capability を宣言する | -| F7.2 | `events.json` の変更を `fs.watch` で監視し、新規 pending イベント検出時に `notifications/claude/channel` を送信する | -| F7.3 | 通知内容はイベントサマリー(type, repo, action, title, sender, url)を改行区切りで送信する | -| F7.4 | `meta` フィールドに `chat_id`, `message_id`, `user`, `ts` を付与する | -| F7.5 | 送信済みイベント ID を追跡し、重複通知を防止する | -| F7.6 | `events.json` が未作成の場合は 5 秒間隔でポーリングし、ファイル出現後に `fs.watch` に切り替える | -| F7.7 | `WEBHOOK_CHANNEL=0` 環境変数でチャンネル通知を無効化できる(デフォルト: 有効) | -| F7.8 | チャンネル通知は one-way(読み取り専用)で、reply tool は提供しない | - -**制約:** - -- `Server` クラスを直接使用すること(`McpServer` ラッパーでは capability が伝わらない) -- capability はコンストラクタオプションで宣言する(`registerCapabilities()` 後付けは不可) -- Claude Code 側で `--channels` または `--dangerously-load-development-channels` フラグが必要(research preview) +| F5.1 | ローカルブリッジが Claude Code の `claude/channel` experimental capability を宣言する | +| F5.2 | Worker の SSE エンドポイントに接続し、新規イベント検出時に `notifications/claude/channel` を送信する | +| F5.3 | 通知内容はイベントサマリー(type, repo, action, title, sender)を含む | +| F5.4 | `meta` フィールドに `chat_id`, `message_id`, `user`, `ts` を付与する | +| F5.5 | `WEBHOOK_CHANNEL=0` 環境変数でチャンネル通知を無効化できる(デフォルト: 有効) | +| F5.6 | チャンネル通知は one-way(読み取り専用)で、reply tool は提供しない | -### F8. 推奨ポーリングフロー +### F6. 推奨ポーリングフロー | ステップ | 操作 | |---------|------| @@ -208,54 +144,42 @@ Python 実装と Node.js 実装の両方が、以下の同一ツールセット | ID | 要件 | |----|------| -| N1.1 | Webhook シークレットは環境変数 `WEBHOOK_SECRET` で管理する | +| N1.1 | Webhook シークレットは Cloudflare Worker の Secret(`GITHUB_WEBHOOK_SECRET`)で管理する | | N1.2 | HMAC-SHA256 による署名検証でスプーフィングを防止する | -| N1.3 | MCP は stdio トランスポートを使用し、ネットワーク露出しない | +| N1.3 | ローカルブリッジは stdio トランスポートを使用し、ネットワーク露出しない | ### N2. 構成 | ID | 項目 | ソース | デフォルト | |----|------|--------|-----------| -| N2.1 | ポート | `--port` / 環境変数 | 8080 | -| N2.2 | シークレット | `--secret` / `WEBHOOK_SECRET` | なし(検証スキップ) | -| N2.3 | イベントプロファイル | `--event-profile` / `WEBHOOK_EVENT_PROFILE` | all | -| N2.4 | trigger command | `--trigger-command` / `WEBHOOK_TRIGGER_COMMAND` | なし | -| N2.5 | trigger working directory | `--trigger-cwd` / `WEBHOOK_TRIGGER_CWD` | なし | -| N2.6 | success 時に pending を維持するか | `--keep-pending-on-trigger-success` | false | -| N2.7 | events.json パス(Node.js MCP) | `EVENTS_JSON_PATH` | `mcp-server/../events.json` | -| N2.8 | 処理済みイベント保持日数 | `PURGE_AFTER_DAYS` | 1 | -| N2.9 | チャンネル通知の有効/無効 | `WEBHOOK_CHANNEL` | 有効(`0` で無効) | - -優先順位: CLI 引数 > 環境変数 > デフォルト -Node.js MCP サーバーは環境変数のみで構成する(CLI 引数なし)。 +| N2.1 | Worker URL | `WEBHOOK_WORKER_URL` 環境変数 | なし(必須) | +| N2.2 | シークレット | Cloudflare Secret `GITHUB_WEBHOOK_SECRET` | なし(検証スキップ) | +| N2.3 | チャンネル通知の有効/無効 | `WEBHOOK_CHANNEL` | 有効(`0` で無効) | ### N3. 制約 | ID | 制約 | |----|------| -| N3.1 | イベントはファイル全体をメモリにロードする(大量イベントには不向き) | -| N3.2 | イベント検索は線形探索(インデックスなし) | -| N3.3 | 単一プロセス想定(webhook と mcp は別プロセスで起動) | -| N3.4 | direct trigger は単一 worker による直列実行であり、同時実行はしない | +| N3.1 | Durable Object は単一インスタンス(`idFromName("singleton")`)で動作する | +| N3.2 | SSE 接続は DO のメモリ内で管理される(DO eviction 時に切断) | +| N3.3 | ローカルブリッジはツール呼び出しごとに Worker セッションを再利用する(セッション失効時は自動リトライ) | ## Dependencies -### Python(webhook 受信 + MCP サーバー) +### Cloudflare Worker -| パッケージ | バージョン | 用途 | -|-----------|-----------|------| -| fastapi | >=0.110.0 | HTTP サーバーフレームワーク | -| uvicorn | >=0.29.0 | ASGI アプリケーションサーバー | -| mcp | >=1.0.0 | Model Context Protocol SDK | -| python-dotenv | >=1.0.0 | 環境変数ロード | +| パッケージ | 用途 | +|-----------|------| +| agents | Cloudflare Agents SDK (McpAgent) | +| @modelcontextprotocol/sdk | MCP SDK | +| zod | スキーマバリデーション | -### Node.js(MCP サーバー) +### ローカルブリッジ (mcp-server/) -| パッケージ | バージョン | 用途 | -|-----------|-----------|------| -| @modelcontextprotocol/sdk | ^1.0.0 | MCP SDK(`Server` クラス直接使用) | -| iconv-lite | ^0.6.3 | レガシーエンコーディング対応 | -| zod | ^3.22.0 | スキーマバリデーション(Python 実装互換用、将来削除候補) | +| パッケージ | 用途 | +|-----------|------| +| @modelcontextprotocol/sdk | MCP SDK(`Server` クラス直接使用) | +| eventsource | SSE クライアント | Node.js >= 18.0.0 が必要。 @@ -265,7 +189,7 @@ Node.js >= 18.0.0 が必要。 | トリガー | ジョブ | 内容 | |---------|--------|------| -| PR to main / push to main | test | Python unittest | +| PR to main / push to main | test | Node.js syntax check | ### リリース(CD) @@ -285,21 +209,23 @@ Node.js >= 18.0.0 が必要。 | コンポーネント | 用途 | |---------------|------| -| Cloudflare Tunnel | GitHub からのインバウンド HTTPS を localhost に転送 | +| Cloudflare Worker | webhook 受信 + MCP サーバー | +| Cloudflare Durable Objects | イベント永続化 (SQLite) + SSE | | GitHub Webhook | イベント送信元 | -| optional trigger command | 保存済みイベントごとの direct reaction | -| MCPB | Claude Desktop 向け Node.js MCP サーバー配布 | -| npx | Codex 向け Node.js MCP サーバー配布 | +| MCPB | Claude Desktop 向けローカルブリッジ配布 | +| npx | CLI/Codex 向けローカルブリッジ配布 | ## Files | パス | 用途 | |-----|------| -| `events.json` | 永続化された webhook イベント本体 | -| `trigger-events/.json` | trigger command に渡す保存済みイベント JSON | -| `main.py` | webhook receiver / Python MCP server / direct trigger queue | -| `codex_reaction.py` | Codex direct execution / resume / notify-only fallback helper | -| `mcp-server/server/index.js` | Node.js MCP サーバー エントリポイント | -| `mcp-server/server/event-store.js` | Node.js イベントストア(events.json 読み書き) | -| `mcp-server/package.json` | Node.js パッケージ定義(npx 配布用) | -| `mcp-server/manifest.json` | MCPB マニフェスト(Claude Desktop 配布用) | +| `worker/src/index.ts` | Cloudflare Worker エントリポイント | +| `worker/src/agent.ts` | WebhookMcpAgent DO(MCP ツール定義) | +| `worker/src/store.ts` | WebhookStore DO(SQLite + SSE) | +| `worker/wrangler.toml` | Worker デプロイ設定 | +| `shared/src/types.ts` | 共有型定義 | +| `shared/src/summarize.ts` | イベントサマリー生成 | +| `local-mcp/src/index.ts` | ローカルブリッジ(TypeScript、開発用) | +| `mcp-server/server/index.js` | ローカルブリッジ(JS、.mcpb 配布用) | +| `mcp-server/manifest.json` | MCPB マニフェスト | +| `mcp-server/package.json` | npm パッケージ定義 |