Codex Control Plane MCP API contract
June 20, 2026 ยท View on GitHub
Contract version: 1.
MCP clients should call codex_health_summary on startup or reconnect and
verify:
version.serverName == "codex-control-plane-mcp"version.contractVersion == "1"version.toolSurfaceHashis present and stable for the installed buildversion.guideHashis present and matches the agent guide returned by discovery- required stable tools are present in
version.stableTools
Agents do not need a separate client guide to discover the basic workflow.
tools/list returns codexMcpGuide, toolGroups,
recommendedStartupTool, and recommendedPrimaryWriteTool next to the normal
MCP tools[] array. If a client library drops those top-level fields, call
codex_get_agent_contract(detail="compact") to read the same guide.
MCP protocol
- Server entrypoint:
python -m codex_control_plane_mcp.server - Hook installer entrypoint:
codex-control-plane-mcp-hooks - Admin helper entrypoint:
codex-control-plane-mcp-admin - Legacy aliases:
openclaw-codex-mcp,openclaw-codex-mcp-hooks - Transport: MCP JSON-RPC over stdio
stdout: JSON-RPC frames only- Diagnostics/logs: file-only, default
logs/server.log - Domain/tool errors: returned from
tools/callwithresult.isError=true - JSON-RPC errors: reserved for protocol errors such as invalid methods or bad request params
Every tool declares outputSchema. Every tool result mirrors the same payload
in:
result.structuredContentresult.content[0].textas formatted JSON
Success:
{"ok": true}
Domain/tool error:
{
"ok": false,
"error": {
"code": "CODEX_ERROR_CODE",
"message": "Human readable message",
"details": {},
"retryable": false
}
}
Stable orchestration tools
These tools are the supported surface for long-running Codex orchestration:
codex_submit_taskcodex_get_operation_statuscodex_start_plan_workflowcodex_start_review_workflowcodex_get_workflow_statuscodex_adopt_workflow_plancodex_approve_plancodex_list_pending_interactionscodex_answer_pending_interactioncodex_interrupt_turncodex_archive_threadcodex_unarchive_threadcodex_start_thread_compactioncodex_get_thread_compaction_statuscodex_get_worker_statuscodex_get_queue_statuscodex_get_concurrency_statuscodex_get_worker_command_statuscodex_get_runtime_capabilitiescodex_preflight_project_runcodex_health_summarycodex_get_agent_contractcodex_collect_diagnosticscodex_repair_issue
Stable tools are asynchronous or pollable when they can trigger long work. New
fields may be added, but existing input fields and machine-readable status/error
fields must not be removed without changing contractVersion.
Public status boundary
All public tool responses are returned in agent_safe mode unless the tool is
the explicit raw audit endpoint. Public status payloads do not expose raw
operation requests, full prompts, full review instructions, full thread titles,
private local paths, exact token counts, account identifiers, or raw command
output.
Operation and workflow status use requestSummary instead of raw request.
requestSummary may include:
- operation type, thread/project/workflow refs, runtime policy, scheduling intent, input item state, output schema hash, and resource keys;
- text summaries with character count and hash.
requestSummary does not include prompt or instruction previews. Clients that
need to correlate a prompt should compare their own stored request with the
hash and operation id. Do not build orchestration logic from raw request text.
Thread titles and lifecycle thread state are returned as titleSummary with
count and hash. Runtime and turn token usage is coarse. Raw audit data is only
available through codex_get_diagnostic_logs(include_payload=true), and that
payload is still secret-redacted.
Write policy defaults
Server-level default write policy is configured by environment/config:
CODEX_MCP_DEFAULT_SANDBOX, defaultread-onlyCODEX_MCP_DEFAULT_APPROVAL_POLICY, defaulton-request- JSON config fields
default_sandbox_policyanddefault_approval_policy
Explicit tool arguments always win over server defaults. A client may submit one task with a stricter or more permissive policy without changing the server configuration.
Plan Mode has a sandbox floor. When codex_start_plan_workflow or
codex_submit_task with collaboration_mode="plan" resolves to read-only,
MCP sends workspace-write to app-server instead. Status output includes
requestedSandbox, effectiveSandbox, runtimePolicyAdjusted, and
runtimePolicy so clients can audit the adjustment. Non-plan write operations
keep normal sandbox semantics.
Execution modes and central worker
CODEX_MCP_EXECUTION_MODE controls whether an MCP process may execute queued
operations:
inline: default. The stdio server can submit, poll, and execute operations.client: submit/status/read surface only. It never picks up queued durable operations and delegates control actions to the worker command queue.worker: long-running scheduler. It owns app-server, leases, queue slots, and resource locks.observe: read-only heartbeat process for rollout checks. It never acquires leases.
In client mode, codex_submit_task creates a durable operation and returns a
fast ACK. codex_get_operation_status remains passive and does not call
_schedule_recoverable_operations.
The worker uses these limits:
CODEX_MCP_MAX_ACTIVE_TURNS_GLOBAL, default4CODEX_MCP_MAX_ACTIVE_TURNS_PER_PROJECT, default3CODEX_MCP_MAX_ACTIVE_TURNS_PER_AGENT, default3CODEX_MCP_MAX_ACTIVE_TURNS_PER_THREAD, default1CODEX_MCP_MAX_ACTIVE_WRITE_TURNS_PER_PROJECT, default1CODEX_MCP_MAX_APP_SERVER_PENDING_REQUESTS, default8
codex_submit_task accepts scheduling hints:
agent_id: stable orchestrator id such ascodex-dev.resource_keys: write scopes for parallel work in one project.priority:low,normal, orhigh.estimated_cost_class:light,normal, orheavy.
If a write turn uses workspace-write or danger-full-access without
resource_keys, the worker takes a broad project:<cwd>:write lock. With
disjoint resource_keys, write turns in one project may run in parallel.
Operation status adds:
queueStateworkerStateslotStateresourceLockStateoperationRowAgeSecondsturnFreshnessworkerFreshnessstalenessMeaning
Running turn operations have slotState.claimed=true and a slotClaim with
the worker id, slot type, count, and claim time. codex_get_queue_status
separates queued, runningOperations, auxiliaryOperations,
activeTurnSlots, and blockedByLocks; lifecycle commands and steer_turn
are not counted as active turn slots.
Queued operations use nextRecommendedAction="wait_for_worker_slot" when a
slot limit blocks scheduling. Write lock conflicts use
nextRecommendedAction="wait_for_resource_lock". Worker health problems use
nextRecommendedAction="inspect_worker_health".
If there are active running turns but no queued or blocked work,
codex_get_queue_status.nextRecommendedAction is none. Clients should not
interpret active slots alone as a reason to create a retry or wait for a queue
slot.
stalenessSeconds remains as a compatibility alias for operation row age. New
clients should read stalenessMeaning; a stale operation row is not enough to
declare a stalled turn without turnFreshness and workerFreshness evidence.
Worker control commands run on a separate command lane from turn scheduling.
codex_get_worker_command_status is a bounded SQLite read. By default it
returns a compact command envelope; request the full result only with
include_result=true and a bounded max_result_chars.
External live-test client
The repository includes scripts/external_mcp_client.py for local development
and release checks. It is not a public MCP tool. It is a standalone client that
talks to this server through the same MCP protocol used by OpenClaw and Hermes.
The daemon mode keeps one MCP stdio subprocess alive:
python .\scripts\external_mcp_client.py daemon-start
python .\scripts\external_mcp_client.py daemon-restart-mcp --reason after_code_change
python .\scripts\external_mcp_client.py run-live-test --scenario baseline --archive-report
daemon-restart-mcp restarts only the MCP subprocess owned by the external
client. It does not restart Codex Desktop and does not restart the central
worker. If a code change affects worker execution, restart the worker separately
before running live scenarios.
Live scenarios are regular MCP client flows:
baselinereaddurable-matrixparallel-stresssteer-interruptworkflow-reviewlifecyclediagnosticsfull
Findings are written to corrective_action_plan.md using redacted evidence:
operation ids, workflow ids, thread ids, action ids, compact status fields, and
short reproduction notes. The report must not include secrets, account ids,
tokens, raw payloads, or private paths outside the configured sandbox projects.
Durable operation types
codex_submit_task supports these operation types:
start_chat: create a Codex thread and start a turn.send_message: resume an existing thread and start a new turn.execute_plan: execute an approved Plan Mode workflow or existing chat plan.steer_turn: send extra text to an active turn through app-serverturn/steer.fork_thread: fork an existing thread through app-serverthread/fork.
codex_submit_task also accepts optional input_items for operations that
start a new turn: start_chat, send_message, execute_plan, and
fork_thread when message is present. Supported v1 items are:
{"type": "image", "url": "https://...", "detail": "auto|low|high|original"}{"type": "localImage", "path": "...", "detail": "auto|low|high|original"}
Remote image URLs must use http or https; data: and file: URLs are
rejected. Local image paths are resolved against the effective cwd when
relative, must point to an existing file under CODEX_ALLOWED_ROOTS, and must
use .png, .jpg, .jpeg, .webp, or .gif. Defaults allow 10 image items
and 20,000,000 bytes per local image. Deployments can override these with
CODEX_MCP_MAX_IMAGE_INPUT_ITEMS and CODEX_MCP_MAX_IMAGE_INPUT_BYTES.
Operation status includes inputItemState when image inputs were accepted.
The state contains counts, item types, detail, file extension, size, and hashes.
It does not include raw image bytes, raw URLs, or full local image paths. MCP
passes the raw URL/path only to codex-app-server for the live turn/start
request.
steer_turn requires thread_id, expected_turn_id, and message. It does
not create a new turn and does not participate in prompt duplicate detection.
After app-server accepts the steering input, the operation remains running
and follows the target turn until the turn reaches a terminal state.
For strict retry safety, pass client_request_id. Reusing the same
client_request_id returns the same steering operation and does not send a
second turn/steer request. Calls without client_request_id are treated as
new steering commands.
Status payloads for steer_turn include normal operation fields plus:
steerState.acceptedsteerState.targetThreadIdsteerState.targetTurnIdsteerState.clientUserMessageId
If the target turn is missing, MCP returns CODEX_TURN_NOT_FOUND. If the target
turn is terminal or belongs to another thread, MCP returns INVALID_ARGUMENT.
fork_thread requires source_thread_id. message is optional only for this
operation type. Without message, MCP completes the operation as soon as
app-server returns the forked thread id. With message, MCP starts the first
turn in the forked thread and the operation follows that turn to a terminal
state.
fork_thread does not participate in prompt duplicate detection because two
similar fork requests may be intentional. Pass client_request_id for strict
retry safety. Reusing the same client_request_id returns the same operation
and does not call thread/fork again. Calls without client_request_id create
new fork requests.
Inputs specific to fork_thread:
source_thread_id: source thread to fork from.message: optional first user message for the forked thread.cwd: optional working directory override, insideCODEX_ALLOWED_ROOTS.model: optional model override.approval_policy: optional approval policy for the fork and first turn.sandbox: optional sandbox mode for the fork and first turn.fork_config: optional object passed to app-server asconfig.ephemeral: defaultfalse.
Status payloads for fork_thread include normal operation fields plus:
forkState.acceptedforkState.sourceThreadIdforkState.forkedThreadIdforkState.hasInitialMessageforkState.cwdforkState.modelforkState.ephemeralforkState.turnId
After fork creation, top-level threadId means the forked thread id. For a
fork-only operation, nextRecommendedAction is read_forked_thread. For a
fork with an initial message, running status uses poll_turn_status.
Thread lifecycle management
Lifecycle tools call app-server maintenance methods for known threads. They do
not use codex_submit_task and do not create durable Codex turns.
Tools:
codex_archive_thread(thread_id, project_id=null, timeout_seconds=30, refresh_catalog=true)codex_unarchive_thread(thread_id, project_id=null, timeout_seconds=30, refresh_catalog=true)codex_start_thread_compaction(thread_id, project_id=null, timeout_seconds=30)codex_get_thread_compaction_status(action_id, include_events=false)
thread_id must be known through the catalog, tracked turns, or hook history.
Unknown threads return CODEX_THREAD_NOT_FOUND. If the thread has an active
turn or pending interaction, MCP returns CODEX_BUSY and does not call
app-server.
Archive and unarchive return completed lifecycle audit actions after app-server ACK. A successful call refreshes the catalog by default so read and search tools see the archived state sooner.
Compaction is pollable. codex_start_thread_compaction calls
thread/compact/start, stores a lightweight lifecycle action, and returns:
actionIdactionType="compact"threadIdstatus="running"threadStatenextRecommendedAction="poll_thread_compaction"recommendedPollAfterSecondspollRecommended=true
codex_get_thread_compaction_status returns running until MCP observes a
matching app-server thread/compacted event for the same thread. Completed
responses include observedEventId and targetTurnId. If the MCP-owned
app-server exits before that event is observed, the action becomes
unknown_after_app_server_exit with nextRecommendedAction="inspect_diagnostics".
Public thread/delete is intentionally not exposed in this contract. It is a
destructive action and needs a separate confirmation and threat model.
Plan workflows and recovery
codex_start_plan_workflow starts a durable Plan Mode workflow. Poll with
codex_get_workflow_status. The normal path is:
- Start planning with
codex_start_plan_workflow. - Poll until the workflow asks for plan review.
- Approve with
codex_approve_planonly after the plan is valid for the task. - Poll the same workflow until the final report is ready.
Plan Mode never starts as read-only. If the caller passes read-only, or the
server default is read-only, MCP raises the effective sandbox to
workspace-write. More permissive values are passed through. The workflow ack
and later status include:
requestedSandbox: sandbox requested by the caller or server default.effectiveSandbox: sandbox sent to app-server.runtimePolicyAdjusted:truewhen MCP raisedread-onlytoworkspace-write.runtimePolicy: compact policy block with approval policy and sandbox floor.
latestPlan includes quality fields:
planQuality:valid_plan,blocker,question,partial,needs_review, orunknown.quality: same value for compact clients.valid:trueonly when MCP has a trusted usable plan artifact.
For Plan Mode, clients must read the plan from latestPlan. MCP does not treat
intermediate assistant text as planOperation.finalReport. When a compact plan
summary is useful, planOperation.planArtifactSummary contains the plan item
id, turn id, hash, quality, and truncated text.
codex_approve_plan rejects blocker, question, partial, and unknown plan
artifacts. A fallback assistant message is not treated as a valid plan unless it
contains an explicit plan artifact such as <proposed_plan>...</proposed_plan>.
codex_get_workflow_status also returns workflowObservation for recovery:
officialPlanTurnId: turn currently attached to the workflow.officialPlanQuality: quality classification for that official plan.expectedExecutionTurnId: execution turn expected by the workflow, when known.latestThreadTurnId: latest turn known in the workflow thread.threadAdvancedAfterOfficialTurn: later turns exist in the same thread.recoverableCandidateFound: a later valid plan/report candidate was found.candidatePlans: candidate plans from the same thread withturnId,planHash,quality,planQuality, andmarkdown.candidateReports: future report candidates from the same observation pass.importStatus: transcript import status, when MCP refreshed tracking from a local transcript.
threadAdvancedAfterOfficialTurn is not set when the latest known turn is the
expected execution turn. It is only a drift warning for manual or untracked work
that happened after the official plan turn.
Workflow status also returns workflowOperationQueueState for the current
nested operation. If that operation is queued by a slot limit, top-level
nextRecommendedAction is wait_for_worker_slot. If it is queued by a resource
lock, the action is wait_for_resource_lock.
When nextRecommendedAction == "adopt_candidate_plan", the client should show
the candidate to a human or policy engine, then call:
{
"tool": "codex_adopt_workflow_plan",
"arguments": {
"workflow_id": "WORKFLOW_ID",
"candidate_turn_id": "CANDIDATE_TURN_ID",
"candidate_plan_hash": "CANDIDATE_PLAN_HASH",
"client_request_id": "CLIENT_RETRY_KEY"
}
}
codex_adopt_workflow_plan updates the workflow's official plan turn and latest
plan hash without starting execution. It is idempotent for the same workflow and
candidate hash. After adoption, poll codex_get_workflow_status again and use
the normal approve path.
When a workflow has already failed or become orphaned because of a bad runtime
policy, use codex_repair_issue with
action="retry_workflow_with_runtime_policy". The action defaults to
dry_run=true.
Dry run returns the planned request, runtime policy, and source workflow. With
dry_run=false, MCP creates a new workflow, links it to the old workflow, and
does not revive the old terminal turn. codex_get_workflow_status exposes the
link through workflowRetryState:
replacesWorkflowId: source workflow replaced by this workflow.replacedByWorkflowId: replacement workflow for the current workflow.retryOfWorkflowId: original workflow id for the retry.retryReason: optional operator/client reason.retryCreatedAt: retry creation time.
Diagnostics may report:
workflow_thread_drift: the thread has advanced after the official workflow turn.workflow_recoverable_candidate_found: a later valid candidate can be adopted.invalid_plan_artifact: the official plan is a blocker, question, partial artifact, or otherwise unsafe to approve automatically.
Code review workflows
codex_start_review_workflow starts a durable pollable code review through
app-server review/start. It is a workflow tool, not a public
codex_submit_task operation type. Internally MCP stores a review_start
operation so retry, restart recovery, diagnostics, and final report extraction
use the same durable state model as other long-running work.
Inputs:
thread_id: optional existing source thread. When present, default delivery isdetached.project_idorcwd: used when MCP must create a service source thread. When no existing thread is passed, default delivery isinline.target_type:uncommitted_changes,base_branch,commit, orcustom.base_branch: required fortarget_type="base_branch".commit_sha: required fortarget_type="commit".commit_title: optional title for commit review.instructions: required fortarget_type="custom".delivery: optionalinlineordetached.client_request_id: strict start idempotency key.model,sandbox, andapproval_policy: optional per-call overrides.
Target mapping to app-server:
uncommitted_changesmaps to{ "type": "uncommittedChanges" }.base_branchmaps to{ "type": "baseBranch", "branch": "..." }.commitmaps to{ "type": "commit", "sha": "...", "title": "..." }.custommaps to{ "type": "custom", "instructions": "..." }.
PR URLs and raw diffs are not separate v1 targets. Use a local checkout with
base_branch, or use custom instructions when the client has already prepared
the context.
Validation and safety:
- The source thread must be known through catalog, tracked turns, or hook
history, unless MCP creates a service thread from
project_idorcwd. cwdmust be insideCODEX_ALLOWED_ROOTS.- If the source thread has an active turn or pending interaction, MCP returns
CODEX_BUSYand does not call app-server. - Review workflows do not write files by themselves, but they run inside the selected Codex sandbox and approval policy.
codex_start_review_workflow returns a fast workflow ack with:
workflowIdworkflowKind="code_review"phasestatusreviewOperationIdcurrentOperationId- nullable
reviewSourceThreadId,reviewThreadId, andreviewTurnId reviewTargetreviewDeliverynextRecommendedAction
Poll with codex_get_workflow_status. Review workflow status includes:
reviewOperationreviewTurnreviewTargetreviewDeliveryfinalReportpendingInteractionscurrentOperationIdreviewOperationIdreviewSourceThreadIdreviewThreadIdreviewTurnId
Phases are queued, starting_thread, starting_review, reviewing,
completed, failed, and orphaned. Recommended actions are wait_review,
read_review_report, answer_pending_interaction, and inspect_diagnostics.
codex_get_operation_status for the internal review_start operation includes
reviewState:
reviewState.acceptedreviewState.sourceThreadIdreviewState.reviewThreadIdreviewState.reviewTurnIdreviewState.targetreviewState.deliveryreviewState.startAttempted
If MCP restarts after review/start was attempted but before the review turn id
is persisted, MCP does not start a second review blindly. It marks the operation
unknown_after_app_server_exit and the workflow moves to orphaned with
nextRecommendedAction="inspect_diagnostics".
When the review turn completes, MCP extracts the final assistant message into
finalReport. Valid JSON is exposed as structured report data. Plain text is
kept as readable finalReport.text with threadId, turnId, and
readFullVia.
Workflow thread goals
codex_start_plan_workflow can mirror an explicit high-level goal into the
Codex thread through app-server thread/goal/set.
Inputs:
goal: optional objective text. If omitted, MCP does not write a thread goal.goal_token_budget: optional positive integer passed astokenBudget.goal_completion_action:clear,set_complete, orleave. Defaultclear.goal_completion_objective: optional objective used withset_complete.
Goal sync starts after the workflow has a threadId, but normal workflow polling
is passive. codex_get_workflow_status does not call app-server goal methods
unless the caller passes refresh_live_goal=true.
When refresh_live_goal=true, MCP performs best-effort sync and returns:
threadGoal.configuredthreadGoal.managedthreadGoal.syncStatethreadGoal.completionActionthreadGoal.desiredObjectivethreadGoal.tokenBudgetthreadGoal.currentGoalthreadGoal.lastSyncedAtthreadGoal.clearedAtthreadGoal.lastErrorthreadGoal.availablethreadGoal.liveRefreshPerformed
Common syncState values:
not_configured: no explicitgoalwas supplied.pending_thread: goal is stored, but the workflow thread is not known yet.active: MCP set the app-server thread goal.cleared: MCP cleared its managed goal after completion.complete: MCP marked the managed goal complete after completion.left: MCP left the managed goal unchanged after completion.external_override: the current app-server goal no longer matches MCP's managed goal, so MCP skipped completion cleanup.unsupported: the local app-server does not support the goal method.error: app-server goal sync failed without failing the workflow.
MCP does not auto-generate goals from prompt or title. It redacts and truncates goal text in public status and workflow events.
Clients should keep frequent polling passive. Use refresh_live_goal=true only
for an explicit goal sync check or a repair flow.
Stalled turn supervision
MCP reports stalled turns in codex_health_summary.stallSupervisor without
calling app-server:
mode:diagnose_onlyorinterrupttimeoutSeconds: configured inactivity thresholdstalledTurnCountstalledTurnsautomaticInterruptEnablednextRecommendedAction
Public defaults are conservative:
CODEX_MCP_TURN_STALL_TIMEOUT_SECONDS=900CODEX_MCP_STALLED_TURN_ACTION=diagnose_only
diagnose_only never interrupts a turn by itself. Agents should collect
diagnostics and run repair actions with dry_run=true before retrying or
interrupting work.
Structured final reports
codex_submit_task, codex_approve_plan, and compatibility
codex_execute_plan accept optional output_schema.
The field must be a JSON object. MCP validates that it is serializable, non
empty, within the size limit, and compatible with Codex strict structured
outputs before it calls app-server. Object schemas must set
additionalProperties to false. Invalid schemas return INVALID_ARGUMENT
and do not start a Codex turn.
Supported operation types:
start_chat,send_message, andexecute_plan:output_schemais passed to app-serverturn/startasoutputSchema.fork_thread:output_schemais accepted only when the fork request also has an initialmessage.steer_turn:output_schemais rejected becauseturn/steerdoes not start a final-answer turn.codex_start_plan_workflow: planning turns do not acceptoutput_schema. Usecodex_approve_plan(..., output_schema={...})for execution output.
Status output does not echo the raw schema. It exposes:
outputSchemaState.providedoutputSchemaState.appliedoutputSchemaState.schemaHashoutputSchemaState.schemaCharsoutputSchemaState.parseStatusoutputSchemaState.structuredStatus
When a turn completes, MCP stores the final assistant message in the operation row. Workflow execution also copies the same report into the workflow row.
codex_get_operation_status and codex_get_workflow_status may return:
finalReport.text: readable final assistant text, truncated by the requested message budget.finalReport.summary: same compact text for clients that expect a summary field.finalReport.structured: parsed JSON object when the final message is valid JSON or contains a fencedjsonblock. Otherwisenull.finalReport.structuredStatus:parsedornot_available.finalReport.structuredParseStatus:valid_json,plain_text, orempty.finalReport.schemaHash: hash of the requestedoutput_schema, when provided.finalReport.threadId,finalReport.turnId, andfinalReport.readFullVia.
Plain text remains a valid final report. MCP does not extract hidden chain-of-thought, and final reports do not include raw tool payloads or command output.
Turn progress journal
codex_get_turn_status and codex_get_operation_status return compact progress
data for tracked app-server turns by default.
Inputs:
progress_events: number of recent progress events to return. Default10, max100. Use0to omit the progress block.progress_max_chars: max text returned for one progress event. Default2000.
Status payloads may include:
progressEventsprogressEventCountlatestProgressAttokenUsagemodelRerouteswarnings
tokenUsage in public status is coarse and redacted. It uses band fields such
as totalTokensBand, inputTokensBand, and outputTokensBand instead of exact
counts. Exact token counters are not part of the public status contract.
Supported progress sources:
item/agentMessage/deltaitem/plan/deltaitem/reasoning/summaryPartAddeditem/reasoning/summaryTextDeltathread/tokenUsage/updatedmodel/reroutedwarningconfigWarningguardianWarning
turn/diff/updated is stored as safe metadata only. MCP keeps diff size and
line counts, but not the unified diff text. The progress journal also avoids raw
tool payloads and command output by default. It records only app-server-visible
progress summaries and does not expose hidden chain-of-thought.
codex_collect_diagnostics includes the same data in progressJournal and adds
progress entries to timeline with source="turn_progress".
Runtime capabilities
codex_get_runtime_capabilities is a read-only inventory endpoint for MCP
clients that need to understand the local Codex runtime before starting work.
It may start the MCP-owned app-server if it is not already running.
Input fields:
refresh: defaultfalse. Whentrue, bypasses the in-memory cache.cwd: optional working directory used for permission profile, hooks, and skills resolution. It must be insideCODEX_ALLOWED_ROOTS.timeout_seconds: per-method timeout. Default2, max30.include_models: defaulttrue.include_hooks: defaulttrue.include_skills: defaulttrue.include_account: defaulttrue. Whenfalse, skips all account, usage, and rate-limit inventory calls.
The tool caches one snapshot for five minutes per cwd and include-flag set.
Inventory calls are best effort. A timeout or error in one app-server method
does not fail the whole tool. The response stays ok=true and reports the
method state in methodResults.
In client mode, the client process does not start a local app-server for live
inventory. The default response is passive and may include
runtimeCapabilities.workerRuntimeSnapshot plus cacheSource="worker_registry"
when a worker heartbeat is available. With refresh=true, MCP enqueues a
worker command and returns refreshCommandId; clients poll
codex_get_worker_command_status to read the command result.
Top-level result fields:
runtimeCapabilitiescacheStatemethodResultswarningsrefreshCommandId, only when a client-mode refresh command was queued.recommendedPollAfterSeconds=0pollRecommended=false
runtimeCapabilities includes:
status:ok,partial, orunavailable.cacheSource: source of the returned snapshot, such asworker_registry,worker_command, or local process cache.workerRuntimeSnapshot: compact worker registry data in client mode.appServer: process state plus redacted initialize metadata.schemaMethods: compact static method manifest with source, version, hash, method count, and method names.models:id,model,displayName,isDefault,hidden,inputModalities, reasoning effort fields, and service tier count.permissionProfiles:idanddescription.sandboxReadiness: Windows sandbox readiness status.hooks: counts grouped by cwd, event, source, trust, enabled state, and handler type. Raw hook commands and source paths are not returned.skills: counts grouped by cwd, scope, and enabled state. Skill names may be returned, but absolute paths are not returned.modelProviderCapabilities:webSearch,imageGeneration, andnamespaceTools.accountStatus:authenticated,requiresOpenaiAuth,accountType,planType,emailPresent, andidentityRedacted=true.accountUsage: availability, daily bucket count, and coarse bands for lifetime usage, peak daily usage, streaks, and longest turn duration.rateLimits: credit availability, unlimited flag, rate-limit reached state, safe used percentages, reset/window minutes, bucket count, and redacted bucket identities.
The account blocks never return raw email, account identifiers, credit balance,
spend-control limit or used, daily usage dates, daily bucket values, exact
usage counts, or raw rate-limit ids. Public bucket ids such as codex may be
shown; other bucket identities are represented by a short hash.
codex_health_summary.runtimeCapabilities contains only a compact subset from
the last collected runtime snapshot: status, cache age, model count, default
model, sandbox readiness, provider capabilities, account authentication state,
account and plan type, rate-limit reached state, credits availability, usage
availability, and warning count. Health summary does not collect inventory on
its own and does not include identity fields, balances, or exact usage values.
Project preflight
codex_preflight_project_run is a read-only guard for long-running work. It is
meant for clients that need a quick "can I start this run?" answer before
submitting a plan workflow, review workflow, or durable operation.
Inputs:
project_id: optional project id fromcodex_list_projects.cwd: optional project root. If bothproject_idandcwdare present,cwdmust match the allowed project path.model: optional model the client plans to use.sandbox: optional sandbox mode the client plans to use.approval_policy: optional approval policy the client plans to use.workflow_kind: optional hint such asplan_then_executeorcode_review.live_probe: defaultfalse. Whentrue, MCP starts a tiny safe Codex turn with markerMCP PREFLIGHT / DO NOT MODIFY FILES.timeout_seconds: short timeout for runtime inventory.
Result fields:
status:ready,degraded, orfailed.checks: machine-readable checks for path, allowed roots, Codex home, auth, hooks, runtime inventory, and optional live probe.runtimeCapabilities: compact runtime subset.probeOperation: durable operation ack whenlive_probe=true.nextRecommendedAction:start_run,inspect_warnings, orfix_environment.pollRecommended=false.
Preflight does not replace codex_get_runtime_capabilities; it combines the
parts OpenClaw needs before a concrete project run.
Agent guidance
Status, diagnostics, preflight, repair, and selected structured error responses may include these additive fields:
agentGuidanceagentGuidanceTextrecoveryAttemptState
agentGuidance.schemaVersion is agent-guidance/v1.
agentGuidance.problemState is one of:
waitrecoverableneeds_inputblockedfatalno_action
agentGuidance.instructions is the preferred automation contract for
OpenClaw/Hermes. Each instruction includes a kind, optional toolName,
redacted arguments, reason, expectedOutcome, risk, dryRunFirst,
requiresHuman, stopIf, and continueIf.
agentGuidance.loopGuard prevents recovery loops. The guard key is stable for
the same problem scope and action. Default limits:
- same repair action on the same scope: two attempts per two hours;
- app-server restart on the same scope: two attempts per 30 minutes;
- workflow runtime retry from the same workflow: two retries per 24 hours;
- forced or destructive actions: one failed forced attempt blocks further automatic recovery.
When loopGuard.allowed=false, the client must stop automatic recovery for that
scope, collect diagnostics, and ask a human. Status and diagnostic methods never
execute repair by themselves. codex_repair_issue(dry_run=false),
codex_restart_app_server, and codex_interrupt_turn record guarded attempts.
Dry runs are recorded for audit but do not consume the retry budget.
Known guidance rules:
- active duplicate prompt: poll the existing operation;
CODEX_TIMEOUTwith aclient_request_id: poll first, then retry only with the same id if needed;- failed Plan Mode with sandbox evidence: run
retry_workflow_with_runtime_policyas dry run first; - app-server unavailable while turns are active: collect diagnostics before restart;
- pending interaction: answer or expire it, do not restart the turn;
- auth or rate limit problem: wait or ask a human;
- invalid argument, missing project, missing thread, or missing turn: fix the payload or configuration before retrying.
Diagnostics and analysis
codex_collect_diagnostics and codex_analyze_issue are scoped-first. If the
request includes operation_id, workflow_id, thread_id, or turn_id, exact
matches are ranked before same-thread, same-project, recent global, and
historical findings.
Diagnostics responses may include:
scopedFindings: findings that match the requested scope and should drive the next action.backgroundFindings: nearby or historical findings that are useful context but should not override scoped evidence.evidenceTruncated: true when the result hit evidence size or time caps.
Routine analysis does not fetch raw diagnostic payloads. Even with
include_evidence=true, codex_analyze_issue returns compact evidence refs:
event id, method or category, timestamp, related operation/workflow/thread/turn
ids, and a redacted summary. It does not return raw command output, directory
listings, account data, tokens, full local paths, or full prompts.
Default caps keep analysis suitable for agent loops:
event_limit=50;timeline_limit=50;- max evidence text per item is 500 characters;
- soft analysis time budget is 10 seconds.
When evidenceTruncated=true, the preferred next action is targeted
diagnostics for the same operation, workflow, thread, or turn. Do not switch to
broad raw logs unless a human explicitly asks for them.
Compatibility tools
These tools remain available for UI support, direct reads, diagnostics, and old clients, but new long-running write paths should use durable operations and workflows:
codex_start_chatcodex_send_messagecodex_execute_plancodex_list_projectscodex_list_project_chatscodex_list_active_chatscodex_search_chatscodex_get_chat_statuscodex_get_chatcodex_get_turn_statuscodex_restart_app_servercodex_get_app_server_statuscodex_get_diagnostic_logscodex_analyze_issue
Low-level write compatibility tools return after turn/start. Prefer
codex_submit_task and polling for retry safety.
codex_list_projects is compact by default. Inputs:
compact: defaulttrue.limit: default200, max1000.refresh: defaultfalse.include_private_details: defaultfalse; whentrue, the response includes local paths and normalized path keys.roots: optional root filters.
The response includes cacheState, totalCount, returnedCount, truncated,
and compact. Compact mode is intended for startup and polling loops.
codex_search_chats enforces the requested index_time_budget_seconds as an
end-to-end refresh budget. When refresh work cannot finish inside that budget,
the response is partial and includes timeBudgetExhausted=true with
nextRecommendedAction="retry_without_refresh_or_increase_budget".
codex_get_chat_status and codex_get_chat prefer fresh tracked turn and hook
history data before legacy _kb_history. Fresh threads can therefore return
source="tracked_turn", source="hook_history", or
source="tracked_turn+hook_history" instead of CODEX_THREAD_NOT_FOUND while
the catalog catches up.
Version block
codex_health_summary.version contains:
serverNameserverVersioncontractVersiontoolSurfaceHashguideVersionguideHashrecommendedStartupFlowrecommendedStartupToolrecommendedPrimaryWriteToolstableToolCountcompatibilityToolCountstableToolscompatibilityToolsgeneratedAt
toolSurfaceHash is a SHA-256 hash over tool names, descriptions, input/output
schemas, contract groups, and codexMcp tool annotations. guideHash is a
SHA-256 hash over the machine-readable agent guide. Both are compatibility
probes, not security signatures.
Agent self-description
Every tool has annotations.codexMcp:
role: one ofprimary_write,poll_status,workflow,diagnostics,lifecycle,compatibility, orread_only.preferred: whether a new agent should prefer this tool over compatibility alternatives.nextTools: ordered follow-up calls.avoidWhen: compact warnings.idempotency:required,recommended, ornot_applicable.passiveRead: whether the tool is expected to be storage/cache only.mayStartTurn: whether the tool can start or affect a Codex turn.
The guide also exposes capabilityMap, usageFlows, globalRules, and
runtimeLimits. Agent guidance instructions include guideAction and
guideFlow, so clients can connect a problem response to the same usage flow
they saw during discovery.
Hook history block
codex_health_summary and codex_collect_diagnostics include a compact
hookHistory block:
enabledstatusinstalledeventshooksJsonconfigPathdbWritablethreadCountturnCountmessageCountlastHookEventAtwarnings
Top-level compatibility aliases are also returned: hookHistoryStatus,
lastHookEventAt, hookInstalled, and hookDbWritable.
Health summary focuses on current readiness. Historical stale operations,
premature terminal rows, and old orphaned workflows are returned in
historicalDebt. They do not change current readiness unless the request is
scoped to that operation, workflow, thread, or turn. Use targeted cleanup or
diagnostics for historicalDebt.nextRecommendedAction="run_targeted_cleanup".
Read/status tools may return these source values in addition to older values:
hook_historyapp_server+hook_historytranscript+hook_historymixed
Legacy _kb_history remains a fallback, but public installations should use:
codex-control-plane-mcp-hooks install --state-db <PATH>
The hook installer stores stateDb as an absolute path even when <PATH> is
relative.
For write operations launched through codex-app-server, MCP mirrors the
accepted prompt, visible assistant messages, and turn status into the same hook
history tables. External Codex hooks remain the independent journal for normal
Codex user turns. The app-server mirror covers orchestrator-managed turns when
app-server does not run user hook commands.
Stable error codes
Common stable error codes include:
INVALID_ARGUMENTCODEX_DUPLICATE_PROMPT_ACTIVECODEX_BUSYCODEX_TIMEOUTCODEX_APP_SERVER_UNAVAILABLECODEX_PENDING_INTERACTION_NOT_FOUNDCODEX_PENDING_INTERACTION_UNAVAILABLECODEX_THREAD_NOT_FOUNDCODEX_TURN_NOT_FOUNDCODEX_PROJECT_NOT_FOUNDCODEX_TRANSCRIPT_NOT_FOUNDCODEX_SEND_FAILEDCODEX_SUMMARY_FAILED
Clients should branch on error.code and treat error.retryable as the retry
hint. Human-readable message text is not a stable parsing target.
Operational rules
- Do not mutate Codex internal SQLite or transcript files through MCP.
- Use app-server for write/control operations.
- For strict retry idempotency, pass
client_request_id. - Poll durable operations/workflows instead of holding long
tools/callrequests open. - Do not run risky repairs without explicit
dry_run=false; forced paths also requireforce=true. - Prefer
refresh_catalog_and_history;refresh_catalog_and_kbremains a compatibility alias.