Phase:M04 — Web Chat MCP Integration & AI Join Kit 類型:M Phase(Modification / Maintenance Phase) 插入時點:P3 關門之後、P4 開工之前。 M04-A 與 M04-B 先於 P4;M04-C 為 P4 前的收尾。若 M04-A 證明目標平台在個人方案上不可行,M04 標為 Platform Blocked / Deferred,直接進 P4。 本文件定義 M04-A~M04-C 每個 Subphase 完成後什麼必須為真。具體 module、endpoint、schema、文字內容與接線放在
開發設計方針.md;自動與人工驗收方式放在測試指南.md。
最後更新:2026-09-11
P3 已交付 POST /mcp:AI 以 P3-D 的 scoped AI Join Token 進場,tool catalog 依 Player/DM/pre-session DM 產生,Human UI 與 MCP 共用同一組 application service。P3-F 以 Claude Code 2.1.260 經 HTTPS 跑完整條 DM journey,證明入口可用。
P3 關門當天(2026-09-11)的第一次真實使用暴露了三件事:
因此 M04 的順序是:先用一個極小的獨立 HTTPS 測試 server 對真實網頁版 chat 做完整 preflight(ChatGPT Plus 優先,不行再驗 Claude chat 個人方案),再把 Adventure Table 的正式入口做成符合那份實測契約,最後才做 Join Kit 與其他 client 相容。
平台判定階梯:ChatGPT Plus 可行 → 以它為 M04-B 目標;ChatGPT Plus 因方案限制不可行 → 驗 Claude chat 個人方案;兩者都因平台限制不可行 → M04 標為 Platform Blocked / Deferred,記錄阻礙,直接進 P4。不為 Business/Enterprise/Edu 等付費工作區方案特別做產品。
M04 不做的事:
controller_epoch SSOT、grant generation snapshot、pre-session TTL、Take Back 規則)。OAuth 只是取得既有 Seat/grant 控制權的另一種交付方式。M04 順序:
P3 closeout
→ M04-A — Web Chat MCP Preflight
→ M04-B — Adventure Table Web Chat Integration
→ M04-C — AI Join Kit, Server-hosted Guide & Other Client Compatibility
→ P4
(M04-A 判定 Platform Blocked 時:M04-A closeout → P4,M04-B/M04-C 延後)
M04-A 依「ChatGPT Plus → Claude chat 個人方案」的順序判定目標平台。M04 closeout 的定義是:使用者在 Adventure Table 建好 Seat 並發出憑證後,朋友在目標平台的網頁版 chat 加一個 connector、完成一次 OAuth、就能以那個 Seat 的 role 進桌,並完成至少一場 DM 與一場 Player 的真實迴圈。 達標平台不可 out-of-scope;不得以「其他 client 可用」宣稱 M04 完成。
若兩個平台都因方案/權限限制不可行,M04 在 M04-A closeout 標為 Platform Blocked / Deferred,記錄每個平台的阻礙與日期,直接進 P4;M04-B/M04-C 延後到平台狀況改變再重啟。
client_id)可能服務多個人的不同 Seat,因此 client_id 不是「一個 Seat 授權」的識別;revoke 只作用在該 authorization 的 token family,不得撤銷同 client_id 的其他 family。controller_epoch == generation、pre-session TTL 與 Session 狀態有效;任何一項不成立就拒絕並使整個 family 失效。refresh token 自身未過期不構成授權。P3 的 role-scoped catalog 在目標平台 frozen snapshot 下有三種可能結果,M04-A 必須實測出是哪一種,M04-B 依結果設計:
| preflight 結果 | M04-B 對應 |
|---|---|
| 重新 authorize 後平台會重新 scan,catalog 跟著 role 變 | 沿用 P3 role-scoped catalog,不改 |
| 需要使用者手動 Refresh 才更新 | 沿用 role-scoped catalog;kit 與指引明講「換 role 後要 Refresh」 |
| snapshot 凍結、無合理刷新路徑 | 改為對目標平台暴露單一 union catalog,權限仍由 server 在 tools/call 階段依 grant role 拒絕;或改為「每個 role 一個 connector URL」。二選一在 M04-B 開工前由使用者拍板 |
不論哪一種,server 端的 tools/call 授權檢查是唯一真值;catalog 呈現只是 discovery 便利,不是權限邊界。
使用者取得 AI credential 時,畫面上拿到的是一份可複製、可下載的純文字 AI Join Kit:URL、token、role、以及「指引在哪」。kit 依 M04-B 定案的入口形態產生(目標平台網頁版 chat 走 connector + OAuth;Claude Code 走 Bearer;有 shell 的 AI 走 GET /mcp/guide)。DM/Player 同一份模板,role 由 token 決定。
GET /mcp/guide(無認證純文字)、加厚的 tool description、get_session_context.briefing 三個通道交付同一份內容。指引告訴 AI 怎麼用桌,不提供劇本。
單機版不掛 /mcp、OAuth endpoint 或 /mcp/guide。M04 不得讓 app.standalone、app.content.*、app.domain.character* 反向依賴 app.mcp 或任何 OAuth module。
M04-B 的任何 OAuth/protocol/catalog 設計決定都必須能指到 M04-A 的 preflight 記錄。沒有實測證據的猜測不得寫成契約。
每個 request 仍走 P3-D:grant → Seat current binding → controller_epoch == generation → scope。OAuth token 只是找到 grant 的另一把鑰匙;找到之後的流程與 Bearer AI Join Token 完全相同,不得複製第二份授權邏輯。
見 2.2。這是不可違反的 invariant,M04-B 必須有自動測試證明 OAuth 流程對 campaign_seats 零寫入。
除 token 本身外,不得含 Room password、Owner Key、DM Key、其他 Seat 的 token、secret DC、DM Notes。GET /mcp/guide 無認證,內容必須是「任何人都可以知道」的。
M04-B 的 OAuth 授權頁、錯誤訊息,與 M04-C 的指引、briefing、kit、UI copy 都是 user-visible content,同一 Subphase 交付 zh-TW/en。
守則可以給;劇本、世界觀、NPC 設定不得給,那是 P6。
M04-A 的極小 HTTPS server 是獨立程式,不 import app.*,不進 app 打包,不共用 DB;它的唯一目的是量測網頁版 chat 平台。它的管理 endpoint(動態加 tool、撤銷全部 token)只監聽 loopback,不經公開 tunnel;request 與 response 的 JSONL 記錄對 access token、refresh token、authorization code 與任何 secret 欄位做相同的遮罩。
M04-A — Web Chat MCP Preflight
→ M04-B — Adventure Table Web Chat Integration
→ M04-C — AI Join Kit, Server-hosted Guide & Other Client Compatibility
每個 Subphase 必須能獨立實作、驗證並 commit;完成時應處於可執行、可測試、沒有已知編譯/型別/該 Subphase 測試錯誤的狀態。
M04-A 不動 Adventure Table app code,產出是 preflight 記錄、目標平台判定與架構結論。M04-B 完成後目標平台的網頁版 chat 已能進桌,這是 M04 的核心增量。M04-C 補上交付體驗與其他 client。
用一個極小、獨立、對外 HTTPS 的 MCP 測試 server,依「ChatGPT Plus → Claude chat 個人方案」的階梯對真實網頁版 chat 做完整 preflight:OAuth、Scan Tools、一個 read tool、一個 write tool、protocol/metadata/callback 實錄、tool cache/refresh/re-auth 行為。產出目標平台判定,以及可指引 M04-B 設計的實測記錄與架構結論。
tools/m04a-webchat-preflight/,有自己的 dependency 清單與 README,可一行啟動。app.*;test_m03_import_boundary.py 與 test_p3e_architecture_boundary.py 的掃描範圍不含它,但它也不得被任何 app module import。dm/player)並提交固定測試密碼;這模擬「提交 AI Join Token 取得既有 Seat 控制權」。get_context,回 role 與計數)、一個 write tool(post_note,把文字寫進 server 記憶體並回 seq)、一個只在 dm scope 出現的 tool(dm_only_ping)、一個可指定等待秒數的 long-poll tool(wait_seconds)。MCP-Protocol-Version、JSON-RPC method、Mcp-Session-Id 有無、Accept 是否要求 SSE 全部寫進 JSONL 記錄;request 與 response 都做相同遮罩:Authorization 值、access token、refresh token、authorization code、client secret 與任何名稱含 token/code/secret 的欄位一律遮罩。request.client.host 為 loopback 才服務),不經公開 tunnel;經公開入口打到它必須是 404。在目標平台(先 ChatGPT Plus)的網頁版上完成一次完整 authorization,記錄:平台呼叫了哪些 metadata endpoint、是否用 dynamic client registration、redirect URI 形態、PKCE 有無、要求的 scope、token endpoint 的 grant type、access token 與 refresh token 的使用方式。
記錄平台在 Scan Tools 時送出的:MCP-Protocol-Version(或缺席)、第一個 JSON-RPC method(initialize/server/discover/直接 tools/list)、是否帶 Mcp-Session-Id、是否要求 SSE、_meta 內容。這決定 M04-B 是否需要 protocol 相容層。
在平台對話中觸發 get_context 與 post_note,兩者都收到正確結果;記錄平台對 write tool 的確認 UX(是否每次要使用者按確認、能否標記為免確認)。若 write 在該方案上被拒,記錄平台訊息並依階梯進下一個平台。
依序執行並記錄每一步平台看到的 tool 清單:
dm authorize → Scan Tools → 清單應含 dm_only_ping。player → 平台是否重新 scan?dm_only_ping 是否消失?需要 Refresh/重新發佈/新 connector 嗎?以 wait_seconds 分別等 10、30、60 秒,記錄平台端的 timeout 上限。這決定 M04-B 對 wait_for_event 的 timeout 上限是否要下修。
docs/M04/M04-A_PREFLIGHT.md 含:每個嘗試過的平台各一節(A.2~A.6 每項的記錄、日期、方案與可辨識版本、入口形態(不含憑證)、遮罩後的關鍵 request/response 樣本),目標平台判定(ChatGPT Plus/Claude chat 個人方案/Platform Blocked),以及對 M04-B 的明確結論:
wait_for_event timeout 上限。若當天方便,同一測試 server 對 Claude Desktop 也量一次 A.3,記錄即可,不影響 M04-A 關門。Claude chat 個人方案若已作為第二階正式量測,則不屬本項。
依 M04-A 實測契約與目標平台判定,讓目標平台的網頁版 chat 以 OAuth 取得既有 Seat/grant 的控制權並進桌;完成真實 DM 與 Player 兩場迴圈。M04-A 判定 Platform Blocked 時本 Subphase 延後。
ai_controller_grants row;DB 只留 hash。resolve_current_scope(grant 有效、Seat current binding 為此 grant、controller_epoch == generation、TTL/Session 狀態有效);任一不成立則回 oauth_invalid_grant 並使整個 family 失效。refresh token 自身未過期不構成授權。authenticate_request 之後的流程與 Bearer AI Join Token 完全相同;沒有第二條授權路徑。campaign_seats、sessions、session_participants 零寫入。client_id 不是 Seat 授權的識別:同 client_id 下其他 grant 的 family 不受任何一次 authorize/revoke 影響。2026-07-28 路徑行為完全不變;P3-E 全部 protocol 測試原樣通過。initialize 若必須回應只回靜態能力;Mcp-Session-Id 若必須回傳則是無狀態 opaque 值,server 不以它保存任何 gameplay scope。依 2.3 表格落點實作;若落在第三列,使用者拍板 union catalog 或 per-role URL 後才開工。不論哪種,tools/call 階段的 role 檢查是唯一真值,並有測試證明 Player token 呼叫 DM tool 被拒且零副作用。
wait_for_event timeout 上限依 M04-A 結論若目標平台端容忍度低於 60 秒,MCP 入口對 timeout 上限下修至實測值;Human UI 的 long-poll 不受影響。
2026-09-12 B.8 後補充:M04-A 實測上限為 120 秒,M04-B 關門時保守維持 60。B.8 觀察到 ChatGPT 每回合只會 wait 一次就停下來等人戳,決定把 MCP 入口上限提到 120 秒(不超出 M04-A 證據),並在 M04-C 指引加「連續等待上限」規則(見 C.2 第 6 點)。上限修改與測試更新在 M04-C 交付,
test_m04b_wait_timeout_cap.py斷言同步改為 120。
README 部署段新增「讓網頁版 chat 連進來」:HTTPS 入口形態、只轉發 /mcp* 與 OAuth 路徑、不把 Room UI 露出公網;不含憑證。
以只拿到 token 的目標平台網頁版 AI:
get_session_context → start_session → post_narration → wait_for_event → Human Player 回應 → AI 再回應 → Owner revoke → AI 下一次呼叫失效。Let AI Control → 平台 OAuth → post_dialogue → Human DM request_check → AI roll_pending → Human Take Back Control → AI 下一次呼叫失效。兩場都是 M04 closeout 必要條件。
把「發 token」升級成「發 kit」,讓指引住 server、與 tool 契約同源;kit 依 M04-B 定案的入口形態產生。順帶記錄非目標平台網頁版 chat/Codex CLI/Codex Desktop 的相容狀態,但不作為 gate。
GET /mcp/guide 存在且無需認證GET /mcp/guide,text/plain; charset=utf-8,200,不需 Bearer,不碰 DB。zh-TW/en。至少包含:
POST /mcp header 契約 + 最小 client 範例)。POST /mcp 的 header 契約、body _meta、不需 initialize、不得帶 Mcp-Session-Id(若 M04-B 加了相容層,另註明 legacy 路徑)。tools/call 範例與成功/業務錯誤/協定錯誤三種回應。get_session_context → pre_session 就 start_session、active_session 就續場 → cursor 規則 → wait_for_event 迴圈。等待規則:每次 wait_for_event 用上限 timeout: 120;逾時(空回傳)就直接再等,連續最多 5 次(約 10 分鐘);5 次都沒事件就停下來,用一句話告知人類「桌上沒有動靜,需要時叫我」,不再自行呼叫;收到事件並回應後計數歸零。request_check;要做 check 時用 post_action 描述意圖,等 DM 建立 Check,再以 roll_pending 或 submit_physical_roll 完成。ai_token_unauthorized 的處置:停止並告知使用者,不重試。不得含 3.4 的任何秘密,不得含 3.6 禁止的劇本內容。
tools/list 每個 description 擴充為用途、何時用、關鍵參數與合法值;仍依 role scope 過濾,不洩漏對方 role 的工具存在。
Catalog 在 Session 開始前後相同(2026-09-12 修訂)。 同一個 role 的 tools/list 是一組固定集合:DM 不論 grant 是否已綁 Session,都列出 start_session 與全部 DM gameplay 工具;Player 不變。原因:ChatGPT connector 在加入當下快照一次 tools/list,M04-B 原本「pre-session 只列兩個工具」讓 AI 在 start_session 後必須 Refresh 並開新對話才看得到 gameplay 工具,這是第一次真實使用暴露的最大摩擦。執行仍照 P3 契約閘門:未綁 Session 呼叫 gameplay 工具回 active_session_required,已綁 Session 呼叫 start_session 回 pre_session_only,兩者零副作用;P3 實作規格「未綁定時只能執行最小 pre-session/Start 能力」仍成立,改變的只是列表。每個 description 必須寫明該工具何時被接受(Session 前後皆可/只在開始前/需要 active Session)。
get_session_context 回傳 briefingGET /mcp/guide。temporary_instruction 保持獨立欄位。request_check、post_narration、set_stage_text;DM briefing 不得提及 quick_roll、whisper_dm。active-session briefing 是強制流程,不是描述(2026-09-12 修訂)。 第一次真實跑桌暴露:AI 只讀 briefing、不會去讀完整 guide,所以核心跑團流程必須直接寫進 briefing。active briefing 改成逐步「必跑流程」:DM=讀 stage→stage_unset 為真先 set_stage_text→post_narration(100–250 字,秘密不進 Stage,暗骰 dm_only)→立即 wait_for_event(不得停在 host chat 等提示)→收到事件即處理,需檢定用 request_check→場景實質變化再 set_stage_text→再 wait_for_event 循環→只有連續 5 次無事件、Session 結束或 host 喊停才停;Player 對應用 post_dialogue/post_action/roll_pending。pre-session briefing 明講「此 token 已使你成為該 DM Seat controller,無入席步驟」與「開場後 Stage 為空,第一步 set_stage_text」。
briefing 帶 MCP 呼叫判定規則。 connector 把「工具探索」與「工具執行」分開,模型會把「看得到工具/重新掃描」誤當成「已呼叫」。briefing(雙語)加硬規則:只有本回合實際執行工具並取得結果才算呼叫 MCP;查看清單、讀 schema、重新掃描都不算,且連線成功/失敗不得據此推測,沒有實際 invocation 只能說「尚未測試」。get_session_context 的 tool description 同步加一句同義規則。
stage_unset / next_required_action(機器可讀提示)get_session_context(active)與 start_session 回傳加 stage_unset: bool 與 next_required_action: str,讓 AI 不必只靠 briefing 文字。next_required_action = "set_stage_text";否則 → "wait_for_event"。Player 一律 "wait_for_event"(不引導去碰 DM-only Stage)。post_narration,守住「Optional 不得變 Mandatory」。server/discover.instructions 指向指引ADVENTURE_TABLE_MCP_PUBLIC_ORIGIN,給網頁版 chat 與機器外的 AI。kit 一句話說明何時用哪個。未設定公網 origin 時不印公網 URL,改印一句「尚未設定公網入口,網頁版 chat 與機器外的 AI 目前連不進來」。(2026-09-12 修訂:原「URL 取自瀏覽器 origin;loopback 時附提醒」的單一 URL 方案,在本機 dev 下連不到 /mcp、給遠端 AI 又要手改 host,故改為雙 URL。)/mcp 的轉發與 /api 相同,非 vite 部署本來同 origin。C.1~C.6 所有 user-visible 文字同時交付 zh-TW/en。
兩條路徑各至少一次,AI session 沒有讀過本 repo、沒有額外口頭說明:
2026-09-12 修訂(使用者拍板):本 Subphase 的 C.8 gate 改為目標平台 ChatGPT Web Plus 憑 kit 進桌一次;上列 Bearer MCP client 與純 HTTP 兩條路徑延後,不列為 M04-C 關門條件。理由:目標平台是 M04 的必要成功條件,其餘路徑屬相容記錄。證據見 M04-C_CLOSEOUT.md。
對非目標平台的網頁版 chat、Codex CLI、Codex Desktop 各嘗試一次 M04-B 的入口,記錄能不能連、卡在哪;寫入 closeout「Compatibility」段。任何一個不通都不阻塞 M04-C 關門。
2026-09-12 修訂(使用者拍板):延後,M04-C closeout 不含「Compatibility」表。
prompts/*、resources/*。