Skip to content

Explore unified architecture: Cloudflare Workers + Durable Objects #48

@liplus-lin-lay

Description

@liplus-lin-lay

目的

webhook 受信サーバーと MCP サーバーを Cloudflare Workers + Durable Objects で統合し、単一のクラウドサービスとして動作させる。

前提

  • 現在の構成: Windows サービスで webhook 受信サーバー(FastAPI/HTTP)+ ローカル MCP サーバー(stdio)、events.json で間接連携
  • 利用環境が多様: Claude Desktop / Claude Code CLI / Codex
  • 個人利用に限定しない設計思想(公開利用も視野)

DO を選ぶ核心的理由

SSE によるリアルタイムプッシュ。

  • GitHub webhook は片方向(POST が飛んでくるだけ)→ 受け口が必要
  • Discord 等は自前サーバーにデータが蓄積されるため API で読むだけで済むが、webhook にはそれがない
  • DO はコネクションを保持しつつ Hibernation でアイドル時は課金ゼロ
  • webhook 到着時に起きて SSE で接続中クライアントに即プッシュできるのは DO だけ(KV/R2 では不可)

アーキテクチャ

GitHub ──POST──▶ Cloudflare Worker fetch()
                    │
                    ├── /webhooks/github → 署名検証 → RPC → McpAgent DO → SQLite 保存
                    ├── /events          → SSE ストリーム(Worker レベルで配信)
                    └── /mcp             → McpAgent DO(リモート MCP、Streamable HTTP)

ローカル stdio MCP(薄いブリッジ、Discord MCP と同パターン)
  ├── DO の /events(SSE)を listen
  ├── 新着イベント → notifications/claude/channel でプッシュ
  └── MCP ツール → DO の /mcp に委譲(データを持たない)

Worker ルーティングの理由

McpAgent DO は内部的に WebSocket を使用し、全リクエストに WebSocket アップグレードを強制する(cloudflare/agents#172)。カスタム SSE エンドポイントを DO 内部に置けないため、SSE は Worker の fetch() レベルでルーティングする。

二層構成の理由

Claude Code チャンネル通知は ローカル stdio MCP 限定(リモート MCP からは送れない)。Discord MCP と同じパターン:

Discord MCP github-webhook-mcp(新)
データソース Discord Gateway(WebSocket) Cloudflare DO(SSE)
ローカル MCP discord.js で受信 → stdio で通知 EventSource で受信 → stdio で通知
データ保持 Discord サーバー DO + SQLite
ツール reply, fetch_messages 等 get_pending_status, list_pending_events 等

クライアント別経路

クライアント 接続先 チャンネル通知 ポーリング リアルタイム性
Claude Code CLI ローカル stdio MCP ○(SSE → チャンネル) 即時
Claude Desktop リモート MCP(DO 直接) ×(未対応) ツール呼び出し間隔次第
Codex リモート MCP(DO 直接) ×(未対応) ツール呼び出し間隔次第

チャンネル通知の実装

// ローカルブリッジの核心部分
const mcp = new Server(
  { name: 'github-webhook', version: '1.0.0' },
  {
    capabilities: {
      experimental: { 'claude/channel': {} },
      tools: {},
    },
  },
)
await mcp.connect(new StdioServerTransport())

// DO の SSE を listen → チャンネル通知に変換
const es = new EventSource('https://your-worker.workers.dev/events')
es.onmessage = (e) => {
  const event = JSON.parse(e.data)
  mcp.notification({
    method: 'notifications/claude/channel',
    params: {
      content: event.summary,
      meta: { repo: event.repo, type: event.type },
    },
  })
}
  • @modelcontextprotocol/sdk のみ依存
  • capabilities.experimental['claude/channel'] を宣言するだけで登録される
  • research preview 中は --dangerously-load-development-channels フラグが必要

リポジトリ構成

モノレポ方式。既存の github-webhook-mcp リポジトリ内にサブディレクトリを設ける。

github-webhook-mcp/
├── worker/          # Cloudflare Worker + DO(TypeScript)
│   ├── src/
│   └── wrangler.toml
├── local-mcp/       # ローカル stdio ブリッジ MCP(TypeScript)
│   └── src/
├── shared/          # 共有型定義(イベント型、ツール定義)
└── (既存コード)     # 現行版(v0.x 系、移行完了まで維持)
  • Worker とローカル MCP が同じイベント型・ツール定義を共有
  • Cloudflare GitHub 連携は worker/ サブディレクトリを指定してデプロイ
  • issue 管理が 1 リポジトリで完結

制約

チャンネル機能(research preview)

  • 参考: https://code.claude.com/docs/en/channels-reference
  • claude/channel capability を持つローカル MCP サーバーのみ対応
  • リモート MCP → チャンネル通知は仕様上不可
  • Claude Code CLI v2.1.80+ が必要、Claude Desktop / Codex は未対応
  • research preview 中は --dangerously-load-development-channels フラグが必要

Cloudflare プラットフォーム

項目 Free Paid ($5/月)
リクエスト数 10万/日 無制限
CPU 時間/リクエスト 10ms 30秒
同時外部接続 6 6
DO storage 5GB 50GB
DO wall time 無制限 無制限
  • CPU 10ms(Free): webhook 受信 → DO 書き込み程度なら十分
  • DO wall time 無制限: SSE 長時間接続の維持に好都合

DDoS 対策(#36 包含)

  • GitHub webhook 署名検証を Worker 入口で実施 → 不正リクエストは DO に到達しない
  • Workers 無料枠の使用量アラート設定
  • 公開利用時: Rate Limiting + マルチテナント時のユーザーごとレート制限

技術選定

  • agents SDK v0.8.0McpAgent クラスで DO 内に MCP サーバー構築
  • Streamable HTTP がデフォルトトランスポート(MCP spec 2025-03-26 確定)
  • RPC バインディング(v0.6.0+)で webhook Worker → DO を HTTP なしで直接呼べる
  • OAuth 2.1 は opt-in — 個人利用なら不要
  • event-profile フィルタリングを Worker 入口で適用し不要イベントを DO に流さない
  • DO 内部 WebSocket は Hibernation 対応 — アイドル時にメモリ退避、wake 時に reinitializeServer() で MCP 状態を復元

検討事項

  • SSE 切断時のリコネクト戦略(ローカルブリッジ側の EventSource reconnect)
  • Worker レベル SSE で DO の新着イベントをどう検知するか(DO → Worker への通知経路)
  • permission relay(claude/channel/permission)の活用可能性

参考

Metadata

Metadata

Labels

enhancementNew feature or requestin-progress着手中、実装または検証が進行中speclanguage or system specification

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions