# Codex Control Plane Mcp

> Durable MCP control plane for long-running Codex Desktop tasks

- **Type:** MCP server
- **Install:** `agentstack add mcp-aresyn-codex-control-plane-mcp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [aresyn](https://agentstack.voostack.com/s/aresyn)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [aresyn](https://github.com/aresyn)
- **Source:** https://github.com/aresyn/codex-control-plane-mcp
- **Website:** https://pypi.org/project/codex-control-plane-mcp/

## Install

```sh
agentstack add mcp-aresyn-codex-control-plane-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

[](https://mseep.ai/app/aresyn-codex-control-plane-mcp)

# Codex Control Plane MCP

English | [Русский](README.ru.md)

[](https://github.com/aresyn/codex-control-plane-mcp/actions/workflows/ci.yml)
[](https://pypi.org/project/codex-control-plane-mcp/)
[](https://www.python.org/)
[](LICENSE)
[](docs/API_CONTRACT.md)

  

Reliable Codex Desktop automation for long tasks.

`codex-control-plane-mcp` turns Codex Desktop and `codex-app-server` into a
durable worker that an MCP client can drive safely. Send a task, get an
`operationId` or `workflowId` right away, poll until the work finishes, approve
Plan Mode when needed, then read the final report.

The server handles the awkward parts that thin wrappers usually leave to the
caller: app-server startup, thread and turn creation, retry safety, duplicate
prompt protection, Plan Mode, approvals, local history, diagnostics, and repair.

OpenClaw and Hermes are first-class clients, but the server is useful for any
local orchestrator that needs Codex Desktop to do long-running work without
holding one MCP call open for hours.

## The short version

```text
MCP client / orchestrator
  -> submit a task or start a Plan Mode workflow
   poll status
  -> answer approvals or approve the plan
   operationId
codex_get_operation_status(operationId)
  -> queued / running / waiting_for_approval / completed / failed
```

Use the same `client_request_id` when a caller retries after a transport timeout.
The retry returns the existing operation instead of creating another turn.

Attach screenshots or other image evidence:

```text
codex_submit_task(
  operation_type="start_chat",
  message="Analyze this screen.",
  input_items=[
    {"type": "localImage", "path": ".\\screens\\error.png", "detail": "low"},
    {"type": "image", "url": "https://example.com/screenshot.png", "detail": "high"}
  ]
)
```

Image inputs are accepted only for operation types that start a new turn:
`start_chat`, `send_message`, `execute_plan`, and `fork_thread` with an initial
message. MCP sends the path or URL to `codex-app-server`, but operation status
and diagnostics return only safe metadata such as type, detail, size, extension,
and hashes. Binary image content, raw URLs, and full local image paths are not
stored in public status payloads.

Steer an active turn:

```text
codex_submit_task(operation_type="steer_turn", thread_id=..., expected_turn_id=..., message=...)
  -> operationId
codex_get_operation_status(operationId)
  -> follows the target turn until completed / failed / interrupted
```

Use `steer_turn` only while the target turn is active. For a completed thread,
use `send_message` instead.

Fork a thread:

```text
codex_submit_task(operation_type="fork_thread", source_thread_id=...)
  -> operationId
codex_get_operation_status(operationId)
  -> completed, threadId=
```

Start work in the fork right away:

```text
codex_submit_task(operation_type="fork_thread", source_thread_id=..., message=...)
  -> operationId
codex_get_operation_status(operationId)
  -> follows the first turn in the forked thread
```

Use `client_request_id` for retry-safe fork requests. Without it, each call is
treated as a new fork request. `threadId` in operation status is the forked
thread; the source thread is reported in `forkState.sourceThreadId`.

Manage thread lifecycle:

```text
codex_archive_thread(thread_id)
  -> completed
codex_unarchive_thread(thread_id)
  -> completed
codex_start_thread_compaction(thread_id)
  -> actionId
codex_get_thread_compaction_status(actionId)
  -> running / completed / unknown_after_app_server_exit
```

Archive and unarchive are audit actions around app-server `thread/archive` and
`thread/unarchive`. They refuse to run while the thread has an active turn or a
pending interaction. Compaction uses its own lightweight `actionId` because
`thread/compact/start` is asynchronous. Public `thread/delete` is intentionally
not exposed.

Ask for a structured final report:

```text
codex_submit_task(operation_type="start_chat", message=..., output_schema={...})
codex_approve_plan(workflowId, output_schema={...})
  -> operationId / executionOperationId
codex_get_operation_status(operationId)
codex_get_workflow_status(workflowId)
  -> finalReport.text + finalReport.structured
```

`output_schema` is passed to app-server `turn/start` and is tracked by a schema
hash in status output. Object schemas must use the strict form required by Codex:
set `additionalProperties` to `false`. MCP stores the final assistant message
as readable text, then parses JSON object output into `finalReport.structured`
when Codex returns valid JSON. Plain text still works and stays available in
`finalReport.text`.

MCP does not extract hidden chain-of-thought and does not store raw tool
payloads or command output in final reports.

Drive Plan Mode:

```text
codex_start_plan_workflow
  -> workflowId
codex_get_workflow_status(workflowId)
  -> wait_plan / review_plan / execute_plan
codex_approve_plan(workflowId)
  -> executionOperationId
codex_get_workflow_status(workflowId)
  -> finalReport
```

Plan Mode has a runtime floor. The public default write policy is still
`read-only` and `on-request`, but Plan Mode needs a writable workspace on
Windows. If the caller or server default resolves to `read-only`, MCP sends
`workspace-write` to `codex-app-server` and returns `requestedSandbox`,
`effectiveSandbox`, and `runtimePolicyAdjusted` in workflow and operation
status.

Mirror a workflow goal into Codex Desktop when the client has one:

```text
codex_start_plan_workflow(goal="Review the migration plan", goal_completion_action="clear")
codex_get_workflow_status(workflowId, refresh_live_goal=true)
  -> threadGoal.syncState + threadGoal.currentGoal
```

MCP writes a thread goal only when the client passes `goal`. Managed goals use
`clear` after completion by default. Use `set_complete` or `leave` when the goal
should remain visible after the workflow ends. Normal workflow polling is
passive; use `refresh_live_goal=true` only when you want MCP to call live
app-server goal methods.

Run a Codex code review:

```text
codex_start_review_workflow(thread_id=..., target_type="base_branch", base_branch="main")
  -> workflowId
codex_get_workflow_status(workflowId)
  -> wait_review / read_review_report
```

Or let MCP create a service thread for a local checkout:

```text
codex_start_review_workflow(cwd=..., target_type="uncommitted_changes")
  -> workflowId
codex_get_workflow_status(workflowId)
  -> reviewThreadId + reviewTurnId + finalReport
```

Review workflows do not write files by themselves. They run inside the selected
Codex sandbox and approval policy. Use `client_request_id` when a caller may
retry the start request after a transport timeout.

Handle approvals and questions:

```text
codex_list_pending_interactions
codex_answer_pending_interaction
```

Start diagnostics with:

```text
codex_get_runtime_capabilities
codex_health_summary
codex_collect_diagnostics
codex_analyze_issue
codex_repair_issue
```

Repair actions default to `dry_run=true`.

Status and diagnostic tools also return `agentGuidance` and
`agentGuidanceText` when MCP sees a blocker, failed state, stale run, pending
interaction, duplicate prompt, auth problem, rate limit, or unsafe recovery
loop. Agents should follow `agentGuidance.instructions` before deciding to
retry or stop. If `agentGuidance.loopGuard.allowed=false`, stop automatic
recovery, collect diagnostics, and ask a human. Do not create a new
`client_request_id` after a timeout unless the guidance explicitly says to start
a replacement workflow.

For a broken Plan Mode workflow, use
`retry_workflow_with_runtime_policy`. It creates a new workflow with the selected
sandbox and approval policy, links it to the old workflow through
`workflowRetryState`, and does not revive the old terminal turn.

`codex_health_summary` is about current readiness by default. Old stale or
orphaned rows are reported in `historicalDebt`, but they do not make fresh
orchestration look broken when the worker, queue, and app-server are currently
healthy. Use targeted cleanup for that debt instead of blocking new work.

Status payloads now separate freshness signals:

- `operationRowAgeSeconds`: age of the durable operation row;
- `turnFreshness.lastProgressAgeSeconds`: age of the last turn progress event;
- `workerFreshness.heartbeatAgeSeconds`: age of the worker heartbeat;
- `stalenessMeaning="operation_row_age"` for the compatibility
  `stalenessSeconds` field.

Public status payloads are agent-safe. Operation and workflow status return
`requestSummary` instead of raw `request`; it contains ids, runtime policy,
scheduling intent, input item state, output schema hash, resource keys, and
text hashes. It does not include the full prompt, full instructions, raw title,
raw image URL/path, exact token counts, raw command output, or private paths.
Use your own stored task text plus `requestSummary.*.sha256` for correlation.

`codex_get_queue_status` only recommends `wait_for_worker_slot` when there is
actual queued work blocked by slots. If there are running turns but
`queueSummary.queued == 0`, the queue action is `none`.

## Runtime capabilities

Use `codex_get_runtime_capabilities` before orchestration or after reconnect. It
starts the MCP-owned app-server if needed, calls short best-effort inventory
methods, and returns a cached snapshot for five minutes.

In `client` mode, the client process does not start its own app-server for live
inventory. It returns a passive worker-managed snapshot when one exists. With
`refresh=true`, it queues a worker command and returns `refreshCommandId`; poll
`codex_get_worker_command_status` to read the refreshed inventory.

The response includes:

- model count, default model, hidden flags, input modalities, reasoning efforts, and service tier count;
- permission profiles by `id` and `description`;
- Windows sandbox readiness;
- provider capabilities for web search, image generation, and namespace tools;
- hook and skill counts without raw hook commands or absolute skill paths;
- redacted account status, coarse usage bands, and operational rate-limit state;
- supported app-server schema methods with a compact source, version, and hash.

Account inventory is safe to show to an orchestrator. It reports whether Codex
is authenticated, the account and plan type, whether an email exists, whether
usage data is available, and whether a rate limit or credits issue is visible.
It does not return raw email, account identifiers, credit balances, spend
limits, exact spend used, daily usage buckets, or exact token counts.

If one inventory method times out or fails, the tool still returns `ok=true`
with `runtimeCapabilities.status="partial"` and a machine-readable warning in
`methodResults`. Set `refresh=true` to bypass the cache. `codex_health_summary`
shows a small `runtimeCapabilities` subset from the last collected snapshot and
does not start app-server on its own. Pass `include_account=false` when a client
does not need account, usage, or rate-limit status.

## Progress journal

`codex_get_turn_status` and `codex_get_operation_status` include a compact
`progressEvents` block by default. It captures app-server-visible progress such
as assistant text deltas, plan deltas, reasoning summary text, token usage,
model reroutes, and warnings.

The journal helps with orchestration and troubleshooting. It does not extract
hidden chain-of-thought. It also does not store raw tool payloads, command
output, or full unified diffs by default. Diff events are reduced to safe
counts, such as changed line count and diff size.

Use `progress_events=0` when a client wants the older, message-only status
shape. Use `progress_max_chars` to cap returned progress text.

Public status returns token usage as coarse bands, not exact token counts. Raw
audit surfaces may keep redacted event payloads for debugging, but orchestrators
should treat `tokenUsage.totalTokensBand` and related band fields as the public
contract.

## Tool surface

Stable orchestration tools:

- `codex_submit_task`
- `codex_get_operation_status`
- `codex_start_plan_workflow`
- `codex_start_review_workflow`
- `codex_get_workflow_status`
- `codex_approve_plan`
- `codex_list_pending_interactions`
- `codex_answer_pending_interaction`
- `codex_interrupt_turn`
- `codex_archive_thread`
- `codex_unarchive_thread`
- `codex_start_thread_compaction`
- `codex_get_thread_compaction_status`
- `codex_get_runtime_capabilities`
- `codex_health_summary`
- `codex_collect_diagnostics`
- `codex_repair_issue`

Compatibility and read tools:

- `codex_start_chat`
- `codex_send_message`
- `codex_execute_plan`
- `codex_list_projects`
- `codex_list_project_chats`
- `codex_list_active_chats`
- `codex_search_chats`
- `codex_get_chat_status`
- `codex_get_chat`
- `codex_get_turn_status`
- `codex_restart_app_server`
- `codex_get_app_server_status`
- `codex_get_diagnostic_logs`
- `codex_analyze_issue`

New clients should use durable operations and workflows. Low-level write tools
stay available for compatibility.

Read and diagnostic calls are bounded for agent loops. `codex_list_projects`
defaults to compact cached output, `codex_search_chats` can return
`timeBudgetExhausted=true` instead of blocking on a full refresh, and chat reads
prefer tracked turn plus hook history before legacy KB fallback. Diagnostics are
scoped-first: `scopedFindings` drive the next action, while
`backgroundFindings` are historical context.

See [docs/API_CONTRACT.md](docs/API_CONTRACT.md) for schemas, error shape,
stable tool groups, and versioning rules.

## Result contract

Every tool declares an `outputSchema` and returns MCP `structuredContent`.

Success:

```json
{"ok": true}
```

Domain or tool error:

```json
{
  "ok": false,
  "error": {
    "code": "CODEX_ERROR_CODE",
    "message": "Human readable message",
    "details": {},
    "retryable": false
  }
}
```

Call `codex_health_summary` on startup and reconnect. The `version` block
contains `serverName`, `serverVersion`, `contractVersion`, `toolSurfaceHash`,
`guideHash`, `guideVersion`, recommended startup/write tools, and
stable/compatibility tool lists.

Agents can discover the operating contract without reading this README.
`tools/list` includes:

- `codexMcpGuide`: compact machine-readable guide with capabilities, flows,
  global rules, and runtime limits;
- `toolGroups`: ordered groups of preferred tools;
- `recommendedStartupTool="codex_health_summary"`;
- `recommendedPrimaryWriteTool="codex_submit_task"`.

Every tool also has `annotations.codexMcp` with its role, follow-up tools,
idempotency rule, passive-read flag, and `mayStartTurn` flag. If a client
library hides top-level `tools/list` fields, call
`codex_get_agent_contract(detail="compact")` or
`codex_get_agent_contract(detail="full", include_examples=true)`.

## Configuration

Configuration can come from environment variables or from a JSON file referenced
by `CODEX_CONTROL_PLANE_MCP_CONFIG`. The old `OPENCLAW_CODEX_MCP_CONFIG` name is
still accepted as a fallback.

Common variables:

- `CODEX_HOME`: Codex home directory. Defaults to `%USERPROFILE%\.codex`.
- `CODEX_PROJECTS_ROOT`: project root scanned by catalog and read tools.
- `CODEX_ALLOWED_ROOTS`: semicolon-separated path allowlist.
- `CODEX_PROJECTS_REGISTRY`: optional JSON project registry.
- `CODEX_MCP_STATE_DB`: local MCP state DB.
- `CODEX_CONTROL_PLANE_MCP_LOG`: log file path.
- `CODEX_MCP_HOOK_HISTORY_ENABLED`: enables SQLite hook history. Defaults to `true`.
- `CODEX_MCP_HOOK_HISTORY_MAX_TEXT_CHARS`: per-message hook capture limit.
- `CODEX_KB_HISTORY_PROJECTS_ROOT`: optional legacy normalized KB history root.
- `CODEX_BINARY_PATH`: optional explicit Codex binary path.
- `CODEX_MCP_DEFAULT_SANDBOX`: default write sandbox. Defaults to `read-only`.
- `CODEX_MCP_DEFAULT_APPROVAL_POLICY`: default write approval policy. Defaults to `on-request`.
- `CODEX_MCP_DEFAULT_MODEL`: default Codex model passed to app-server.
- `CODEX_MCP_DEFAULT

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [aresyn](https://github.com/aresyn)
- **Source:** [aresyn/codex-control-plane-mcp](https://github.com/aresyn/codex-control-plane-mcp)
- **License:** Apache-2.0
- **Homepage:** https://pypi.org/project/codex-control-plane-mcp/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-aresyn-codex-control-plane-mcp
- Seller: https://agentstack.voostack.com/s/aresyn
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
