adventure-table

M04 — 實作規格

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


1. M04 定位

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)的第一次真實使用暴露了三件事:

  1. 使用者的首要目標是讓朋友用網頁版 chat(首選 ChatGPT Plus,次選 Claude chat 個人方案)直接進桌當 DM 或 Player。 這條路目前完全不通:網頁版 connector 不能填 static header、需要公開 HTTPS 入口、走 OAuth,而 client 實際送出的 protocol、tool discovery 與 cache 行為對本專案都是未知。
  2. 這些未知不是小細節,會決定架構。 依 reviewer 引述的 OpenAI 文件:custom MCP app 的流程是 authentication → Scan Tools;approved app 使用 frozen tool snapshot,server 之後的 tool 變更不會自動套用,要 Refresh/重新發佈。這直接撞上 P3「tool catalog 依授權 scope(DM/Player/pre-session DM)不同」的設計。不先實測,M04 後續任何入口設計都可能被推翻。
  3. AI 拿到 token 之後不知道該怎麼開始。 token 只在 Lobby 顯示一次,沒有隨附說明;AI 要能讀 repo 才拼得出 client。這是真實缺口,但它必須配合目標平台的已知契約來做,不能先做。

因此 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 不做的事:

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 延後)

2. M04 產品行為

2.1 目標平台由階梯判定,達標平台是 M04 的必要成功條件

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 延後到平台狀況改變再重啟。

2.2 Seat 先於 OAuth,一個 connection 一個固定 Seat

2.3 Tool catalog 對目標平台的呈現由 preflight 決定

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 便利,不是權限邊界。

2.4 AI Join Kit 是唯一的 AI 交付物(M04-C)

使用者取得 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 決定。

2.5 指引住 server、與 tool 契約同源(M04-C)

GET /mcp/guide(無認證純文字)、加厚的 tool description、get_session_context.briefing 三個通道交付同一份內容。指引告訴 AI 怎麼用桌,不提供劇本。

2.6 Standalone 不受影響

單機版不掛 /mcp、OAuth endpoint 或 /mcp/guide。M04 不得讓 app.standaloneapp.content.*app.domain.character* 反向依賴 app.mcp 或任何 OAuth module。


3. M04 共通硬原則

3.1 先實測再設計

M04-B 的任何 OAuth/protocol/catalog 設計決定都必須能指到 M04-A 的 preflight 記錄。沒有實測證據的猜測不得寫成契約。

3.2 OAuth 不得新增授權邊界

每個 request 仍走 P3-D:grant → Seat current binding → controller_epoch == generation → scope。OAuth token 只是找到 grant 的另一把鑰匙;找到之後的流程與 Bearer AI Join Token 完全相同,不得複製第二份授權邏輯。

3.3 Seat 不可在 OAuth 內建立、選擇或切換

見 2.2。這是不可違反的 invariant,M04-B 必須有自動測試證明 OAuth 流程對 campaign_seats 零寫入。

3.4 kit 與指引不含任何秘密

除 token 本身外,不得含 Room password、Owner Key、DM Key、其他 Seat 的 token、secret DC、DM Notes。GET /mcp/guide 無認證,內容必須是「任何人都可以知道」的。

3.5 雙語同步交付

M04-B 的 OAuth 授權頁、錯誤訊息,與 M04-C 的指引、briefing、kit、UI copy 都是 user-visible content,同一 Subphase 交付 zh-TWen

3.6 指引告訴 AI 怎麼用桌,不替 AI 決定怎麼跑團

守則可以給;劇本、世界觀、NPC 設定不得給,那是 P6。

3.7 Preflight 測試 server 不得污染 Adventure Table app,且自身有安全下限

M04-A 的極小 HTTPS server 是獨立程式,不 import app.*,不進 app 打包,不共用 DB;它的唯一目的是量測網頁版 chat 平台。它的管理 endpoint(動態加 tool、撤銷全部 token)只監聽 loopback,不經公開 tunnel;request 與 response 的 JSONL 記錄對 access token、refresh token、authorization code 與任何 secret 欄位做相同的遮罩


4. M04 Subphase 順序

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。


M04-A — Web Chat MCP Preflight

目標

用一個極小、獨立、對外 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 設計的實測記錄與架構結論。

Prerequisite

完成後必須為真

A.1 測試 server 存在且獨立

A.2 OAuth 一次成功

