One Brain, Four Hands
Picture a surgeon with four arms. Each arm holds a different instrument — a scalpel, a clamp, a suture needle, a camera. The arms look nothing alike. They serve entirely different purposes. But they are all controlled by the same brain, following the same surgical plan, drawing on the same years of training.
That is the architecture of Codex CLI's frontend layer.
In the previous articles, we traced the nervous system (the message protocol), dissected the brain (the orchestrator), and explored the vault (the sandbox). Now we arrive at the surface — the four distinct interfaces through which users and other programs interact with that single, shared intelligence.
The four frontends are:
- The TUI — a rich, interactive terminal UI built with Ratatui
- The Headless Executor — a non-interactive runner for CI/CD pipelines
- The App Server — a JSON-RPC 2.0 server for IDEs like VS Code and Cursor
- The MCP Server — a bridge that lets other AI agents use Codex as a tool
Each frontend is a thin shell. The real work happens in the shared core — the AgentCoreProcess, the sandbox, the tool registry, the conversation engine. The frontends only differ in how they present information and how they collect user decisions.
The Architecture at a Glance
Click any frontend to explore its role:
An event-driven state machine built with Ratatui. Renders a full terminal UI with streaming markdown, vim-style keybindings (j/k, i, Escape, dd, :q), approval dialogs, and diff previews. The user sees a rich, interactive experience while the core engine does the heavy lifting underneath.
No UI at all. Runs the same core engine in a non-interactive mode with two output formats: HumanOutput (pretty terminal with colors) and JsonOutput (JSONL for machine consumption). Auto-approval modes let it run unattended in pipelines. A policy engine makes approval decisions without human input.
A transport-agnostic server that speaks JSON-RPC 2.0 over stdio or WebSocket. VS Code, Cursor, and other IDEs connect to this server and drive the agent through methods like thread/start, turn/start, and review/respond. A ThreadManager holds a HashMap of concurrent sessions.
The most mind-bending frontend. Instead of a human using Codex, another AI agent uses Codex as a tool. The MCP Server exposes Codex's capabilities through the Model Context Protocol, letting orchestrators like Claude Desktop invoke it via CodexToolRunner. Built on the rmcp crate.
Deep Dive 1: The TUI (Ratatui)
When you type codex with no flags, you land in the TUI. It is the most complex of the four frontends because it must do something remarkably hard: present a sophisticated AI agent inside an 80-column terminal.
The App Struct: A State Machine
At its heart, the TUI is an event-driven state machine. The App struct holds the entire UI state — which panel is focused, whether a modal is open, the current input buffer, scroll positions, and the conversation history. Every frame, the TUI follows a tight loop:
// The core TUI loop, simplified
loop {
// 1. Render current state to terminal
terminal.draw(|frame| {
app.render(frame);
})?;
// 2. Wait for an event (key press, agent message, resize)
let event = next_event(&mut event_stream).await;
// 3. Update state based on the event
match app.handle_event(event) {
Action::Quit => break,
Action::SendMessage(msg) => core.send(msg).await,
Action::ApproveCommand(id) => core.approve(id).await,
_ => {}
}
}
This is the classic render → wait → update loop that every immediate-mode UI framework uses. Ratatui handles the rendering. Crossterm handles the terminal events. The App struct is the glue.
Vim Bindings: Muscle Memory Matters
The TUI ships with vim-style keybindings because the people who live in terminals tend to have vim burned into their fingers:
Press i to enter insert mode and type your prompt. Press Escape to return to normal mode. Use j/k to scroll through the conversation. dd clears your current input. :q quits. It feels like home.
Streaming Markdown & Approval Dialogs
As the agent streams its response, the TUI renders markdown in real time — syntax-highlighted code blocks, bold text, lists, all flowing character by character. When the agent proposes a file edit or command execution, the TUI pauses and presents an approval dialog with a diff view showing exactly what will change. The user can approve, reject, or ask for modifications. This is the human-in-the-loop boundary that makes the agent safe to use interactively.
Deep Dive 2: Headless Exec
Not every interaction needs a terminal. Sometimes you want to fire off Codex in a CI pipeline, a cron job, or a shell script. That is what the headless executor is for.
Two Output Modes
The exec frontend supports two output strategies, selected at startup:
enum OutputMode {
// Pretty-printed for human eyes in a terminal
HumanOutput {
writer: TermWriter,
colors: bool,
},
// Structured JSONL for machine consumption
JsonOutput {
stream: Stdout,
},
}
HumanOutput renders the agent's responses with ANSI colors and formatting — it looks like a normal terminal session. JsonOutput emits one JSON object per line (JSONL), where each line represents an event: a message chunk, a tool call, a completion signal. This is what you pipe into jq or feed to another program.
Auto-Approval for CI/CD
In an interactive session, the agent asks permission before running commands. In a pipeline, there is no human to ask. The exec frontend solves this with auto-approval modes:
suggest — Show what it would do, but do not execute anything
auto-edit — Automatically approve file edits, but ask for shell commands
full-auto — Approve everything. Use with a locked-down sandbox and a policy engine
The Policy Engine
When running in full-auto, decisions are not truly "automatic" — they pass through a policy engine. This is the same ExecPolicy DSL we explored in Article 4 (The Vault), now acting as a headless decision-maker. If a command matches an allow-rule, it runs. If it matches a deny-rule, it is blocked. If it matches neither, the default policy applies. No human needed, but still governed by rules.
Deep Dive 3: App Server (JSON-RPC 2.0)
This is the frontend that powers every IDE integration. When you use Codex inside VS Code or Cursor, you are not talking to the TUI. You are talking to the App Server — a headless JSON-RPC 2.0 service that the IDE drives like a puppet.
Transport + Message Processor
The App Server has two layers. The transport handles the wire protocol — either stdio (for local processes) or WebSocket (for remote connections). The message processor parses incoming JSON-RPC requests, dispatches them to the core engine, and streams responses back. The transport is pluggable. The processing logic is identical regardless of how bytes arrive.
Key Methods
{
"jsonrpc": "2.0",
"id": 1,
"method": "turn/start",
"params": {
"thread_id": "abc-123",
"message": "Fix the login bug"
}
}
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "streaming",
"turn_id": "turn-456"
}
}
The four critical methods are:
thread/start— Create a new conversation thread with a session IDturn/start— Send a user message and begin an agent turnreview/respond— Approve or reject a pending tool callconfig/get— Retrieve current configuration and model settings
ThreadManager: Concurrent Sessions
The App Server can run multiple conversations simultaneously. It uses a ThreadManager that holds a HashMap<ThreadId, AgentThread> — each thread is an independent agent session with its own conversation history, sandbox, and state. This is how a single Codex process can serve multiple IDE tabs or even multiple users.
struct ThreadManager {
threads: HashMap<ThreadId, AgentThread>,
config: Arc<AppConfig>,
}
impl ThreadManager {
async fn start_thread(&mut self, id: ThreadId) -> Result<()> {
let thread = AgentThread::new(
id.clone(),
self.config.clone(),
).await?;
self.threads.insert(id, thread);
Ok(())
}
}
Deep Dive 4: MCP Server
This is where things get recursive.
The first three frontends all assume a human (or human-built system) is driving Codex. The MCP Server flips this assumption on its head: now another AI agent is the user. Codex becomes a tool that other agents can invoke, like a function call.
The Reverse Integration
In the App Server model, Codex is the brain and the IDE is the window. In the MCP model, some other AI is the brain, and Codex is just one of its tools. Claude Desktop, for example, can call Codex to run code, edit files, or explore a codebase — all through the Model Context Protocol.
An AI agent calling another AI agent. Codex might use GPT-4.1 internally while being invoked by Claude externally. Two different models collaborating through a clean protocol boundary. This is agent composition.
CodexToolRunner
The MCP Server wraps the core engine in a CodexToolRunner — a struct that implements the MCP tool interface. When an external agent sends a request like "run this code and tell me the output," the runner creates a temporary agent session, executes the task, and returns the result. It is built on the rmcp crate (Rust MCP), which handles the protocol serialization and transport.
// Simplified MCP tool registration
struct CodexToolRunner {
config: CodexConfig,
}
impl McpTool for CodexToolRunner {
fn name(&self) -> &str { "codex_run" }
async fn invoke(&self, input: Value) -> Result<Value> {
let prompt = input["prompt"].as_str()?;
let session = AgentSession::headless(
&self.config, prompt
).await?;
session.run_to_completion().await
}
}
Approval Flows in MCP
Here is the tricky part: when Codex is running as an MCP tool, who approves dangerous commands? The calling agent cannot click "yes" on a dialog box. The MCP Server handles this by inheriting the approval policy of the session that launched it — typically auto-edit or full-auto with a strict policy engine. The human approves the policy once, and the machines follow it autonomously.
Hexagonal Architecture
What we have been describing has a name in software architecture: the Hexagonal Architecture, also known as Ports and Adapters. The idea is simple but powerful:
Put your business logic in the center. Surround it with ports (interfaces) and adapters (implementations). The core never knows which adapter is talking to it. It does not care if it is a terminal, an IDE, or another AI.
In Codex's case:
This architecture has three enormous benefits:
- The core stays focused. It does not have rendering code, terminal escape sequences, or JSON-RPC parsing. It just orchestrates AI agent tasks.
- Frontends are independent. You can ship a new frontend without touching the core. You can test the TUI without an LLM. You can test the core without a terminal.
- Scaling is natural. Need a new integration? Write a new adapter. The fifth frontend could be a Slack bot, a web app, or a voice interface. The core does not care.
How to Add a New Frontend
If you wanted to build a fifth window into Codex's brain — say, a Slack bot or a web dashboard — here is the recipe:
Implement the Port Traits
Your frontend must implement EventSink (to receive agent events), ApprovalHandler (to make approval decisions), and OutputWriter (to present results). These traits are your contract with the core.
Spawn the Core Process
Create an AgentCoreProcess with your adapter injected. The core does not know or care that it is talking to a Slack bot. It sends events through the port. You render them however you want.
Handle Approvals Your Way
The TUI shows a dialog. The exec frontend checks a policy. The App Server sends a JSON-RPC notification. Your Slack bot could post a message with reaction buttons. The mechanism is yours; the contract is the same.
That is it. Three steps. The hexagonal architecture makes this possible because the core has zero knowledge of its frontends. It speaks through abstractions, and the adapters do the translation.
The View From Here
We have now seen the full surface of Codex CLI. Four frontends, each serving a radically different use case, all sharing a single core engine. The TUI for humans who love their terminals. The executor for pipelines that never sleep. The App Server for IDEs that need real-time agent collaboration. And the MCP Server for the coming age of agent-to-agent communication.
But all four windows share one thing: they need the agent to do things. Read files. Write code. Run commands. Apply patches. In the next article, we will dissect the tool system — the Swiss Army knife that gives the agent its hands.
Article 6 will explore the tool registry — how Codex defines, discovers, validates, and executes tools. From built-in file operations to shell commands to custom MCP tools, this is where the agent stops thinking and starts doing.