06
Article Six — Tools & Extensibility

The Swiss Army Knife

MCP, Tools, and the Plugin Ecosystem. How Codex transforms from a single-purpose CLI into a universal integration platform through one protocol: the Model Context Protocol.

MCP is USB for AI

Before USB, every peripheral spoke a different language. Printers needed parallel ports, mice needed PS/2 connectors, modems needed serial. Then one protocol unified them all: Universal Serial Bus. Plug anything in. It just works.

MCP — the Model Context Protocol — does the same thing for AI agents. It is JSON-RPC 2.0 over stdio. Client asks, server responds. That is the entire specification. No custom adapters, no bespoke integrations, no vendor lock-in.

Without MCP

  • Shell commands only
  • File read/write
  • Patch application
  • Hard-coded tools
  • No external services

With MCP

  • GitHub / GitLab APIs
  • Jira / Linear / Notion
  • Slack / Discord
  • CI/CD pipelines
  • Deploy to any platform
  • Custom internal tools
  • Databases, caches, queues
GitHub
Jira
Slack
Vercel
Datadog
PostgreSQL
Codex CLI

Key insight: MCP turns Codex from a tool that can only talk to your filesystem into a tool that can talk to anything. One protocol. Any server. Infinite possibilities.

Codex as MCP Client

Codex implements the MCP client side through the rmcp_client crate — a Rust implementation of the Model Context Protocol. The initialization sequence is precise and deterministic:

mcp_client_init.rs
// MCP Client Initialization Pipeline async fn initialize_mcp() { // 1. Read config let config = read_config("~/.codex/config.toml"); // 2. Spawn MCP servers as child processes for server in config.mcp_servers { let child = spawn_stdio_server(server.command); // 3. Discover available tools via initialize() let tools = child.list_tools().await; // 4. Inject tool definitions into context context.register_tools(tools); } // 5. Route calls to correct server at runtime // 6. Return results to Claude }

Architecture: The MCP Pipeline

config.toml Spawn Servers Discover Tools Inject Context Route Calls Return Results

Each MCP server runs as a separate child process communicating over stdio. This means a crashing server cannot take down Codex. It also means servers can be written in any language — TypeScript, Python, Go, Rust — as long as they speak JSON-RPC 2.0.

Connector Management

MCP server connections are configured in ~/.codex/config.toml. The ConnectorManager handles the lifecycle of every server process, including discovery, caching, and authentication.

basic
with oauth
multi-server

Caching (1hr TTL)

Tool discovery results are cached for one hour. After TTL expires, Codex re-queries each server's tool list. This balances freshness with startup speed.

🔑

OAuth Handling

ConnectorManager stores OAuth tokens and handles refresh flows transparently. Servers receive fresh tokens on each call without manual re-authentication.

The Skills System

Skills are another axis of extensibility. While MCP servers provide tools (functions Claude can call), skills provide knowledge (instructions injected into the system prompt). Two types exist:

System Skills

  • Embedded in the binary
  • Installed via install_system_skills
  • Fingerprinted for integrity
  • Core capabilities (git, test, etc.)
  • Cannot be modified by users

📦 User Skills

  • Located in ~/.codex/skills/
  • Discovered by Skills Manager
  • Written as YAML or TOML
  • Custom workflows and prompts
  • Hot-reloaded on change
