資料來源#
- An open-source spec for Codex orchestration: Symphony.
- Codex App Server
- MCP Specification Changelog — 2026-07-28
摘要#
這是一種透過 stdio 傳輸、以行分隔的類 JSON-RPC 協定,讓外部協調器能以程式方式驅動 Codex 程式設計代理程式工作階段。文件見於 developers.openai.com/codex/app-server,而 Symphony 的 SPEC.md 則詳細示範了它的用法。協調器會啟動 codex app-server(預設命令)、交換啟動握手,接著持續接收回合事件,直到回合結束。多個 turn/start 請求會重用同一個 thread_id 以延續工作;動態工具呼叫則讓協調器能注入自訂工具(例如 linear_graphql),而不必將憑證暴露給子代理程式容器。
詳細說明#
為什麼要用協定,而不是 CLI#
Symphony 直接指出這項限制:透過 CLI 或 tmux 工作階段驅動 Codex,無法擴展以支援程式化協調。App Server 是「Codex 內建的無頭模式」,提供 JSON-RPC API,可用來啟動執行緒或回應回合等操作。協調器可取得:
- 程式化控制執行緒生命週期,無須擷取終端機輸出。
- 可注入自訂工具實作的掛鉤(動態工具呼叫)。
- 可用於觀測、重試邏輯與停滯偵測的結構化事件。
啟動契約#
Symphony 規格中的子程序啟動參數:
- 命令:
codex.command(預設:codex app-server) - 呼叫方式:
bash -lc <command> - 工作目錄:每個議題各自的工作區路徑(驗證為安全不變條件——啟動前
cwd == workspace_path) - Stdout/stderr:分開的串流。只有 stdout 是協定串流。 Stderr 僅供診斷使用——不可將其解析為 JSON。
- 訊框格式:以行分隔的 JSON,每行一則訊息。
- 建議的最大行長度:10 MB。
啟動握手#
以下為依 Symphony 說明性逐字稿改寫的必要訊息順序:
{"id":1,"method":"initialize","params":{"clientInfo":{"name":"symphony","version":"1.0"},"capabilities":{}}}
{"method":"initialized","params":{}}
{"id":2,"method":"thread/start","params":{"approvalPolicy":"...","sandbox":"...","cwd":"/abs/workspace"}}
{"id":3,"method":"turn/start","params":{"threadId":"<thread-id>","input":[{"type":"text","text":"<rendered prompt>"}],"cwd":"/abs/workspace","title":"ABC-123: Example","approvalPolicy":"...","sandboxPolicy":{"type":"..."}}}說明:
initialize請求——包含clientInfo和capabilities。如果指定的 Codex 版本要求為動態工具協商能力,請在此處宣告。initialized通知——在收到initialize回應後傳送。thread/start——建立執行緒脈絡,包含核准政策、沙箱模式與 cwd。可選擇在此宣告用戶端工具規格(例如linear_graphql)。turn/start——第一個回合會帶上完整呈現後的提示;後續延續回合只傳送延續指引。
工作階段識別碼組成:
thread/start結果中的thread_id(result.thread.id)- 每個
turn/start結果中的turn_id(result.turn.id) - 產生的
session_id=<thread_id>-<turn_id>
延續回合(重用執行緒)#
一項重要但不太直觀的特性:延續回合會在同一個執行中的子程序上,以新的 turn/start 請求重用相同的 thread_id。第一個回合傳送完整呈現的任務提示;後續回合只傳送延續指引,因為原始提示已留在執行緒歷史中。
Symphony 的工作者邏輯:
- 每個回合成功後,重新檢查追蹤器狀態。
- 如果議題仍在進行,就在同一個
threadId上再發出一個turn/start,最多達到agent.max_turns(預設 20)。 - 子程序會在延續回合期間保持執行——只有工作者執行結束時才停止。
這項基本機制讓以工單驅動的多回合工作切實可行:代理程式能在外部看似獨立的延續回合之間維持對話狀態。
串流回合事件#
單一回合的終止條件:
| 條件 | 結果 |
|---|---|
turn/completed | 成功 |
turn/failed | 失敗 |
turn/cancelled | 失敗 |
回合逾時(turn_timeout_ms,預設 1h) | 失敗 |
| 子程序退出 | 失敗 |
Symphony 規格列出的重要事件:
session_started、startup_failed、turn_completed、turn_failed、turn_cancelled、turn_ended_with_error、turn_input_required、approval_auto_approved、unsupported_tool_call、notification、other_message、malformed。
三種獨立逾時#
| 逾時 | 預設值 | 範圍 |
|---|---|---|
read_timeout_ms | 5 s | 啟動期間與同步請求中的請求/回應 |
turn_timeout_ms | 1 h | 回合串流的總時長 |
stall_timeout_ms | 5 m | 事件之間的不活動時間(由協調器強制執行) |
將 stall_timeout_ms <= 0 設為停用停滯偵測。
核准、沙箱與使用者輸入#
實作可自行決定處理方式,但規格要求核准請求和需要使用者輸入的事件不得讓執行無限期停滯。實作可選擇:
- 自動核准(高信任模式,例如在工作階段中自動核准命令執行與檔案變更)。
- 呈交操作人員處理(互動流程)。
- 依政策自動解決。
- 直接判定執行失敗(Symphony 參考實作會讓需要使用者輸入的回合失敗)。
對無人值守的協調而言,將需要使用者輸入視為硬性失敗是合理的——沒有真人能在迴圈中滿足該請求。
動態工具呼叫(最被低估的功能)#
這是實驗性功能——而根據下方廠商參考資料(2026-09-07),目前仍屬實驗性:thread/start 上的 dynamicTools 需要 capabilities.experimentalApi = true。代理程式可對協調器在 thread/start 宣告的工具提出 item/tool/call 請求。若工具不受辨識,請回傳工具失敗回應——工作階段會繼續,不會停滯。
其優勢在於:由協調器實作的工具可以包裝子代理程式絕不應接觸的憑證。
Symphony 的 linear_graphql 工具是典型例子:
- 子代理程式容器不會取得 Linear 存取權杖。
- 協調器會宣告一個
linear_graphql工具,代理經過驗證的 GraphQL 查詢。 - 子代理程式以
{ "query": "...", "variables": {...} }呼叫該工具;協調器則使用自己的驗證資訊,對 Linear 端點執行查詢。
linear_graphql 的契約:
- 每次呼叫只能有一個非空的 GraphQL 操作(多個操作會遭拒)。
variables物件為選填。- 結果語義:
- 傳輸成功且沒有 GraphQL
errors→success=true - 頂層 GraphQL
errors→success=false,但保留回應本文 - 輸入無效/缺少驗證資訊/傳輸失敗 →
success=false並附上錯誤內容
這在架構上與 MCP 平行,但它位於程式設計代理程式執行環境內,而非獨立程序中——協調器會決定每個工作階段要注入哪些工具。
兩種協定在狀態性上已分道揚鑣(2026-07-28)#
App Server vs MCP, and the Claude-Side Equivalent: Three Boundaries for Driving Agents 根據觀察到的分工提出工具層/工作階段層的區隔;如今這已成為 MCP 自己明確表明的立場。2026-07-28 修訂版(MCP Specification Changelog — 2026-07-28,vendor-claim;紀錄見 MCP and Computer Use)移除了協定層級的工作階段與 Streamable HTTP 的 Mcp-Session-Id 標頭,也徹底刪除了 initialize/notifications/initialized 握手,改以 _meta 中的逐請求版本協商取代,並將跨呼叫狀態移出協定,改由伺服器產生的控制代碼以一般工具引數傳遞。
App Server 保留並以此為基礎建構的,正是上述每一項:initialize/initialized 握手、在多次 turn/start 呼叫間重用的 thread_id、<thread_id>-<turn_id> 工作階段識別碼,以及串流回合生命週期。因此比較的結論更明確,而非有所轉變——MCP 遠離了工作階段語義,而 App Server 的全部核心正是工作階段語義。另一個方向也有兩項較小的趨同值得一提:MCP 新增的 MRTR 模式(伺服器回傳 InputRequiredResult,用戶端再以 inputResponses 重試)在結構上與此協定的 turn_input_required 事件相同;MCP 在逐請求版本不符時使用的 UnsupportedProtocolVersionError,則是對同一問題採取更嚴格的回應,而 Symphony 下方的相容性設定檔是透過容忍欄位名稱差異來解決此問題。
相容性設定檔#
Symphony 的規格對版本容忍度說明得格外明確:
「規範性契約在於訊息順序、必要行為,以及必須擷取的邏輯欄位。相容的 app-server 版本之間,確切的 JSON 欄位名稱可能略有差異。只要承載相同的邏輯意義,實作就應容忍等效的負載結構。」
實際上的含意:不要與特定 JSON 欄位名稱緊密綁定。規格鼓勵實作者透過 codex app-server generate-json-schema --out <dir> 檢查已安裝的 Codex 結構描述,並將 Codex 管理的設定(approval_policy、thread_sandbox、turn_sandbox_policy)視為直接傳遞的值。
廠商參考資料所確認的內容(2026-09-07 快照)#
以上內容皆整理自 Symphony 規格,該規格從用戶端角度解讀協定。Codex App Server(vendor-claim;OpenAI 持續更新的參考文件,快照日期為 2026-09-07——developers.openai.com/codex/app-server 網址目前會以 308 重新導向至 learn.chatgpt.com/docs/app-server)則從伺服器端出發,並解答了本頁四月提出的三個問題:
- 版本依二進位檔而定,而且沒有登錄庫。「你可以從 CLI 產生 TypeScript 結構描述或 JSON Schema 套件。每份輸出都專屬於你執行的 Codex 版本,因此產生的成品會與該版本完全相符」(
generate-ts、generate-json-schema)。沒有協定版本欄位,也沒有已發布的結構描述套件;開源實作openai/codex(codex-rs/app-server)就是參考依據。因此 Symphony 的相容性設定檔——容忍等效的負載結構、匯出已安裝的結構描述——不是權宜做法,而是預期的契約;與前述 MCP 逐請求_meta版本協商的差異,則是設計上的不同,不是文件缺漏。 - **「實驗性」是能力閘門,而非保留說明。**在
initialize中設定capabilities.experimentalApi = true,即表示用戶端選擇啟用所有實驗性方法與欄位;若未設定,伺服器會以<descriptor> requires experimentalApi capability拒絕這些請求。thread/start上的dynamicTools就是其中一個欄位;同一道閘門也涵蓋process/*、thread/turns/list、thread/items/list、背景終端機控制、environment/info、協作模式、tool/requestUserInput,以及parentThreadId/ancestorThreadId執行緒篩選條件。對 Symphony 的憑證代理模式而言,有兩點很重要:動態工具會持續儲存在執行緒 rollout 中繼資料,並在thread/resume時還原,除非用戶端提供新的工具;其呼叫則會以標準dynamicToolCall項目呈現({id, tool, arguments, status, contentItems?, success?, durationMs?})——項目屬於穩定介面,宣告則位於閘門之後。沒有公布任何發展路線圖或正式推出日期;experimentalFeature/list會列出「附有生命週期階段中繼資料」的功能旗標,是唯一可由機器讀取的穩定性訊號。 - 回合支援多模態。
turn/start的輸入項目可為text、image({url})和localImage({path});userMessage項目也帶有相同三種類型;model/list會回報各模型的inputModalities(若未提供,則假設為["text", "image"]);代理程式呼叫自己的圖片檢視器時,會產生imageView項目。先前「以文字為主」的解讀來自 Symphony 規格,其協調器不會傳送圖片。
此外還發現一些超出原先問題範圍的資訊:stdio 以外的傳輸方式(WebSocket,屬於「實驗性且不受支援」;透過 HTTP Upgrade 握手使用 Unix socket;off);使用 toolOutput 與空 input 的 turn/start,可將用戶端執行的工具結果作為 functionCallOutput 項目送入執行緒;標記為 required 的 MCP 伺服器若初始化失敗,如今會導致 thread/start/thread/resume 失敗,而不會在缺少該伺服器的情況下繼續;若恢復時使用的模型與 rollout 記錄的模型不同,則會發出警告,並在下一個回合附上一則一次性的模型切換指示。
建議的錯誤類別#
統一不同實作時可使用:
codex_not_found、invalid_workspace_cwd、response_timeout、turn_timeout、port_exit、response_error、turn_failed、turn_cancelled、turn_input_required。
Token 計數的細微處#
值得特別提醒,因為很容易弄錯。代理程式事件可能會以多種形式包含 token 計數:
- 優先採用執行緒絕對總量(例如
thread/tokenUsage/updated,或 token 計數包裝資料中的total_token_usage)。 - 忽略差值型負載(例如
last_token_usage)以供儀表板使用,否則會重複計數。 - 除非事件類型明確定義,否則不要將一般
usage對照表視為累計值。 - 對絕對總量,追蹤相較於上次回報總量的差值。
延伸閱讀#
- Symphony — 以此協定建構的典型協調器;實體頁面收錄生命週期與操作細節
- Ticket-Driven Agent Orchestration — 延續回合讓每張工單執行多回合工作成為可能;一張工單 → 一個執行緒 → 多個回合
- Agent Harness Engineering — App Server 協定是實現「harness 即服務」的整合邊界——協調器可驅動工作階段生命週期,而無須擷取 CLI 輸出
- Claude Code Best Practices — Claude 的
claude -p非互動模式與 Claude Agent SDK 是平行生態系;兩者都能讓外部協調器驅動工作階段,但 Codex 的 App Server 對穩定 JSON-RPC 協定有更明確的定義 - Client-Side Agent Optimization —
agent.max_turns、turn_timeout_ms、stall_timeout_ms和動態工具呼叫成本(代理往返與直接呼叫的差異)是 AgentOpt 所形式化之預算控制手段的實際案例
尚待解答#
- 在採用 Symphony 風格的協調器,並以此代理憑證之前,
dynamicTools會離開experimentalApi閘門嗎?觸發條件:該欄位從 app-server 參考文件的實驗性功能清單中消失,或experimentalFeature/list將其列為穩定生命週期階段。
已解答的問題#
-
是否有公開的結構描述登錄庫,讓外部協調器無須使用
generate-json-schema就能以特定 App Server 版本為目標?**已解答(2026-09-07):**沒有——Codex App Server 只提供依二進位檔產生的結構描述匯出(「每份輸出都專屬於你執行的 Codex 版本,因此產生的成品會與該版本完全相符」),沒有協定版本欄位,也沒有已發布的套件;openai/codex原始碼樹就是參考依據。Symphony 容忍等效結構的設定檔是預期的用戶端處理方式,而非權宜之計。 -
「動態工具呼叫(實驗性)」這項保留說明——穩定性的發展路線圖為何?Symphony 的安全模型仰賴這項功能。**已解答(2026-09-07):**沒有已公布的發展路線圖;這項機制受能力閘門控制。
thread/start上的dynamicTools需要capabilities.experimentalApi = true,與涵蓋process/*、回合/項目清單和背景終端機控制的選擇性啟用相同;已宣告工具會持續保存在 rollout 中,並在thread/resume後保留;呼叫會以穩定的dynamicToolCall項目呈現;experimentalFeature/list則會提供各旗標的生命週期階段中繼資料。其正式推出時間目前列為上方的#oq/wait(Codex App Server)。 -
此協定處理多模態回合(圖片輸入、螢幕截圖附件)的能力如何?規格著重於文字。**已解答(2026-09-07):**原生支援——
turn/start接受text、image(URL)和localImage(路徑)輸入項目;userMessage項目承載這三種類型;model/list會公告各模型的inputModalities,並以["text", "image"]作為向下相容的預設值(Codex App Server)。對純文字的印象來自 Symphony,因為其協調器不會傳送圖片。 -
App Server 協定與 MCP 的詳細比較如何?兩者都會向模型提供工具,但 App Server 位於 Codex 執行環境內部,而 MCP 位於外部。各自在什麼時候更適合?已解答:App Server vs MCP, and the Claude-Side Equivalent: Three Boundaries for Driving Agents——兩者位於不同層面,通常是互補而非競爭:MCP 是模型↔世界的工具層(撰寫一次、各個介面皆可使用、由供應商執行);App Server 是協調器↔執行環境的工作階段層(執行緒生命週期、回合、事件、逾時、token 計數——這些都不屬於 MCP 範圍)。兩者唯一重疊之處是動態工具呼叫;原則是:可重複用於不同介面的第三方功能使用 MCP;工作階段專用且涉及敏感憑證的功能使用協調器注入工具(
linear_graphql權杖隔離模式也能將中毒中繼資料/暗中替換攻擊面縮減至第一方程式碼)——代價是實驗性穩定程度,以及無法在生態系中重複利用。 -
Claude 端是否有類似協定,還是 Claude 的對應方案只有 Agent SDK 加上工具使用 API?比較兩者有助於釐清何時「驅動既有 CLI」勝過「以 SDK 建構」。已解答:App Server vs MCP, and the Claude-Side Equivalent: Three Boundaries for Driving Agents——沒有文件記載的 Claude 端協定;其提供的兩端方案分別涵蓋 App Server 的定位:
claude -p(驅動產品,承接完整 harness——包含無人值守時中止而不掛起的權限管理、技能、脈絡檔案、MCP 連線——但提供文字而非結構化事件)以及 Agent SDK(在原始執行環境上打造不同產品——例如 Claude Design 的週末原型)。原則:當產品的 harness 就是價值所在,而且協調工作以批次/扇出形式進行時,驅動 CLI;當代理程式本身是具有獨立介面與工具的另一種產品時,以 SDK 建構。Symphony 自身從 tmux 演進至協定,標示出中間層(在產品 harness 之上提供結構化工作階段控制);Claude 端目前只能從任一端近似實現——這層最終會標準化,還是 harness 縮減會使其不再必要,仍值得觀察。
衍生內容#
- App Server vs MCP, and the Claude-Side Equivalent: Three Boundaries for Driving Agents — 根據本頁兩項協定問題整理出的三邊界比較
資料來源#
- An open-source spec for Codex orchestration: Symphony. — 第 10 節(Agent Runner Protocol)和第 13.5 節(Token Accounting)是權威參考資料
- Codex App Server — OpenAI,Codex App Server 參考文件(
vendor-claim)。持續更新的頁面,快照日期為 2026-09-07,依設計published:欄位留白;透過learn.chatgpt.com/docs/app-server的.md端點擷取,本頁引用的developers.openai.com/codex/app-server網址目前會重新導向至該處。用於傳輸方式、依版本產生的結構描述匯出、experimentalApi閘門及其涵蓋範圍、dynamicTools持續保存與dynamicToolCall項目、turn/start輸入項目與inputModalities、toolOutput,以及requiredMCP 伺服器與模型切換行為。完整方法清單(執行緒、回合、審查、程序、檔案系統、核准、徵詢)收錄於原始資料中,未在此整理超出問題所需的部分 - MCP Specification Changelog — 2026-07-28 — Model Context Protocol 專案,規格修訂版 2026-07-28 的 Key Changes(
vendor-claim)。重大變更 1–3(移除工作階段與Mcp-Session-Id、刪除握手、逐請求_meta版本協商、server/discover)以及 7–8(MRTR、resultType),僅用於上述協定比較
Cited by 13
- App Server vs MCP, and the Claude-Side Equivalent: Three Boundaries for Driving Agents×7
Dynamic tool calls · The capability is session-scoped and credential-sensitive · The linear_graphql…
- The Future of Agent Interfaces×4
Codex App Server Protocol looks MCP-like because it exposes tool calls, but it lives at a different…
- Symphony×4
Symphony uses Codex's headless mode (the App Server) rather than driving the CLI. The full protocol…
- Agent Control Plane Patterns: Tickets, Loops, Specs, and Memory Files×3
The control plane is not just files. It also needs a runtime integration boundary. Codex App Server…
- Agent Harness Engineering×3
Symphony (OpenAI, March 2026) — long-running daemon polling Linear, per-issue workspace, Codex App…
- Codex×3
The headless protocol. The App Server Protocol (JSON-RPC over stdio) drives Codex non-interactively…
- Claude Code Best Practices×2
Non-interactive / programmatic · claude -p, Claude Agent SDK · hermes CLI in scripts · Codex App…
- OpenAI×2
Agent tooling and orchestration. OpenAI ships Codex and the surrounding harness layer: the Codex…
- Client-Side Agent Optimization
Codex App Server Protocol — agent.max_turns, turn/stall timeouts, and dynamic-tool-call cost are…
- MCP and Computer Use
Codex App Server Protocol — the comparable protocol, now diverged on statefulness: the App Server…
- Agent Systems & Harness Engineering
Codex App Server Protocol — JSON-RPC stdio protocol for headless Codex sessions:…
- Open Questions Backlog
Codex App Server Protocol: Does dynamicTools leave the experimentalApi gate before Symphony-style…
- Ticket-Driven Agent Orchestration
Codex App Server Protocol — the runtime protocol Symphony uses for per-ticket sessions;…
Related articles
- Claude Code Best Practices
Anthropic's guide to effective Claude Code usage: context management, verification-driven development, explore→plan→cod…
- Symphony
OpenAI's open-source agent orchestrator (March 2026): turns Linear into a control plane for Codex, per-issue workspace,…
- Agent Harness Engineering
Patterns for scaffolding long-running LLM agents: environment design, progressive context disclosure, mechanical archit…
- Hermes Agent
Nous Research's CLI agent + Gateway daemon (Telegram/Discord/Slack/WhatsApp); AGENTS.md/SOUL.md context split, bounded…
- LLM-as-Compiler Knowledge Base
Karpathy's architecture: LLM incrementally compiles raw docs into a persistent interlinked wiki, replacing RAG with a 4…
