# How Claude Code Broker Communicates with Codex: JSON-RPC Architecture Explained

> Discover how Claude Code Broker communicates with Codex using JSON-RPC over sockets. Learn about this architecture for efficient inter-process communication.

- Repository: [OpenAI/codex-plugin-cc](https://github.com/openai/codex-plugin-cc)
- Tags: internals
- Published: 2026-07-07

---

**Claude Code communicates with Codex through a dedicated broker process that multiplexes JSON-RPC messages over platform-specific sockets, enforcing single-client semantics while allowing interrupt requests to pass through.**

The `openai/codex-plugin-cc` repository implements a sophisticated inter-process communication layer that allows Claude Code to manage Codex CLI sessions reliably. This architecture uses a broker pattern to handle concurrency, lifecycle management, and transport abstraction across Unix sockets and Windows named pipes.

## Broker Endpoint Creation

Communication begins with establishing a persistent transport channel between Claude Code and the Codex runtime.

### Platform-Specific Endpoints

When a Claude Code session initiates, the plugin constructs a platform-specific endpoint using `createBrokerEndpoint()` in `plugins/codex/scripts/lib/broker-endpoint.mjs`. On macOS and Linux, this creates a Unix socket; on Windows, it generates a named pipe. The endpoint path is typically derived from the session directory to ensure isolation between concurrent projects.

```javascript
import { createBrokerEndpoint } from './lib/broker-endpoint.mjs';

const endpoint = createBrokerEndpoint(sessionDir);
// Returns: unix:/tmp/session/broker.sock (Unix) or named pipe path (Windows)

```

The broker endpoint serves as the persistent address where the broker process listens for incoming JSON-RPC connections from Claude Code clients.

## The Broker Process

The core multiplexing logic resides in `plugins/codex/scripts/app-server-broker.mjs`, which implements the broker process that mediates all communication between Claude Code and the Codex App-Server.

### Starting the Broker

The broker is launched as a separate Node.js process that binds to the previously created endpoint and manages the lifecycle of the underlying Codex App-Server:

```bash
node plugins/codex/scripts/app-server-broker.mjs serve \
  --endpoint unix:/tmp/session/broker.sock \
  --pid-file /tmp/session/broker.pid

```

This process maintains a single active connection to the Codex App-Server (spawned via `codex app-server`) while exposing a socket interface to Claude Code clients.

### Request Routing and Protocol Handling

The broker implements a line-delimited JSON-RPC protocol. Each message is newline-separated, allowing for simple parsing via standard stream interfaces. The broker handles three distinct message types:

- **Initialization requests**: Returns a static `initialize` response identifying the broker as `codex-companion-broker`
- **Method requests**: Forwards calls to the underlying Codex App-Server and streams responses back
- **Notifications**: Routes asynchronous events from Codex back to the active client socket

```javascript
// Simplified broker request handling
if (message.id !== undefined && message.method === 'initialize') {
  send(socket, { 
    id: message.id, 
    result: { userAgent: 'codex-companion-broker' } 
  });
}

```

### Single-Client Enforcement and Interrupts

The broker enforces strict single-client semantics to prevent request interleaving. If a second client attempts to send a request while another is active, the broker returns `BROKER_BUSY_RPC_CODE` (`-32001`):

```javascript
if (activeRequestSocket && activeRequestSocket !== socket) {
  send(socket, { 
    id: message.id, 
    error: buildJsonRpcError(
      BROKER_BUSY_RPC_CODE, 
      'Shared Codex broker is busy.'
    ) 
  });
}

```

However, the architecture permits `turn/interrupt` requests to bypass this restriction, allowing Claude Code to cancel in-flight operations even when the broker is ostensibly busy with a streaming response.

## Client-Side Integration

Claude Code connects to the broker through the `CodexAppServerClient` class defined in `plugins/codex/scripts/lib/app-server.mjs`.

### Connection Establishment

The client checks for the `CODEX_COMPANION_APP_SERVER_ENDPOINT` environment variable or accepts an endpoint parameter from the broker lifecycle manager. When a broker endpoint is present, the client instantiates a `BrokerCodexAppServerClient` that connects to the Unix socket or named pipe:

```javascript
import { CodexAppServerClient } from './lib/app-server.mjs';

const client = await CodexAppServerClient.connect(cwd, {
  env: process.env,
  reuseExistingBroker: true,
});

// Send requests to Codex through the broker
const result = await client.request('review/start', { /* payload */ });

```

If no broker endpoint is available, the client falls back to spawning a fresh Codex App-Server process directly, maintaining compatibility with standalone usage.

### Lifecycle Management

The `plugins/codex/scripts/lib/broker-lifecycle.mjs` module handles broker persistence across Claude Code sessions. It manages the creation of PID files, socket cleanup on shutdown, and process termination using `plugins/codex/scripts/lib/process.mjs` for cross-platform process tree management.

## Practical Usage Example

To integrate Claude Code with Codex through the broker architecture:

1. **Start the broker** with a specific endpoint and PID file:

```bash
node plugins/codex/scripts/app-server-broker.mjs serve \
  --endpoint unix:/tmp/codex/broker.sock \
  --pid-file /tmp/codex/broker.pid

```

2. **Connect from your application** using the client library:

```javascript
const client = await CodexAppServerClient.connect(process.cwd(), {
  env: { 
    ...process.env, 
    CODEX_COMPANION_APP_SERVER_ENDPOINT: 'unix:/tmp/codex/broker.sock' 
  },
  reuseExistingBroker: true,
});

// Execute Codex commands
const review = await client.request('review/start', { path: './src' });

```

3. **Gracefully shut down** the broker when finished:

```javascript
await client.request('broker/shutdown', {});
process.exit(0);

```

## Summary

- **Transport layer**: The broker uses platform-specific sockets (Unix sockets on macOS/Linux, named pipes on Windows) created via `createBrokerEndpoint()` in `plugins/codex/scripts/lib/broker-endpoint.mjs`.
- **Protocol**: All communication uses newline-delimited JSON-RPC, with the broker parsing messages in `plugins/codex/scripts/app-server-broker.mjs`.
- **Concurrency model**: Single-client semantics prevent request interleaving, returning error code `-32001` for busy states, while allowing `turn/interrupt` requests to cancel active operations.
- **Client integration**: `CodexAppServerClient.connect()` in `plugins/codex/scripts/lib/app-server.mjs` automatically detects broker availability via environment variables and handles connection fallback.
- **Lifecycle**: The broker maintains PID files and performs cleanup through `broker-lifecycle.mjs` and `process.mjs`.

## Frequently Asked Questions

### What protocol does Claude Code use to communicate with the Codex broker?

Claude Code communicates with the Codex broker using **JSON-RPC 2.0** over line-delimited streams. Each request and response is a complete JSON object terminated by a newline character, transmitted through either Unix sockets (macOS/Linux) or named pipes (Windows). This protocol allows for bidirectional communication, including method calls from Claude Code to Codex and asynchronous notifications from Codex back to Claude Code.

### Why does the broker enforce single-client semantics?

The broker enforces single-client semantics to prevent interleaved JSON-RPC requests from corrupting the state of the underlying Codex App-Server. When a second client attempts to send a request while another is active, the broker returns error code `-32001` (`BROKER_BUSY_RPC_CODE`). However, the architecture specifically exempts `turn/interrupt` requests from this restriction, allowing Claude Code to cancel in-flight operations through a separate socket connection even while a streaming response is in progress.

### How does Claude Code find an existing broker process?

Claude Code locates existing brokers through the `CODEX_COMPANION_APP_SERVER_ENDPOINT` environment variable or through the broker lifecycle management logic in `plugins/codex/scripts/lib/broker-lifecycle.mjs`. When `CodexAppServerClient.connect()` is called with `reuseExistingBroker: true`, it checks for existing socket files and PID files to determine if a broker is already running for the current session, avoiding unnecessary process duplication.

### What happens if the broker process crashes during a Codex operation?

If the broker process crashes or is terminated unexpectedly, the underlying Codex App-Server process may continue running but becomes unreachable. The `CodexAppServerClient` detects connection failures when attempting to read from or write to the socket. On the next operation, if the broker endpoint is no longer responsive, the client can fall back to spawning a new Codex App-Server directly, though any in-flight operations from the crashed session will be lost and must be retried.