install_system_skills.rs
fn install_system_skills(target: &Path) { for skill in EMBEDDED_SKILLS { let path = target.join(skill.name); write(&path, skill.content); // Fingerprint check: detect tampering let hash = sha256(skill.content); store_fingerprint(skill.name, hash); } }
~/.codex/skills/deploy-helper.yaml
name: deploy-helper description: Assists with Vercel deployments trigger: "deploy|ship|release" instructions: | When deploying, always run tests first. Use `vercel --prod` for production. Check environment variables are set.

How injection works: The Skills Manager discovers all matching skills, serializes them as YAML/TOML blocks, and injects them directly into Claude's system prompt. Claude sees them as additional instructions, not as tools.

Shell-Tool-MCP Internals

The shell tool — Codex's primary built-in tool — is more sophisticated than it appears. Under the hood, it uses patched Bash binaries compiled for multiple platforms, with an execution interception layer.

Patched Bash Binaries

Codex ships with pre-compiled Bash binaries for darwin-arm64, darwin-x64, linux-arm64, and linux-x64. These are patched versions of Bash that integrate with the execution wrapper system.

exec_wrapper.c — Execution Interception
// The EXEC_WRAPPER environment variable points to // an interceptor that wraps every execve() call int wrapped_execve(const char *path, char *const argv[], char *const envp[]) { // Check rules before execution Rule rule = check_rules(path, argv); switch (rule) { case ALLOW: return execve(path, argv, envp); case PROMPT: return request_approval(path); case FORBID: return -EPERM; } }

Rules Files

Rules files define what each binary can do at three levels:

allow

Command runs immediately without user confirmation. Used for safe, read-only operations like ls, cat, grep.

prompt

Requires explicit user approval before running. Used for write operations like git commit, npm install.

forbidden

Blocked entirely. Cannot run under any circumstances. Used for destructive operations like rm -rf /, :(){ :|:& };:

Complete Tool Call Flow

Every tool invocation follows a precise seven-step pipeline. Watch each step light up to trace a complete call from user input to returned result:

1

User Sends Message

"Create a Jira ticket for the login bug with priority High"

2

Claude Generates tool_use

Claude sees Jira tools in context, emits: {"tool": "jira_create_issue", "args": {...}}

3

Codex Receives & Routes

The tool call is intercepted. Codex looks up which MCP server registered jira_create_issue.

4

RMcpClient Forwards Call

JSON-RPC message is written to the Jira server's stdin pipe. {"jsonrpc":"2.0","method":"tools/call",...}

5

MCP Server Executes

The Jira MCP server authenticates, calls the Jira REST API, creates the ticket, receives JIRA-1234.

6

Result Returns via stdio

Server writes JSON-RPC response to stdout: {"result": {"key":"JIRA-1234","url":"..."}}

7

Claude Reads Result

The tool result is injected back into the conversation. Claude responds: "Created JIRA-1234 with priority High."

Building Your Own MCP Server

The power of MCP is that anyone can write a server. Here is a minimal Jira connector in TypeScript:

jira-mcp-server.ts
import { McpServer } from "@modelcontextprotocol/sdk"; const server = new McpServer({ name: "jira-connector", version: "1.0.0", }); server.tool( "jira_create_issue", "Create a Jira issue", { project: { type: "string" }, summary: { type: "string" }, priority: { type: "string", enum: ["High","Medium","Low"] } }, async ({ project, summary, priority }) => { const res = await fetch(`https://jira.example.com/rest/api/3/issue`, { method: "POST", headers: { Authorization: `Bearer ${process.env.JIRA_TOKEN}` }, body: JSON.stringify({ fields: { project: { key: project }, summary, priority: { name: priority } } }) }); return { content: [{ type: "text", text: JSON.stringify(await res.json()) }] }; } ); server.connect(new StdioTransport());
~/.codex/config.toml
# Register your custom MCP server [[mcp_servers]] name = "jira" command = "npx" args = ["ts-node", "~/mcp-servers/jira-mcp-server.ts"] [mcp_servers.env] JIRA_TOKEN = "${JIRA_TOKEN}"

The Ecosystem Vision

Codex MCP Client
GitHub
Slack
Jira
Datadog
Vercel
PostgreSQL
Custom MCP
Internal API

Security: The Rules Gauntlet

Extensibility without security is a vulnerability. Every tool call in Codex passes through five layers of defense before execution. Fail any layer, and the call is blocked.

1

Rules Files

Per-binary allow/prompt/forbidden rules. Loaded from .codex/rules/ and evaluated against the command's executable path and arguments.

2

Process Sandboxing

MCP servers run as isolated child processes. macOS Seatbelt and Linux seccomp profiles restrict filesystem and network access per-server.

3

Approval Workflows

Commands classified as "prompt" require explicit user confirmation in the TUI. The flow pauses and waits for [y/n] before proceeding.

4

Audit Logging

Every tool call is logged with timestamp, arguments, result, and duration. The audit trail is append-only and cannot be modified by the agent.

5

Token Management

OAuth tokens and API keys are stored in the system keychain, never in environment variables. Tokens are scoped to specific servers and auto-expire.

Defense in depth: These five layers work independently. Even if an attacker compromises one layer (e.g., injects a malicious MCP server), the remaining four layers (sandboxing, approval, audit, token scoping) contain the blast radius.