在目標平台(先 ChatGPT Plus)的網頁版上完成一次完整 authorization,記錄:平台呼叫了哪些 metadata endpoint、是否用 dynamic client registration、redirect URI 形態、PKCE 有無、要求的 scope、token endpoint 的 grant type、access token 與 refresh token 的使用方式。

A.3 Scan Tools 與 protocol 實錄

記錄平台在 Scan Tools 時送出的:MCP-Protocol-Version(或缺席)、第一個 JSON-RPC method(initializeserver/discover/直接 tools/list)、是否帶 Mcp-Session-Id、是否要求 SSE、_meta 內容。這決定 M04-B 是否需要 protocol 相容層。

A.4 一個 read tool 與一個 write tool 各成功一次

在平台對話中觸發 get_contextpost_note,兩者都收到正確結果;記錄平台對 write tool 的確認 UX(是否每次要使用者按確認、能否標記為免確認)。若 write 在該方案上被拒,記錄平台訊息並依階梯進下一個平台。

A.5 Tool cache/refresh/re-auth 行為

依序執行並記錄每一步平台看到的 tool 清單:

  1. dm authorize → Scan Tools → 清單應含 dm_only_ping
  2. Server 端新增一個 tool(不重新 authorize)→ 平台是否看到?需要什麼操作才看到?
  3. 重新 authorize 為 player → 平台是否重新 scan?dm_only_ping 是否消失?需要 Refresh/重新發佈/新 connector 嗎?
  4. 撤銷 access token(server 端)→ 平台下一次呼叫的行為:自動 refresh、要求重新 authorize、或直接錯誤。
  5. 兩個不同對話同時使用同一 connector → 是否共用同一個 access token。

A.6 Long-poll 容忍度

wait_seconds 分別等 10、30、60 秒,記錄平台端的 timeout 上限。這決定 M04-B 對 wait_for_eventtimeout 上限是否要下修。

A.7 Preflight 記錄與架構結論

docs/M04/M04-A_PREFLIGHT.md 含:每個嘗試過的平台各一節(A.2~A.6 每項的記錄、日期、方案與可辨識版本、入口形態(不含憑證)、遮罩後的關鍵 request/response 樣本),目標平台判定(ChatGPT Plus/Claude chat 個人方案/Platform Blocked),以及對 M04-B 的明確結論:

A.8 順帶量測(不列驗收)

若當天方便,同一測試 server 對 Claude Desktop 也量一次 A.3,記錄即可,不影響 M04-A 關門。Claude chat 個人方案若已作為第二階正式量測,則不屬本項。

本 Subphase 不要求


M04-B — Adventure Table Web Chat Integration

目標

依 M04-A 實測契約與目標平台判定,讓目標平台的網頁版 chat 以 OAuth 取得既有 Seat/grant 的控制權並進桌;完成真實 DM 與 Player 兩場迴圈。M04-A 判定 Platform Blocked 時本 Subphase 延後。

完成後必須為真

B.1 OAuth 入口存在於 web channel

B.2 OAuth token 只對應既有 grant

B.3 Seat 不可在 OAuth 內建立、選擇或切換

B.4 Protocol 相容層(僅在 M04-A 證明必要時)

B.5 Tool catalog 呈現依 M04-A 結論

依 2.3 表格落點實作;若落在第三列,使用者拍板 union catalog 或 per-role URL 後才開工。不論哪種,tools/call 階段的 role 檢查是唯一真值,並有測試證明 Player token 呼叫 DM tool 被拒且零副作用。

B.6 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。

B.7 對外入口文件

README 部署段新增「讓網頁版 chat 連進來」:HTTPS 入口形態、只轉發 /mcp* 與 OAuth 路徑、不把 Room UI 露出公網;不含憑證。

B.8 真實目標平台 DM 與 Player gate

以只拿到 token 的目標平台網頁版 AI:

兩場都是 M04 closeout 必要條件。

本 Subphase 不要求


M04-C — AI Join Kit, Server-hosted Guide & Other Client Compatibility

目標

把「發 token」升級成「發 kit」,讓指引住 server、與 tool 契約同源;kit 依 M04-B 定案的入口形態產生。順帶記錄非目標平台網頁版 chat/Codex CLI/Codex Desktop 的相容狀態,但不作為 gate。

完成後必須為真

C.1 GET /mcp/guide 存在且無需認證

