SDKs, APIs, and What's Next for AI-Powered Development
Across seven articles, we've disassembled every major subsystem of the OpenAI Codex CLI. We traced signals from keystroke to kernel call, watching how a natural-language prompt becomes validated shell commands executing inside hardened sandboxes. We studied the event queues that keep the UI responsive, the approval policies that keep the system safe, and the multi-model reasoning architecture that keeps the answers sharp.
Now we arrive at the translation layer—the SDKs and APIs that let you build on top of all that machinery. If the CLI is the reference implementation, the SDKs are the contracts that let you automate, extend, and embed it in your own workflows.
The TypeScript SDK is the most direct interface to the Codex engine. It mirrors the internal architecture
closely, exposing three core abstractions: Codex (the client), CodexExec (a configured
execution environment), and Thread (a conversation with the model).
// Three levels of abstraction const codex = new Codex({ apiKey, model }); // CodexExec: configured with sandbox policy + working dir const exec = codex.exec({ workingDir: "/path/to/repo", sandbox: "seatbelt", approvalPolicy: "auto-edit", }); // Thread: a live conversation const thread = exec.startThread("Fix the failing unit tests"); // or resume an existing one const resumed = exec.resumeThread(threadId);
The SDK offers two execution modes. run() returns a Promise<ThreadResult>—you
get the final answer when the agent is done. runStreamed() returns an
AsyncGenerator<ThreadEvent>, yielding events as they happen. This is the key to building
responsive UIs and automation pipelines.
| Event | When It Fires |
|---|---|
ThreadStarted | Thread is initialized, model connection established |
TurnStarted | A new agent turn begins (prompt sent to model) |
ItemStarted | The model begins producing a new item (message, tool call, etc.) |
ItemUpdated | Streaming delta—partial text, partial tool arguments |
ItemCompleted | An item is fully resolved |
TurnCompleted | All items for this turn are done |
Each Item inside an event is one of these discriminated union variants:
import { Codex } from "@openai/codex"; async function fixTodos(repoPath: string) { const codex = new Codex({ model: "o4-mini" }); const exec = codex.exec({ workingDir: repoPath, approvalPolicy: "auto-edit", }); const stream = exec.runStreamed( "Find all TODO comments and fix the ones that " + "describe clearly actionable bugs." ); for await (const event of stream) { if (event.type === "ItemCompleted") { const item = event.item; if (item.kind === "FileChange") { console.log(`Modified: ${item.path}`); } if (item.kind === "CommandExecution") { console.log(`Ran: ${item.command} (exit ${item.exitCode})`); } } } }
Key insight: Because the stream is an AsyncGenerator, you can break out of it
at any time, implement backpressure, or pipe events into another system. The agent keeps running until you stop
consuming or the turn completes.
The Python SDK takes a fundamentally different architectural approach. Instead of direct library bindings, it communicates with the app-server binary via JSON-RPC over stdio. The binary is the same compiled Rust/TypeScript core that powers the CLI—the Python layer is a thin typed wrapper around a subprocess pipe.
┌──────────────────┐ stdio (JSON-RPC) ┌──────────────────┐ │ Python Client │ ◄──────────────────► │ app-server bin │ │ (Pydantic) │ req/res + events │ (Rust/TS core) │ └──────────────────┘ └──────────────────┘ Auto-generated Same sandbox, from protocol same model layer, schema same everything
All the Pydantic models—ThreadEvent, Item, ExecPolicy—are
auto-generated from the protocol schema. This means the Python types are always in sync
with the TypeScript types, and you get full IDE autocompletion and type checking.
from codex_sdk import CodexClient # Context manager spawns/tears down the app-server async with CodexClient(model="o4-mini") as client: result = await client.run( prompt="Refactor auth module to use JWT", working_dir="/path/to/repo", approval_policy="auto-edit", ) for change in result.file_changes: print(f" {change.path}: {change.action}")
import asyncio from codex_sdk import CodexClient async def generate_tests(modules: list[str]): """Generate test files for multiple modules in parallel.""" async with CodexClient(model="o4-mini") as client: tasks = [ client.run( prompt=f"Write comprehensive unit tests for {mod}", working_dir="/path/to/repo", approval_policy="full-auto", ) for mod in modules ] results = await asyncio.gather(*tasks) for mod, result in zip(modules, results): print(f"{mod}: {len(result.file_changes)} files created") asyncio.run(generate_tests(["auth", "billing", "notifications"]))
// Direct library binding const codex = new Codex({...}); const exec = codex.exec({...}); // Streaming via AsyncGenerator for await (const ev of stream) { // handle event } // Manual lifecycle thread.stop(); // Types: hand-written TS // Transport: in-process
# JSON-RPC over stdio async with CodexClient() as c: result = await c.run(...) # Streaming via async iterator async for ev in c.stream(...): # handle event # Auto lifecycle (context mgr) # exits on __aexit__ # Types: Pydantic (auto-gen) # Transport: subprocess pipe
The Model Context Protocol (MCP) is the plug-in architecture for Codex's tool ecosystem. By building a custom MCP server, you give the agent access to any capability—databases, APIs, hardware devices, internal services—without modifying the Codex core.
import { McpServer } from "@modelcontextprotocol/sdk"; import Database from "better-sqlite3"; const db = new Database("./analytics.db"); const server = new McpServer({ name: "sqlite-query", version: "1.0.0", }); server.tool( "query", "Run a read-only SQL query against the analytics database", { sql: { type: "string", description: "SELECT query to execute" }, }, async ({ sql }) => { // Safety: only allow SELECT statements if (!sql.trim().toUpperCase().startsWith("SELECT")) { throw new Error("Only SELECT queries are allowed"); } const rows = db.prepare(sql).all(); return { content: [{ type: "text", text: JSON.stringify(rows, null, 2), }], }; } ); server.listen(); // stdio transport
# Register with the --mcp-server flag $ codex --mcp-server sqlite-query --mcp-server-cmd "node ./sqlite-server.js" # Or in .codex/config.json { "mcpServers": { "sqlite-query": { "command": "node", "args": ["./sqlite-server.js"] } } }
Once registered, the agent can invoke query like any other tool. The MCP protocol handles
discovery, schema negotiation, and invocation—you just implement the business logic.
Regardless of whether you use Codex, its codebase is a masterclass in building robust agent systems. Here are four patterns that deserve to be extracted and reused.
Eight articles. Thousands of lines of source examined. Here is the complete mental model we've built:
The Complete Mental Model
The throughline: Every design decision in Codex optimizes for the same thing—giving an AI agent maximum capability within minimum trust boundaries. Power without peril. Autonomy within constraints. This is the template for every agent system that will follow.
The architecture we've studied isn't just a snapshot—it's a forward-compatible foundation. Here's what the next generation will bring, and why the current design is ready for it.
Longer chains of thought, deeper planning, fewer hallucinations. The agent loop doesn't change—only the model behind the port.
MCP servers for every API, every database, every cloud service. The plug-in architecture scales without touching the core.
Nested SQ/EQ pairs already support sub-agents. The next step is agents that delegate, review each other's work, and merge results.
Organization-specific models that know your codebase conventions. The model provider port makes swapping seamless.
Proving that sandbox policies enforce what they claim. Policy-as-code makes this tractable—you can verify data, not code paths.
Running agents on local hardware, edge devices, or distributed clusters. The hexagonal architecture already decouples compute from interface.
Notice the pattern: every future capability maps to an existing port in the architecture. Better models? Swap the model adapter. More tools? Add MCP servers. Multiple agents? Nest the event queues. This is the power of getting the architecture right from the start.
We set out to understand how an AI coding agent really works—not the marketing pitch, not the demo, but the actual engineering. What we found was a system built with the same principles that make any great software great: clear boundaries, explicit contracts, defense in depth, and composable abstractions.
The Codex CLI is not magic. It is event queues and sandbox profiles and policy engines and model providers, wired together with discipline and taste. And because it's open source, because it runs on your machine, because you can read every line—it's a system you can understand.
Understanding is the prerequisite to trust. Trust is the prerequisite to delegation. And delegation—giving an AI agent real autonomy over your code—is where we're all heading.
The future of AI coding isn't in cloud-hosted black boxes.
It's in local, transparent systems you understand and control.