メインコンテンツまでスキップ

Discord メッセージ検索 MCP サーバー 要件定義書

0. ドキュメント情報

項目内容
プロジェクト名Discord メッセージ検索 MCP サーバー
バージョンv0.7.0
作成日2026-07-08
最終更新日2026-07-27
作成者@yharuto0917 @shiyow5
承認者
ステータス検討中

更新履歴

バージョン日付変更内容担当者
v0.7.02026-07-27第8次レビュー反映。Gateway Bot を PM2 fork mode + instances: 1 の単一プロセス運用へ固定し、その制約を R-15 として記録。half-open probe の期限付き lease、Durable Object 到達不可時の縮退(R-16)、Workers 側 CIRCUIT_OPEN と VPS 側 GATEWAY_UNAVAILABLE の分離を追加。平文 HTTP は redirect 時点で認証情報が平文送信済みである事実を R-14 として明記し、AI Agent Host の https:// 実物検証と漏洩時ローテーションをリリース条件化。max_restarts のリセット仕様を補う永続 restart budget(24 時間 / 10 回)、認証前段の keyId 単位レート制限、Workers 側の署名前 body サイズ検査、受信制御の応答形式を追加。リスク表の ID 順を整理@yharuto0917
v0.6.02026-07-27第7次レビュー反映。平文 HTTP の gateway 転送前拒否、内部 API の 65,536 bytes 上限と 413 PAYLOAD_TOO_LARGE、Rate Limiting binding と guild 単位 Durable Object による受信制御、PM2 min_uptime を含む連続異常終了の保証範囲を確定。B-03 を本番リリース Blocker に統一し、認証済み heartbeat 欠損を恒久停止の一次検知、Workers のリクエスト起点アラートを補助検知に変更。v1 と Phase 2 のリプレイ受入条件を分離@yharuto0917
v0.5.02026-07-27第5次レビュー反映。復帰不能な shardDisconnect の復旧方針を close code で分岐させ(discord.js の private 定数と同一の 6 コードを自前 allowlist として保持し、一致時は自動再起動を抑止して手動是正、不一致時はバージョンアップ互換の防御的分岐として上限付き・バックオフ付き再起動)、無条件再起動による無限クラッシュループと Discord 側レート制限のリスクを排除(§8 可用性 / R-02 / §13)。アラート経路(log-throw-man → Discord チャンネル)からの秘匿情報流出を R-12 として追加し、フィールド allowlist・設定済み秘密値の完全一致マスク・秘密パターンのマスク・サイズ上限・1 invocation 内の request-local 重複抑止を受け入れ条件に追加。第6次レビュー反映として、exit 78 の自動再起動抑止と B-03 未確定の組み合わせで恒久停止が無検知になる問題を R-13 として追加し、Workers 側アラートによる検知を稼働前提条件化。プロセスマネージャを PM2 5.1+ に固定して systemd 分岐を削除。requestPath / workerName の値限定とマスクを R-12・§13 に追加@yharuto0917
v0.4.02026-07-26権限変更がキャッシュへ反映されるまでの時間(旧権限情報が参照される時間を許容・TTL 既定 5 分/最大 15 分・shardReconnecting で検知した Gateway 切断中は fail-closed、R-02)、監査ログ保存先(既存資源で二系統)、AI Agent Host 接続契約 I/F(汎用 MCP クライアント前提)、スタッフトークン誤設定への MCP 側診断(staff_token_required・R-08)、監査ログ保持期間 30 日(Workers 側の保持期間が短いことを受容・R-09)を確定。第2次レビュー反映: アクセスキーと guild の関連付け、Gateway 切断中の fail-closed、/api/token 実挙動に沿った JWT 取得手順、VPS→logger の認証済み経路・アダプタ契約。第3次レビュー反映。guild 横断防止の主要な対策を「1 セッション = 1 guild」(MUST)へ訂正し、キーと guild の関連付けは資格情報流用の防止に位置づけ直して残存リスクを R-10 に記録。Gateway 切断検知を discord.js v14 の実イベント(shardReconnecting 等)に修正、キャッシュ TTL 上限を数値化、log-throw-manworkers_dev: false を連携前提として明記、Discord 通知を監査ログ保持の補完手段から除外、staff_token_required の受け入れ条件を追加。第4次レビュー反映として、発火しない invalidated への依存を除去し、shardError / shardDisconnect の復旧方針と再開可能な Invalid Session の残存リスク(R-11)を明文化。health/ready を guild 部分障害時の degraded 応答へ変更し、logger 送信データの相関情報欠落を修正。VPS の外部監視サービスは B-03 の未確定事項に戻した@yharuto0917
v0.3.02026-07-18Blocker B-01(Workers ↔ VPS 接続・認証)と B-02(aud / guildId 伝搬)の方式を確定。B-01 は Cloudflare Tunnel + Cloudflare Access + アプリ層 HMAC 署名の3層。B-02 は unified-auth-worker 不変の制約下で、MCP Worker の userType: 'staff' 強制+サーバー側 guild バインド(マルチギルド routing)+毎回検証を補完的な対策として S-003 に定義(Issue #371)。レビュー反映: MCP 専用アクセスキーによる宛先スコープの強制、global_fetch_strictly_public 必須化、代替構成での Access JWT 検証モードを追加。実装コストと残存リスクの比較から、許容期間内でのリプレイ攻撃防止(nonce 照合)を Phase 2 へ移し、S-006・§13 ではリプレイ可能な時間を期限で制限する方針へ再定義し、受容を R-07 に記録@yharuto0917
v0.2.02026-07-16MCP 本体(Cloudflare Workers)と Discord Gateway Bot(VPS)のアーキテクチャ分離に伴い、Gateway Pattern、versioned 内部 API、セキュリティ Blocker、互換性・監査要件を更新(Issue #371)@yharuto0917
v0.1.02026-07-08初版作成。details.md を基に要件を整理@yharuto0917 @shiyow5

1. 概要

1.1 背景・目的

蒼翔祭の運営では、連絡・決定事項・議論の経緯が Discord 上に蓄積される。一方で、メッセージは時系列に流れて埋もれやすく、後から必要な情報を探すコストが高い。

本要件定義書の目的は、AI Agent から利用できる MCP サーバーを通じて、Discord メッセージを安全に検索・参照できるようにすることにある。特に、利用者が本来閲覧できるチャンネルの範囲だけを検索対象に限定し、prompt injection や権限逸脱による情報漏洩を防ぐ。

1.2 ゴール

  • Discord のメッセージ検索を AI Agent 経由で実行できること
  • 検索対象を利用者の可視チャンネルに限定し、権限外情報を返さないこと
  • 認証・権限判定・監査をサーバー側で完結させること
  • search_messageslist_accessible_channelsget_message_context を仕様どおり提供すること
  • 主要な権限分離、認証否定系、キャッシュ無効化のテストが通ること

Definition of Done は以下とする。

  • 権限外チャンネルの検索結果混入が発生しない
  • 認証失敗や権限解決失敗時に fail-closed で拒否する
  • Workers → VPS 内部 API が認証済み MCP Worker 以外を拒否し、userId / guildId を MCP Worker が生成した認証済み request context として安全に確定する
  • 監査ログに必要情報のみを残し、秘密情報を残さない
  • 受け入れ条件と回帰テストが green である

1.3 スコープ

対象範囲(Scope In)

  • MCP サーバー本体
  • Discord メッセージ検索ツール群
  • 利用者の可視チャンネルに対するサーバー側アクセス制御
  • 既存認証基盤との連携
  • 専用 Discord Bot による権限解決と検索アクセス
  • 権限キャッシュとイベント無効化
  • マルチギルド対応

対象外(Scope Out / Out of Scope)

  • AI Agent 本体の会話設計やプロンプト設計
  • Discord メッセージの自前インデックス構築
  • メッセージ送信・編集・削除などの書き込み操作
  • DM 検索
  • 人が直接操作する Web UI の提供

1.4 用語定義

用語説明
MCPModel Context Protocol。LLM に外部ツールを提供するプロトコル
AI Agent HostMCP ツールを呼び出す側の LLM 実行環境
ギルドDiscord のサーバー単位
可視チャンネル利用者が VIEW_CHANNELREAD_MESSAGE_HISTORY を持ち、閲覧可能なチャンネル
enforcementサーバー側で検索範囲を強制的に絞り込む処理
fail-closed情報不足や権限不明時に、安全のため処理を拒否する挙動
privileged intentDiscord の特権 intent。利用には開発者ポータルでの有効化が必要
Permission Cache可視チャンネル集合を保持する権限キャッシュ

2. ステークホルダー

役割氏名関わり方
プロダクトオーナー学園祭実行委員会要件の優先順位付け、運用可否の判断
開発担当Haruto Yamazaki / Sho Sato実装、検証、ドキュメント整備
Discord JS 実装担当@shiyow5Discord.js 側の Gateway、Bot、権限制御の実装
MCP Server 実装担当@yharuto0917MCP、HTTP、認証、受信制御の実装
利用者・対象ユーザー学祭実行委員Discord 上の情報検索を行う

3. 利用者・ユースケース

3.1 想定利用者(ペルソナ)

  • 学祭実行委員: 自分の担当企画に関する連絡を確認したい
  • 部門長: 部門内の決定経緯や過去の議論を追いたい
  • 幹部・鯖缶: 複数ギルドにまたがる運用状況を把握したい
  • AI Agent Host: 利用者の問いに応じて MCP ツールを呼び出す

3.2 ユースケース一覧

IDユースケース名概要優先度
UC-01過去の決定経緯の検索部門長が予算や方針の決定経緯を探す
UC-02担当企画の連絡確認学祭実行委員が自分の担当に関する最新連絡を探す
UC-03特定メンバーの発言確認幹部・鯖缶が特定メンバーの直近発言を追う
UC-04可視チャンネルの把握AI Agent が検索前に閲覧可能なチャンネルを確認する

4. 機能要件

優先度は Must / Should / Could / Won't で管理する。

ID機能名説明優先度備考
F-001メッセージ検索Discord メッセージを検索し、可視チャンネルに限定して返すMustsearch_messages
F-002可視チャンネル一覧利用者が閲覧可能なチャンネルのみを返すMustlist_accessible_channels
F-003メッセージ文脈取得ヒットしたメッセージの前後文脈を返すShouldget_message_context
F-004認証・権限境界userId を検証済み claim(sub)から、guildId をサーバー側 route/config バインドから受け取り、非スタッフ種別・認証失敗時は拒否するMustfail-closed
F-005監査・キャッシュ制御権限キャッシュを保持し、イベントに応じて無効化するMust漏洩防止の基盤

4.1 セキュリティ MUST

ID要件優先度備考
S-001/mcp は HTTPS のみで公開する。zone の Always Use HTTPS を有効化し、gateway-worker は元リクエストが平文 HTTP の場合、認証 header・body の読取前および MCP_WORKER Service Binding 転送前に redirect または 4xx で終端する。あわせて、AI Agent Host 側が送信前に http:// URL を拒否することをリリース条件とし、平文 HTTP 到達を検知した場合は当該アクセスキーとスタッフ JWT を漏洩扱いとしてローテートするMustCloudflare edge / Worker への平文 HTTP 到達は起こりうるため、TLS 終端だけを拒否根拠にしない。redirect が返る時点で認証情報は平文送信済みであり、サーバー側終端は credential 保護にならない(R-14)
S-002Discord への連携には Bot token のみを用い、受信した Bearer token やユーザートークンを転送しないMusttoken passthrough 禁止
S-003宛先外トークン・宛先外スコープ・宛先外 guild で動作させない。/mcp は MCP 専用アクセスキー(X-MCP-Access-KeykeyId と guildId を関連付け。キーの流用を防ぐ)を必須とし staff JWT 単体では呼び出せず、キーの guild と route の guild が一致しなければ拒否する。prompt injection による guild 横断は「1 セッション = 1 guild」(運用強制・R-10)を主要な対策とする。MCP Worker は /token/verify 検証後に userType === 'staff' を強制し(ゲスト JWT を拒否)、対象 guildId をクライアント入力に依存せずサーバー側で決定する。VPS は guild membership / 可視チャンネルを強制するMustトークン層の aud 検証は issuer 側対応を要する将来拡張(Out of Scope: unified-auth-worker)。それまでは左記を補完的な対策として維持(B-02 で確定)
S-004認証失敗、BAN、権限不明時は fail-closed で拒否するMust
S-005権限外の channel_id / 本文 / 監査対象情報を保存しないMustallowlist 再フィルタを前提とする
S-006Workers → VPS 内部 API は VPS origin への公開 Internet からの直接到達を禁止して通信経路を暗号化する。VPS は MCP Worker の service identity を検証し、method・canonical path・version・requestId・nonce・issuedAt / deadlineAt・userId / guildId・input を同一の認証済み request として完全性保護する。認証・コンテキストの整合性検証に失敗した場合や期限切れの場合は拒否し、認証情報を rotation・失効できること。リプレイ可能な最長時間は deadline と clock skew で制限することMustTunnel + Access + HMAC の3層で確定(B-01)。HMAC を完全性・送信元の最終的な検証の根拠とする。許容期間内でのリプレイ攻撃防止(nonce 照合)は Phase 2 とし、残存リスクを R-07 に記録

4.2 機能詳細(必要に応じて個別に展開)

F-001: メッセージ検索

  • 概要:利用者の可視チャンネルに限定して Discord メッセージを検索する
  • 入力
    • 検索文字列
    • 投稿者、メンション、添付有無、並び順、件数上限
    • 時間範囲
    • 任意の channel_id
  • 処理
    • 認証済み userId / guildId を取得する
    • 可視チャンネル集合を算出する
    • LLM 指定の channel_id と可視集合を交差する
    • Discord Search API を呼び出す
    • 検索結果を可視集合で再度フィルタする
    • 検索結果をサニタイズして返す
  • 出力
    • チャンネル名
    • 投稿者
    • 本文の抜粋
    • タイムスタンプ
    • メッセージリンク
  • 異常系・エラー処理
    • 認証失敗は 401 相当で拒否
    • 権限解決失敗は fail-closed
    • インデックス未準備はリトライ後にエラー通知
    • レート制限はバックオフ後に再試行
  • 関連する画面/コマンド
    • MCP ツール search_messages

F-002: 可視チャンネル一覧

  • 概要:利用者が閲覧可能なチャンネルだけを返す
  • 入力
    • 認証済み userId / guildId
  • 処理
    • guild 参加有無を確認する
    • 権限判定を行い、可視チャンネルのみを抽出する
    • 権限解決に失敗した場合は空またはエラーを返す
  • 出力
    • channel_id
    • channel 名
    • 必要に応じて category / 種別
  • 異常系・エラー処理
    • 非メンバーは拒否
    • 権限未確定時は全チャンネルを列挙しない
  • 関連する画面/コマンド
    • MCP ツール list_accessible_channels

F-003: メッセージ文脈取得

  • 概要:検索ヒットの前後文脈を返す
  • 入力
    • channel_id
    • message_id
  • 処理
    • channel_id が可視集合に含まれるか再検証する
    • 可視でない channel_id は拒否する
    • 該当メッセージ周辺のみを取得する
  • 出力
    • 前後のメッセージ
    • 対象メッセージの位置情報
  • 異常系・エラー処理
    • 非可視 channel_id は fail-closed
    • message_id が不正な場合はエラーを返す
  • 関連する画面/コマンド
    • MCP ツール get_message_context

F-004: 認証・権限境界

  • 概要:利用者 ID を検証済み claim から、ギルド ID をサーバー側バインドから受け取り、ツール引数に依存しない
  • 入力
    • Authorization Bearer トークン(既存スタッフ JWT)
    • MCP 専用アクセスキー(X-MCP-Access-Key
    • request path(/mcp / /mcp/g/{slug})から allowlist 解決する guildId
  • 処理
    • MCP 専用アクセスキーを許可キーリストと constant-time で照合する(欠如・不一致は拒否し /token/verify を呼ばない)
    • /token/verify(現行 I/F・毎リクエスト)で JWT を検証し、userType === 'staff' を強制する
    • payload を構造検証し、sub を userId とする
    • BAN を確認する(authority の現在値)
    • guildId はサーバー側バインドのみとし、tool 引数・query・header からは取得しない
    • 失敗時は拒否する
  • 出力
    • 検証済み userId(sub)とサーバー側バインドの guildId
  • 異常系・エラー処理
    • 検証失敗、非スタッフ種別、到達不可、BAN は fail-closed
  • 関連する画面/コマンド
    • /mcp

F-005: 監査・キャッシュ制御

  • 概要:権限キャッシュを持ち、権限変更時に即時無効化する
  • 入力
    • Gateway のロール更新、チャンネル更新、スレッド変更、メンバー変更イベント
  • 処理
    • 可視チャンネル集合をキャッシュする
    • 権限に影響するイベントで該当キャッシュを破棄する
    • 監査ログに userId、guildId、付与スコープ、除外された channel_id、件数を記録する
  • 出力
    • キャッシュ済み権限情報
    • 監査ログ
  • 異常系・エラー処理
    • Gateway イベントを受信できなかった場合は TTL で補完する
    • 秘密情報や本文はログに残さない
  • 関連する画面/コマンド
    • 運用監視 / ログ確認

5. 非機能要件

区分要件
性能キャッシュヒット時の検索応答は p95 2 秒以内を目標とする。キャッシュミス時も 5 秒以内を暫定目標とする
受信制御遮断段を認証前後に分ける。認証前は MCP 専用アクセスキーの keyId を key とする Rate Limiting binding(300 calls / 60 seconds)で /token/verify 到達前に遮断する(userId は検証後にしか得られず、userId クォータでは認証基盤への洪水を防げないため)。認証後は userId 単位の Rate Limiting binding(30 calls / 60 seconds、location-local・許容的)を用いる。厳密なサーキットブレーカー状態は guild 単位の Durable Object(SQLite storage)を正本とし、複数 isolate 間で共有する。half-open の probe は期限付き lease で発行し、lease 期限切れ後の報告は破棄する。1 ツール呼び出しあたりの上流予算も設け、悪意ある過剰呼び出しを遮断する。拒否応答は HTTP status(429 / 503)と JSON-RPC error(RATE_LIMITED / CIRCUIT_OPEN)の双方で表現し、Retry-After を付与する
可用性再接続可能な Gateway 切断は自動再接続する。復帰不能な shardDisconnect はプロセスを終了し、close code が自前 allowlist(4004 / 4010 / 4011 / 4012 / 4013 / 4014)に一致する場合は自動再起動を抑止して手動是正へ、不一致の場合(v14.26.3 では到達しないバージョンアップ互換の分岐)のみ、PM2 の min_uptime: "60s" 未満で繰り返す連続異常終了を max_restarts: 5 と指数バックオフで停止する。PM2 のカウンタは 60 秒以上の稼働でリセットされるため、長間隔で繰り返す障害はアプリ側の永続 restart budget(24 時間 rolling window で 10 回)で打ち切る。恒久停止は B-03 の認証済み heartbeat 欠損でリクエスト有無にかかわらず検知し、Workers 側の内部 API 到達不可 / GATEWAY_UNAVAILABLE 率アラートを補助経路とする。Gateway Bot は単一プロセス運用のため冗長化しない(R-15)。検索 API の未インデックス応答はバックオフ付きで再試行する
セキュリティ権限外チャンネルの結果混入を禁止し、認証失敗・権限不明時は fail-closed とする
拡張性検索 Provider とキャッシュ backend をインターフェースで分離し、将来差し替え可能にする
運用・保守性監査ログ、メトリクス、ヘルスチェックを備え、MCP 本体は Cloudflare Workers、Discord Gateway Bot は VPS 常駐プロセスとしてそれぞれ独立に運用できること。requestId で両プロセスの記録を相関できること
互換性Cloudflare Workers ランタイム(MCP 本体)、Node.js 20+(Bot)、TypeScript 5+、discord.js v14.26.3、MCP 2025-06-18 系に整合すること。Workers / VPS の独立リリース時も versioned 内部 API の後方互換性を維持すること。discord.js 更新時は Gateway イベント状態遷移の結合テストを再実行すること
コスト追加の外部 DB や Redis を初期必須にせず、既存 Cloudflare Workers 資源、guild 単位 Durable Objects の最小状態、既存 VPS 資源内で運用できること

6. システム構成・アーキテクチャ

6.1 全体構成図

MCP 本体(transport / 認証)は Cloudflare Workers、Discord Gateway Bot(権限解決 / キャッシュ / 検索実行)は VPS 常駐プロセスとして分離する(詳細は details.md を参照。アーキテクチャ分離は Issue #371 による)。 外部公開入口は既存の gateway-worker に集約し、MCP_WORKER Service Binding で非公開の MCP Worker へ /mcp prefix(/mcp/mcp/g/{slug})を転送する。

6.2 使用技術・技術スタック

区分技術配置選定理由
言語TypeScript共通型安全性と既存モノレポとの整合性
フレームワークHono + @hono/mcpWorkersMCP エンドポイントを軽量に実装できる
インフラ/ホスティング(MCP 本体)Cloudflare WorkersWorkers既存 Gateway Pattern との整合、エッジでの低レイテンシ
インフラ/ホスティング(Bot)VPS 常駐 Node.js + PM2 5.1+(fork mode / instances: 1VPSDiscord Gateway 常駐が必要なため。恒久停止時の自動再起動抑止(stop_exit_codes)に依存するためバージョン下限を固定し、systemd は使用しない。Gateway 接続・readiness・権限キャッシュがプロセスローカルなため cluster mode は使用しない(R-15)
状態管理Cloudflare Rate Limiting binding(認証前 keyId 単位 / 認証後 userId 単位)+ guild 単位 Durable Objects(SQLite storage)Workers過剰呼び出しの早期遮断と、全 isolate で共有する厳密なサーキットブレーカー状態に使用。検索データ・userId・検索クエリは保存しない
データベース既存認証基盤の D1 を参照 / Durable Objects はサーキットブレーカー状態のみに限定共通Discord メッセージや検索結果を自前保存しない
認証既存 soshosai 認証 JWT + /token/verify(Service Binding 経由)Workers利用者 ID を信頼経路で扱うため
その他ライブラリdiscord.js v14.26.3, zod, @modelcontextprotocol/sdk, pnpm両方権限判定、入力検証、MCP 実装に必要

6.3 外部連携・API

連携先用途認証方式
Discord Search Guild Messages APIメッセージ検索Bot token(VPS)
Discord Gateway / REST API権限判定、イベント受信、キャッシュ無効化Bot token(VPS)
gateway-worker /mcp(実サービス名 system-gatewayAI Agent Host からの唯一の公開入口、非公開 MCP Worker への転送通常の MCP_WORKER Service Binding(Worker → Worker)
unified-auth-worker /token/verifyJWT 検証、BAN 確認Cloudflare Service Binding(Workers)
VPS 内部 API v1認証済みリクエストの Workers → VPS 転送Cloudflare Tunnel(非公開経路)+ Cloudflare Access(Service Auth service token)+ アプリ層 HMAC 署名の3層(B-01 で確定。HMAC を送信元と完全性の最終的な検証の根拠とする)

7. データ要件

7.1 データモデル概要

検索対象のメッセージ本文やインデックスは保持しない。ローカルで保持する主なデータは、権限キャッシュ、監査ログ、設定値である。

7.2 主要エンティティ

エンティティ名概要主な属性
PermissionCacheEntryユーザーごとの可視チャンネル集合guildId, userId, visibleChannelIds, computedAt, expiresAt
AuditLogEntry検索・権限判定の監査記録requestId, userId, guildId, grantedScope, excludedChannelIds, resultCount
SearchResultEnvelopeMCP の返却結果channelName, author, contentExcerpt, timestamp, link, paginationCursor

8. 画面・UI要件(該当する場合)

画面ID画面名概要モック/参考リンク
UI-01MCP ツール呼び出し人が直接操作する画面は持たず、AI Agent からのツール呼び出しで利用するdetails.md

9. 制約条件

  • 予算・期間の制約: 既存 Cloudflare Workers 資源および既存 VPS 資源を前提とし、初期段階で追加の外部基盤を増やしすぎない
  • 技術的制約: Discord Search API はレート制限や挙動の変更がありうる。MessageContent privileged intent も必要。Cloudflare Workers と VPS の 2 プロセス構成となるため、両者間の内部 API 設計が新たな技術的制約となる(Issue #371)
  • 規約・ポリシー上の制約: 認証・権限制御はサーバー側で完結させ、権限外データを返さない
  • 体制・リソースの制約: 専用 Bot、認証連携、運用監視、Workers/VPS 間の内部 API を含むため、実装と検証の負荷は小さくない

10. 前提条件

  • 利用者は既存の soshosai 認証基盤で識別できる
  • 対象 Discord ギルドに専用 Bot を招待できる
  • Discord Search API が利用可能である
  • 監査ログは既存資源で賄う(Workers native observability + VPS 構造化ログ。Workers のアラートは log-throw-man へ集約し、requestId で相関)。VPS の外部監視サービスとの通信仕様は B-03 で確定し、認証済み heartbeat と欠損アラートを有効化するまで本番リリースしない(details.md §6.3 / §7.2)

11. リスク・課題

ID内容影響度対応方針担当者
R-01Discord Search API の仕様変更やレート制限強化Provider を分離し、再試行と例外処理を実装する開発担当
R-02古い権限キャッシュによる情報漏洩Gateway イベントで即時無効化し、イベントを受信できなかった場合は TTL(既定 5 分・最大 15 分、超過設定は起動時に拒否)で補完する。shardReconnecting で該当 shard を ready=false にし、guildUnavailable では該当 guild のみを ready=false にして検索を拒否(fail-closed)、再同期後に担当 guild を flush して受付再開する。shardDisconnect は復帰不能としてプロセスを終了し(自前 allowlist に一致する close code は自動再起動を抑止して手動是正、不一致の場合のみ上限付き・バックオフ付き再起動とし、長間隔で繰り返す障害はアプリ側の永続 restart budget で打ち切る)、shardError 単体では readiness を変更しない。対象 guild の受付は担当 shard と guild 自体がともに ready の場合に限り、障害のない guild は受付を継続する。正常接続中に旧権限情報が参照される時間(最長 TTL 以内)と R-11 の Invalid Session 経路を許容する。再接続・再開・復帰不能な切断・guild unavailable の結合テストを必須とする。詳細は details.md §5.4「キャッシュによる権限反映のタイムラグ」開発担当
R-03base スタッフ JWT は全サービス共通の広い権限を持ち、AI Agent Host 設定に保存されるため、漏洩時の影響が MCP に限定されないissuer を変更しない本件の範囲では根本解決できない。MCP 専用アクセスキー(JWT 単体では /mcp を呼べない)+userType: 'staff' 強制+サーバー側での guildId 決定+VPS 側の権限制御+毎回検証で影響を緩和し、残存リスクを記録する。issuer 側に aud を導入する際に B-02 を再検討するプロダクトオーナー
R-04MessageContent privileged intent の取得遅延専用 Bot として申請・設定を先行する開発担当
R-05AI Agent 側の利用方法が固まらないMCP ツール単位の利用契約に加え、接続契約 I/F(guild ごと URL + AuthorizationX-MCP-Access-Key)を固定し、汎用 MCP クライアント前提とする(具体ホスト実装は別 Issue。details.md §4.6)プロダクトオーナー
R-06Workers ↔ VPS 内部 API の通信断・レイテンシB-01 で確定した3層構成(Tunnel + Access + HMAC)を実装し、到達不可時は fail-closed で拒否する。cloudflared / Tunnel の健全性監視を運用に含める開発担当
R-07v1 では内部 API の有効期限内(約 60 秒)のリプレイを防止しない実装・運用コスト(再起動ごとの受付停止と単一プロセス制約)に見合わないため受容。攻撃には Access service token と署名済みバイト列の双方が必要で、HMAC により userId / guildId / input を改変できず「直前の同一検索の再実行」に限定される。envelope の nonceREPLAY_DETECTED を予約し、契約変更なしに Phase 2 で有効化できる状態を維持するプロダクトオーナー
R-08/api/token が guest トークンを優先して返すため、スタッフトークンの誤設定が起こりやすい根治は login-api 側の実装に属しスコープ外。MCP 側は識別可能なエラー(staff_token_required)で拒否して自己診断可能にし、取得手順(customer Cookie が無い状態で取得、または Cookie 直接取得)を運用手順に明記する。details.md §3.2 / §4.6開発担当
R-09Workers 側監査ログの保持が目標 30 日に満たない(Workers Logs 上限 3〜7 日)追加基盤を増やさない方針のもと、Workers 側の保持期間が短いことを受容する。Discord 通知は配送・保持保証がないため保持の補完手段には数えない(best-effort アラートのみ)。Workers 側にも 30 日の証跡が必要と判断された時点で Logpush → R2 を将来拡張として導入する。details.md §6.3プロダクトオーナー
R-10複数 guild を 1 セッション / 1 ホスト設定に登録すると、prompt injection による guild 横断が成立しうるサーバー側では「登録済み guild への正当なリクエスト」を拒否できないため、この対策はホスト設定と運用に依存する。**1 セッション = 1 guild を運用で強制(MUST)**し、やむを得ず複数登録する場合はホスト側 capability gate(利用可能 MCP サーバーの限定・ツール選択の人手承認)を必須とする。リリース条件として、実利用する各ホスト設定の実物を検証する(単一 guild 構成の確認、または capability gate の実機動作確認)。手順の定義・周知のみでは解消扱いにしない。guild とアクセスキーの関連付けはキー流用を防ぐが横断は防げない。details.md §5.1.1 / §4.6プロダクトオーナー
R-11discord.js v14.26.3 の再開可能な Gateway Invalid Session(OP 9, d=true)は、public Client API で resume 開始前の状態通知がなく、shardReconnecting を経ず replay 後に shardResume のみ発火する型に残るが実ランタイムで発火しない invalidated を統制に使用しない。v1 はこの経路に限り Discord の replay を信頼し、shardResume 受信時に担当 guild を全 flush する。resume 開始から完了まで ready=false にできず、旧権限情報が参照されうる点を残存リスクとして受容し、OP 9 の結合テストで挙動を固定する。厳密な fail-closed が必要になった場合は @discordjs/ws / discord.js に OP 9 状態通知を公開する変更を導入する。details.md §3.8 / §5.4プロダクトオーナー
R-12アラート経路(log-throw-man → Discord チャンネル)は監査ログ本体より閲覧範囲が広く、例外の stack / message 経由で秘密情報が流出しうる送出フィールドを allowlist(requestId・event 種別・guildId・エラー分類・プロセス名・時刻・例外クラス・解析済みスタックフレーム)に限定し、requestPath / workerName も固定候補値に限定して同じマスクを通し(log-throw-man は両フィールドを Discord メッセージ本文へ直接展開するため)、raw の error.message / stack 先頭行と errorMessage フィールドは送出しない。allowlist 済みデータから errorStack を組み立てた後、空でない設定済み秘密値を長さやラベルに依存せず完全一致で [REDACTED] へ置換し、続いて既知の秘密パターン(JWT / Bearer / アクセスキー / HMAC 署名 / service token / 長い base64url・hex 列)をマスクしてから 1,500 文字で切り詰める。1 invocation 内は request-local の送出済みフラグで Service Binding 呼び出しを 1 回に集約し、異なる invocation 間の永続的な重複抑止は v1 の保証対象外とする。完全一致・パターンマスクと重複抑止を単体テスト対象とする。未知形式の秘密がマスク漏れする残存リスクは、本文へ載せる情報を allowlist で絞ることで限定する。details.md §6.3開発担当
R-13自前 allowlist に一致する close code の shardDisconnect(token / intent / shard 設定の誤り)で Gateway Bot が exit 78・自動再起動抑止となった際、実リクエストがなければ Workers のエラー率アラートは発火せず、恒久停止が通知されないおそれがあるB-03 を本番リリース Blocker とし、VPS から外部監視サービスへの認証済み heartbeat と欠損アラートを本番前に有効化する。これを無通信時も機能する一次検知経路とし、Workers 側の内部 API 到達不可率・GATEWAY_UNAVAILABLE 率アラート(Service Binding 経由で log-throw-man へ到達可能)は実トラフィックがある場合の補助経路とする。補助経路が機能するよう、Workers 側の自発的遮断は GATEWAY_UNAVAILABLE ではなく CIRCUIT_OPEN を用い、両者を混用しない。details.md §3.4.3 / §3.8 / §7.2開発担当
R-14AI Agent Host が http:// の endpoint URL を設定した場合、リクエスト送信時点で Authorization(スタッフ JWT)と X-MCP-Access-Key が平文でネットワークへ流れる。Cloudflare の Always Use HTTPS は「HTTP リクエストを受信してから redirect を返す」動作であり、サーバー側の redirect / 4xx 終端では認証情報を保護できないサーバー側では原理的に防げないため、送信前にホスト側で http:// を拒否させることをリリース条件とする。実利用する全 AI Agent Host 設定の endpoint URL が https:// であることを設定の実物で検証し、http:// 設定時にリクエストが送出されないことを実機確認する(手順の周知だけでは解消扱いにしない)。平文 HTTP での /mcp 到達を検知した場合は当該アクセスキーとスタッフ JWT を漏洩扱いとし、details.md §7.6 の緊急ローテーション手順で即時に無効化・再発行する。平文終端イベントは監査ログ・アラート対象とする(キー値・トークン値は記録しない)。details.md §1.3 / §4.6 / §7.6プロダクトオーナー
R-15Gateway 接続・shard / guild 単位の readiness・権限キャッシュはいずれも VPS プロセスローカルな状態であり、Gateway Bot を水平スケールできない(単一プロセスが SPOF となる)v1 は PM2 fork mode + instances: 1 に固定して受容する。cluster mode で複数 instance を起動すると IDENTIFY の多重化、instance ごとのキャッシュ不整合による旧権限情報の返却、health/ready の不整合が生じるため、設定上も起動時検証で禁止する。プロセス停止は B-03 の認証済み heartbeat 欠損で検知し、復旧は PM2 の再起動と手動是正に依存する。水平スケールが必要になった時点で、readiness / キャッシュの共有ストア化と shard 配分設計を伴う拡張として再検討する。details.md §3.4 / §7.1開発担当
R-16サーキットブレーカー状態を Durable Object に集約したため、Durable Object 側の障害・instance migration が検索全体の可用性に影響しうるサーキットブレーカーは Discord 上流を保護する可用性統制であり認可境界ではないため、Durable Object 到達不可時は fail-closed とせず、isolate ローカルのフォールバック制御へ縮退する。縮退の発生はメトリクス化してアラートする。権限判定・可視性判定は本縮退の対象外とし、従来どおり fail-closed を維持する。details.md §7.3開発担当

11.1 実装・リリース Blocker

以下は該当するシステム間の接続・有効化・環境リリース前に解消する。独立して検証可能な schema / authz 実装は進められるが、単にリスクを受容するだけでは解消扱いにしない。

IDBlocker解消条件状態
B-01Workers ↔ VPS 内部 API の通信・認証方式Cloudflare Tunnel(非公開到達経路)+ Cloudflare Access(Service Auth service token)+ アプリ層 HMAC 署名(送信元と完全性の最終的な検証根拠)の3層で確定。deadline horizon 30s / clock skew ±30s とし、v1 ではリプレイ可能な時間を約 60 秒に制限する。nonce 照合は Phase 2 とする(nonce / REPLAY_DETECTED は前方互換のため予約。残存リスクは R-07)。health の別ポート分離、HMAC keyId ローテと Access token 失効管理を details.md §3.3 / §3.4 / §7.6 に定義。内部 hostname を本番と同一ゾーンに置くため、MCP Worker に global_fetch_strictly_public compatibility flag を必須設定し、接続前に staging で loopback の肯定系(Access 評価と Cf-Access-Jwt-Assertion 到達)と否定系(service token なしが Access で遮断され VPS へ到達しない)の両方を検証する。不成立時は Tunnel + HMAC + WAF の2層構成へ切り替え、VPS の Access JWT 検証モードも disabled へ計画的に変更する。HMAC を送信元と完全性の最終的な検証の根拠とする方式確定(接続前の staging 検証を残す)
B-02aud / guildId の署名済み伝搬unified-auth-worker を変更せず、MCP 専用アクセスキー(X-MCP-Access-Key。staff JWT 単体では /mcp を呼べない宛先スコープの強制。keyId と guildId の関連付けでキーの流用を遮断。prompt injection による guild 横断は「1 セッション = 1 guild」の運用上の対策で担保する。R-10 参照)+ MCP Worker で /token/verify 検証後の userType === 'staff' 強制(ゲスト JWT 拒否)+ request path を allowlist で解決して対象 guildId をサーバー側で決定する方式(マルチギルド routing、未定義 path は 404)とする。毎リクエスト検証で BAN の最新状態を確認する。これらを補完的な対策として S-003 に定義する。トークン層 aud は issuer 側対応を要する将来拡張として残す方式確定
B-03VPS の外部監視サービスとの通信仕様本番リリース前に必ず解消する。既存資源を優先してサービスを選定し、環境別 endpoint、service token 等の送信元認証、送信データ形式、heartbeat の送信間隔・有効期限、timeout、retry / backoff、rate limit、欠損アラート条件、否定系テストを details.md §6.3 / §7.2 / §7.5 と運用手順に定義する。通常リクエストが 0 件でも exit 78 の恒久停止を検知できる試験に合格する。確定前は開発・pre-production の PM2 ローカル監視と VPS 構造化ログだけを使用でき、本番稼働は許可しない未確定(本番リリース Blocker。サービス選定が必要)

12. マイルストーン・スケジュール

フェーズ内容期限ステータス
要件定義本書作成、関係者確認2026-07-08作成中
設計details.md に基づく実装設計未定未着手
実装MCP サーバー、専用 Bot、認証連携未定未着手
テスト権限回帰、認証否定系、DoS 制御未定未着手
リリースpre-production への反映未定未着手

13. 受け入れ条件(Acceptance Criteria)

  • search_messages が可視チャンネル外の結果を返さない
  • list_accessible_channels が権限解決失敗時に fail-closed で動作する
  • get_message_context を実装した場合、可視性を再検証したうえで文脈を返す
  • 認証失敗、BAN、権限不明時に検索処理を実行しない
  • 公開 /mcpgateway-worker から MCP_WORKER Service Binding 経由で転送され、MCP Worker が workers_dev: falsepreview_urls: false、公開 routes / custom domains なしである
  • gateway-worker が path / query、method、request headers / body、status、response headers、streaming response を保持し、/mcpOPTIONS を汎用 handler で終端しない。gateway-worker に置かれたロジックは route 転送と転送前 TLS 判定のみで、認証・guild 解決・body 解釈を行わない
  • 公開 /mcp への平文 HTTP が zone の Always Use HTTPS で redirect されるか gateway の 4xx で終端し、MCP_WORKER Service Binding、/token/verify、MCP 専用アクセスキー検証へ到達しない。HTTPS の同一 request は通常の認証処理へ進む。判定は new URL(request.url).protocol を一次根拠とし、request.cf.tlsVersion の有無だけに依存しない
  • 実利用する全 AI Agent Host 設定の endpoint URL が https:// であることを設定の実物で検証し、http:// を設定した場合にホスト側がリクエストを送出しないことを実機で確認する(サーバー側の redirect / 4xx は認証情報を保護できないため。R-14 のリリース条件。手順の定義・周知だけでは合格としない)
  • 平文 HTTP の終端イベントが監査ログとアラートに記録され、キー値・トークン値が出力されない。検知時のアクセスキー・スタッフ JWT の緊急ローテーション手順が運用手順に定義されている
  • 内部 API が未認証リクエスト、認証済みコンテキストとの不一致、不正 schema、期限切れを fail-closed で拒否する(許容期間内でのリプレイ攻撃防止は Phase 2。R-07 の受容範囲)
  • 内部 API の raw body は 65,536 bytes まで受理し、65,537 bytes 以上を 413 PAYLOAD_TOO_LARGE で HMAC・schema 検証前に拒否する(Content-Length 宣言あり / streaming の双方)。Workers 側も同じ共通定数で署名前に検査し、上限超過 body を送信しない
  • v1 では有効期限内の同一 raw body / nonce の再送を受理し、期限超過後は REQUEST_EXPIRED で拒否する。Phase 2 の nonce store を有効化した場合のみ、期限内の再送を 409 REPLAY_DETECTED で拒否する
  • versioned 内部 API の consumer / provider contract test が新旧 Workers / VPS の組み合わせで通る
  • requestId が Workers / VPS の request・response・監査ログで一致する
  • 監査ログに秘密情報やメッセージ本文が残らない
  • アラート連携先の log-throw-manworkers_dev: falsepreview_urls: false、公開 routes なしであり、未認証の公開エンドポイントを持たない
  • 権限変更イベントでキャッシュが無効化される
  • PREAUTH_RATE_LIMITER が keyId 単位で 300 calls / 60 seconds の超過を 429 で遮断し、その際 /token/verify を呼ばない(未認証洪水が認証基盤へ増幅されない)
  • USER_RATE_LIMITER が同一 location の同一 userId に対する 30 calls / 60 seconds の超過を 429 で遮断する。location-local・許容的であり、厳密な全体カウンタとして扱わない
  • 受信制御による拒否が HTTP status(429 / 503)と JSON-RPC error(RATE_LIMITED / CIRCUIT_OPENretryable)の双方で表現され、Retry-After が付与される
  • guild 単位の MCP_CIRCUIT_BREAKER Durable Object が複数 isolate 間で状態を共有し、60 秒 window・最低 10 calls・上流エラー率 50% で 30 秒 open、単一 half-open probe、成功時 closed / 失敗時再 open となる。異なる guild は相互に遮断しない
  • half-open の probe が期限付き lease(probeId / 10 秒)で発行され、lease 期限切れ時に open へ戻る(Worker 中断時に half-open が永久占有されない)。lease 期限後に到達した stale な結果報告は状態遷移へ反映されない
  • MCP_CIRCUIT_BREAKER 到達不可時は遮断せず isolate ローカルのフォールバック制御へ縮退し、縮退がメトリクスに記録される(権限判定・可視性判定は縮退対象外で fail-closed を維持する。R-16)
  • MCP_CIRCUIT_BREAKER に userId・検索クエリ・channelId・メッセージ本文が保存されず、60 秒 window 外の記録が pruning され、無トラフィックが 24 時間続いた instance が破棄される
  • Workers 側の自発的遮断が CIRCUIT_OPEN、VPS 側の Gateway 不可が GATEWAY_UNAVAILABLE として区別され、監査ログ・メトリクスに originworkers / vps)が付与される(R-13 の補助検知が上流劣化と混同されない)
  • 非スタッフ(ゲスト含む)トークンで /mcp の全ツールが拒否される(VPS へ到達しない)
  • 非スタッフトークン時の応答が 401 かつ error body に staff_token_required を含み、WWW-Authenticate: Bearer が付与され、汎用の認証失敗と区別できる
  • 非スタッフトークン時の監査ログに reason: non_staff_principal が残り、トークン値が残らない
  • 実際に利用する各 AI Agent Host 設定に対して、登録済み MCP サーバーが 1 guild のみであることを検証する(設定の実物を確認する。手順の定義・周知だけでは合格としない)。複数登録する構成を採る場合は、当該ホストで capability gate が有効に機能すること(別 guild のツールがセッション中に選択できないこと)を実機で検証する。いずれも未実施のまま本番リリースしない(R-10 のリリース条件)
  • MCP 専用アクセスキーの欠如・不一致で全ツールが拒否され、staff JWT 単体では /mcp を呼び出せない
  • guild A に関連付けられたアクセスキーで guild B の route を呼ぶと拒否される(キー流用の遮断)
  • shardReconnecting で該当 shard の検索が拒否され(fail-closed)、切断中に権限剥奪された利用者へ旧権限情報に基づく結果を返さず、shardResume / shardReady 後に担当 guild を flush してから受付を再開する
  • shardDisconnect では復帰イベントを待たずプロセスが終了し、shardError 単体では readiness を変更しない
  • shardDisconnect の close code が自前 allowlist(4004 / 4010 / 4011 / 4012 / 4013 / 4014。discord.js の private 定数は import しない)に一致する場合、終了コード 78 で終了して再起動されないこと(PM2 5.1+ の stop_exit_codes 実設定を確認する)。allowlist 外の close code(v14.26.3 では実発生しないバージョンアップ互換の分岐)を起動後 60 秒未満で繰り返した場合は、PM2 の min_uptime: "60s" / max_restarts: 5 / exp_backoff_restart_delay: 1000 により 5 回の連続異常終了後に停止する。60 秒以上の安定稼働でカウンタがリセットされることも確認し、通算上限とはみなさない
  • allowlist 外の close code を 60 秒より長い間隔で繰り返し注入した場合でも、アプリ側の永続 restart budget(24 時間 rolling window / 10 回)により終了コード 78 へ切り替わって再起動が打ち切られる。budget ファイルは起動時に window 外の記録を pruning し、close code・shardId を保持しない。budget ファイルへの書き込みが IO エラーで失敗した場合も終了コード 78 で終了する(fail-closed)
  • VPS 内部 API が PM2 fork mode + instances: 1 で起動し、cluster mode / instances > 1 の設定が起動時検証で拒否される(Gateway 接続・readiness・権限キャッシュがプロセスローカルであるため。R-15)
  • 通常の MCP リクエストが 0 件でも、exit 78 による Gateway Bot 恒久停止が B-03 の認証済み heartbeat 欠損アラートで検知される。B-03 を解消してこの検知経路を有効化しないまま Gateway Bot を本番稼働させない。トラフィックがある場合は Workers 側の内部 API 到達不可 / GATEWAY_UNAVAILABLE 率アラートも補助経路として発火する
  • discord.js v14.26.3 の invalidated を readiness 統制に使用せず、再開可能な Invalid Session(OP 9, d=true)後の shardResume で担当 guild を flush する(resume 中に ready=false へ変更できず、旧権限情報が参照されうる点は R-11 の受容範囲)
  • 複数 shard 構成で一方の shard が復帰しても、切断中の別 shard が担当する guild の受付を再開しない。一方、ready な shard / guild の受付は継続し、health/ready は一部正常時に 200 degraded、全 guild 停止時のみ 503 not_ready を返す
  • shardReadyunavailableGuilds に含まれる対象 guild と guildUnavailable を受けた対象 guild は検索を拒否し、guildAvailable 後に当該 guild を flush してから受付を再開する。他に ready な guild があれば停止させない
  • log-throw-man へ stack を含む例外を渡しても、採用される errorStack に requestId / event 種別が残り、後続が allowlist 済みのエラー分類・例外クラス・解析済みスタックフレームだけで構成される。raw の error.message / stack 先頭行と errorMessage フィールドが送出されない
  • MCP 専用アクセスキーを 32 文字未満の任意文字列として設定し、ラベルを伴わず例外データへ埋め込んでも、設定済み秘密値の完全一致マスクにより [REDACTED] へ置換される。JWT・Bearer トークン・HMAC 署名・Access service token・長い base64url / hex 列もパターンマスクされ、マスク後の errorStack が 1,500 文字を超えず、request body・header・検索クエリ文字列・Discord メッセージ本文が含まれない。UUID 形式(8-4-4-4-12 のハイフン付き hex)はパターンマスクの対象外であり、requestId 等の相関識別子が [REDACTED] に置換されない
  • requestPath に対象 guildId 以外(request path・URL・例外由来文字列)、workerName にプロセス名以外が入らず、両フィールドも完全一致・パターンマスクを通り、バッククォート・改行を含む値が拒否または除去される(log-throw-man はこの2フィールドを Discord メッセージ本文へ直接展開するため)
  • 1 invocation 内で複数の例外ハンドラが同じ requestId の通知を試みても、log-throw-man への Service Binding 呼び出しが 1 回に抑止される。異なる invocation 間の永続的な重複抑止は v1 の受け入れ条件に含めない
  • BAN 適用後の次のツール呼び出しが拒否される(トークン TTL 内であっても)
  • guildId をツール引数・query・header から受け付けず、サーバー側の設定だけで決定する
  • Blocker B-01 / B-02 / B-03 が解消され、決定内容が details.md と運用手順に反映されている

14. 承認

役割氏名承認日サイン/コメント
プロダクトオーナーHaruto Yamazaki
開発担当Haruto Yamazaki / Sho Sato
承認者未定

付録