Skip to main content

Connect Codex

The ctrlb-agent installer writes CtrlB into Codex's config.toml. After that, Codex does what Claude Code does with the plugin:

  • Every session, including subagent threads, is uploaded to the session store from the rollout files in ~/.codex/sessions. Secrets are redacted before upload.
  • The CtrlB Model Context Protocol (MCP) server gives the agent the session tools and the relay tools (peers, post, claim, release).
  • At session start the agent is told its run id and agent id. Before each apply_patch, the files the patch names are checked against the run's claims.

It needs Codex CLI 0.166 or newer and Python 3.9 or newer. Nothing else is installed.

1. Generate your MCP key​

  1. In CtrlB, open AI Observability in the sidebar and choose Connect.
  2. Under Your MCP key, click Generate key. Rotate key replaces an existing key.
  3. Choose the Codex tab under Connect an agent. Copy command fills your key into what you copy.

The key acts as you, with your roles and dataset grants.

2. Set the environment​

Add these lines to your shell profile and open a new terminal:

export CTRLB_URL="<your CtrlB URL>"
export CTRLB_MCP_URL="<your CtrlB URL>/mcp"
export CTRLB_KEY="<your key>"

Codex reads the key from CTRLB_KEY each time it starts. The key is never written into config.toml.

3. Run the installer​

The installer runs from a clone of the repository. The hooks it writes call codex/hook.py in that clone, so keep the clone where it is.

git clone https://github.com/ctrlb-hq/ctrlb-agent.git ~/ctrlb-agent
cd ~/ctrlb-agent
python3 -I install.py --codex --dry-run
python3 -I install.py --codex

--dry-run prints the change to ~/.codex/config.toml as a diff and writes nothing. The second command writes it.

Then start Codex and run /hooks once. Codex skips new or changed hooks until you trust them.

4. Verify​

  1. Start Codex and give it one task, so the session has at least one turn.
  2. In CtrlB, open AI Observability, then Runs. The session appears as a run. Click it, then click the agent's name to read its transcript.

If nothing appears, check that you trusted the hooks with /hooks, and read ~/.ctrlb/hooks.log.

What the installer writes​

The installer writes one block between two marker comments, # >>> ctrlb-agent and # <<< ctrlb-agent. Everything else in the file stays as it was.

[mcp_servers.ctrlb]
url = "<your CtrlB URL>/mcp"
bearer_token_env_var = "CTRLB_KEY"
env_http_headers = { "X-CtrlB-Run" = "CTRLB_RUN", "X-CtrlB-Agent" = "CTRLB_AGENT" }
  • url is CTRLB_MCP_URL, or CTRLB_URL followed by /mcp, or the value of --mcp-url.
  • bearer_token_env_var names the variable Codex reads the key from. Codex sends it as Authorization: Bearer.
  • env_http_headers sends CTRLB_RUN and CTRLB_AGENT as headers when they are set.

Then one [[hooks.<Event>]] table per event. Each runs python3 -I <clone>/codex/hook.py <mode>:

EventModeWhat it does
SessionStartsession-startTells the agent its run id and agent id, and how to search its record
SubagentStartsubagent-startUploads the parent's new lines and tells the subagent its parent and ids
PreCompact, SessionEndflushUploads the lines added since the last upload
Stop, SubagentStopuploadThe same, in the background
PreToolUse, matcher apply_patchpre-toolSends the patch's file paths to the edit check

For example, the PreToolUse table:

[[hooks.PreToolUse]]
matcher = "apply_patch"
[[hooks.PreToolUse.hooks]]
type = "command"
command = "python3 -I \"/home/you/ctrlb-agent/codex/hook.py\" pre-tool"
timeout = 5

Every hook exits without an error and never blocks a session. Errors go to ~/.ctrlb/hooks.log.

Backup and running it again​

  • Before it writes, the installer copies the old file to config.toml.ctrlb-backup-<time> beside it.
  • Running it again with the same settings changes nothing and makes no backup. With a different URL, it replaces the block. --dry-run shows the changed lines first.
  • It refuses to write, and changes nothing, when config.toml already has an MCP server named ctrlb outside the block. Remove that one with codex mcp remove ctrlb and run the installer again.

Options, all passed after --codex:

OptionMeaning
--dry-runPrint the diff and write nothing
--mcp-url URLThe MCP URL to write
--codex-home DIRAnother Codex home (default $CODEX_HOME, else ~/.codex)
--config FILEEdit this file instead of <codex home>/config.toml
--otelAlso write Codex's OpenTelemetry export. See Connect over HTTP and OpenTelemetry
--uninstallRemove the block

To try it without touching your Codex setup, point it at an empty home:

cd ~/ctrlb-agent
CODEX_HOME="$(mktemp -d)" python3 -I install.py --codex --mcp-url "<your CtrlB URL>/mcp" --dry-run

Runs and agents​

A Codex session is its own run, and its subagent threads join it. To put several sessions in one run, give them the same CTRLB_RUN:

CTRLB_RUN=nightly-refactor-1 codex exec "Fix the failing tests in src/parser"
  • The agent id is the thread id. Set CTRLB_AGENT to use a label instead.
  • user_name on every row is the user your key belongs to. CTRLB_USER is still read for compatibility, as the agent label when CTRLB_AGENT is unset. It never sets user_name.

Edit checks​

Before each apply_patch, the hook reads the file paths from the patch's *** Update File:, *** Add File:, *** Delete File: and *** Move to: lines and sends them to CtrlB. The workspace's enforce mode decides the answer:

Enforce modeWhen another agent in the run holds a file
offNothing happens.
advisory (default)The agent gets a note naming the holder and the claim call to make. The patch goes ahead.
enforceThe patch is refused. The reason names the holder and the claim call to make.

The check gives up after two seconds and lets the patch through. See Limits for what the check does not cover.

Past sessions and removal​

  • Upload past sessions: python3 -I ~/ctrlb-agent/bin/backfill.py --codex. It lists what it would send and asks first. --count only lists.
  • Off for one session: CTRLB_OFF=1 codex.
  • Remove: python3 -I ~/ctrlb-agent/install.py --codex --uninstall. It backs the file up first, as an install does.