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.