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
- In CtrlB, open AI Observability in the sidebar and choose Connect.
- Under Your MCP key, click Generate key. Rotate key replaces an existing key.
- 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
- Start Codex and give it one task, so the session has at least one turn.
- 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" }
urlisCTRLB_MCP_URL, orCTRLB_URLfollowed by/mcp, or the value of--mcp-url.bearer_token_env_varnames the variable Codex reads the key from. Codex sends it asAuthorization: Bearer.env_http_headerssendsCTRLB_RUNandCTRLB_AGENTas headers when they are set.
Then one [[hooks.<Event>]] table per event. Each runs python3 -I <clone>/codex/hook.py <mode>:
| Event | Mode | What it does |
|---|---|---|
SessionStart | session-start | Tells the agent its run id and agent id, and how to search its record |
SubagentStart | subagent-start | Uploads the parent's new lines and tells the subagent its parent and ids |
PreCompact, SessionEnd | flush | Uploads the lines added since the last upload |
Stop, SubagentStop | upload | The same, in the background |
PreToolUse, matcher apply_patch | pre-tool | Sends 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-runshows the changed lines first. - It refuses to write, and changes nothing, when
config.tomlalready has an MCP server namedctrlboutside the block. Remove that one withcodex mcp remove ctrlband run the installer again.
Options, all passed after --codex:
| Option | Meaning |
|---|---|
--dry-run | Print the diff and write nothing |
--mcp-url URL | The MCP URL to write |
--codex-home DIR | Another Codex home (default $CODEX_HOME, else ~/.codex) |
--config FILE | Edit this file instead of <codex home>/config.toml |
--otel | Also write Codex's OpenTelemetry export. See Connect over HTTP and OpenTelemetry |
--uninstall | Remove 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_AGENTto use a label instead. user_nameon every row is the user your key belongs to.CTRLB_USERis still read for compatibility, as the agent label whenCTRLB_AGENTis unset. It never setsuser_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 mode | When another agent in the run holds a file |
|---|---|
off | Nothing happens. |
advisory (default) | The agent gets a note naming the holder and the claim call to make. The patch goes ahead. |
enforce | The 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.--countonly 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.