C.2 指引內容涵蓋「從零到迴圈」

至少包含:

  1. 三種接入方式各一節:目標平台網頁版 chat(connector + OAuth 貼 token,含 M04-A 實測出的 Refresh 注意事項)、有 MCP client 的 AI(Bearer)、有 shell 或可對外連網 code execution 的 AI(POST /mcp header 契約 + 最小 client 範例)。
  2. 最小 client 範例段開頭明講:只給有 shell 或可對外連網 code execution 的 AI;網頁版 chat 的 sandbox 沒有對外網路,不要嘗試,改走 connector。
  3. POST /mcp 的 header 契約、body _meta、不需 initialize、不得帶 Mcp-Session-Id(若 M04-B 加了相容層,另註明 legacy 路徑)。
  4. 一個完整的 tools/call 範例與成功/業務錯誤/協定錯誤三種回應。
  5. 工具表:名稱、用途、必要參數、適用 role,由 tool 定義同源產生。
  6. 進場流程:get_session_contextpre_sessionstart_sessionactive_session 就續場 → cursor 規則 → wait_for_event 迴圈。等待規則:每次 wait_for_event 用上限 timeout: 120;逾時(空回傳)就直接再等,連續最多 5 次(約 10 分鐘);5 次都沒事件就停下來,用一句話告知人類「桌上沒有動靜,需要時叫我」,不再自行呼叫;收到事件並回應後計數歸零。
  7. DM 守則與 Player 守則各一節。Player 守則必須正確反映 Player scope:Player 沒有 request_check;要做 check 時用 post_action 描述意圖,等 DM 建立 Check,再以 roll_pendingsubmit_physical_roll 完成。
  8. 401 ai_token_unauthorized 的處置:停止並告知使用者,不重試。

不得含 3.4 的任何秘密,不得含 3.6 禁止的劇本內容。

C.3 tool description 加厚

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_sessionpre_session_only,兩者零副作用;P3 實作規格「未綁定時只能執行最小 pre-session/Start 能力」仍成立,改變的只是列表。每個 description 必須寫明該工具何時被接受(Session 前後皆可/只在開始前/需要 active Session)。

C.4 get_session_context 回傳 briefing

active-session briefing 是強制流程,不是描述(2026-09-12 修訂)。 第一次真實跑桌暴露:AI 只讀 briefing、不會去讀完整 guide,所以核心跑團流程必須直接寫進 briefing。active briefing 改成逐步「必跑流程」:DM=讀 stage→stage_unset 為真先 set_stage_textpost_narration(100–250 字,秘密不進 Stage,暗骰 dm_only)→立即 wait_for_event(不得停在 host chat 等提示)→收到事件即處理,需檢定用 request_check→場景實質變化再 set_stage_text→再 wait_for_event 循環→只有連續 5 次無事件、Session 結束或 host 喊停才停;Player 對應用 post_dialoguepost_actionroll_pending。pre-session briefing 明講「此 token 已使你成為該 DM Seat controller,無入席步驟」與「開場後 Stage 為空,第一步 set_stage_text」。

briefing 帶 MCP 呼叫判定規則。 connector 把「工具探索」與「工具執行」分開,模型會把「看得到工具/重新掃描」誤當成「已呼叫」。briefing(雙語)加硬規則:只有本回合實際執行工具並取得結果才算呼叫 MCP;查看清單、讀 schema、重新掃描都不算,且連線成功/失敗不得據此推測,沒有實際 invocation 只能說「尚未測試」。get_session_context 的 tool description 同步加一句同義規則。

C.4a stage_unset / next_required_action(機器可讀提示)

C.5 server/discover.instructions 指向指引

C.6 Lobby 與 Session 頁提供 kit

C.7 雙語

C.1~C.6 所有 user-visible 文字同時交付 zh-TWen

C.8 真實 AI 只憑 kit 能進桌

兩條路徑各至少一次,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

C.9 其他 client 相容記錄(非 gate)

對非目標平台的網頁版 chat、Codex CLI、Codex Desktop 各嘗試一次 M04-B 的入口,記錄能不能連、卡在哪;寫入 closeout「Compatibility」段。任何一個不通都不阻塞 M04-C 關門。

2026-09-12 修訂(使用者拍板):延後,M04-C closeout 不含「Compatibility」表。

本 Subphase 不要求