# Codex App Server JSON-RPC Protocol Implementation: Architecture and Usage

> Explore the Codex app server JSON-RPC protocol implementation's architecture and usage. Discover its JSONL stream, TypeScript definitions, and dual transport adapters for efficient communication.

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

---

**The Codex app server JSON-RPC protocol implementation uses JSON-RPC 1.0 over a line-delimited JSON stream (JSONL), featuring TypeScript protocol definitions, a base client for request correlation, and dual transport adapters for child processes and Unix domain sockets.**

The `openai/codex-plugin-cc` repository contains a lightweight client implementation that enables the Codex plugin to communicate with the app server for interactive code review and thread management. This protocol handles method invocation, error propagation, and asynchronous notifications through a structured JSON message exchange.

## Protocol Architecture and Definitions

The implementation adheres to the **JSON-RPC 1.0** specification transmitted over **JSONL** (line-delimited JSON). This transport choice enables real-time streaming communication without HTTP overhead, making it suitable for local process spawning and socket-based broker connections.

### TypeScript Protocol Definitions

All RPC methods, parameters, return types, and notification schemas are strictly defined in [`plugins/codex/scripts/lib/app-server-protocol.d.ts`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/app-server-protocol.d.ts). This file exports the `AppServerMethodMap` interface, which acts as the source of truth for available operations such as `thread/start` and their corresponding parameter types (`ThreadStartParams`).

**Key file:** [`plugins/codex/scripts/lib/app-server-protocol.d.ts`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/app-server-protocol.d.ts)

Extending the protocol requires adding entries to `AppServerMethodMap`; the base client automatically handles serialization and type enforcement based on these definitions.

## Core Client Implementation

The client architecture separates transport logic from protocol handling through inheritance, allowing the same JSON-RPC semantics to operate over different communication channels.

### Base Client: AppServerClientBase

The abstract class `AppServerClientBase` in `plugins/codex/scripts/lib/app-server.mjs` implements the core JSON-RPC state machine. It manages:

- **Request ID generation**: Monotonically increasing numeric identifiers
- **Pending request tracking**: A Map storing `{resolve, reject}` pairs keyed by request ID
- **Message parsing**: Line-by-line JSON validation and routing

**Key method:** `request(method, params)` (lines 86‑98) generates a unique ID, stores the pending promise, and writes the JSON-encoded line to the transport.

**Key method:** `handleLine` (lines 124‑160) parses incoming JSON and dispatches to:
- Response handling (lines 136‑149) for IDs without methods
- Notification handling (lines 151‑154) for methods without IDs
- Server request rejection (lines 156‑160) via `handleServerRequest`

### Transport Adapters

Two concrete subclasses extend the base client to handle specific transport mechanisms:

**SpawnedCodexAppServerClient**
- Launches a local `codex app-server` process
- Communicates over the child process's stdin/stdout pipes
- Implements `sendMessage` for stream-based writing

**BrokerCodexAppServerClient**
- Connects to a Unix-domain socket when the environment variable `CODEX_COMPANION_APP_SERVER_ENDPOINT` is set
- Used for attaching to existing broker processes
- Shares the same `sendMessage` interface but writes to a socket

**Source file:** `plugins/codex/scripts/lib/app-server.mjs` (sections `class SpawnedCodexAppServerClient` and `class BrokerCodexAppServerClient`)

## Request-Response Lifecycle

Understanding the message flow is essential for implementing custom clients or debugging communication issues.

### Connection and Initialization

The static factory method `CodexAppServerClient.connect()` (lines 352‑360) determines the transport mode based on environment variables and instantiates the appropriate adapter. After establishing the transport, the client automatically sends an `initialize` request containing `clientInfo` and `capabilities`, followed by a `client/ready` notification (lines 225‑229).

### Making RPC Requests

Call the `request` method with a method name defined in `AppServerMethodMap`:

```javascript
const thread = await client.request("thread/start", {
  title: "Bug fix session",
  language: "typescript"
});

```

The client automatically assigns a numeric ID, serializes the message to JSON, and appends a newline. The promise resolves when a matching response ID arrives or rejects if an error object is returned.

### Handling Asynchronous Messages

The protocol supports two server-initiated message types:

- **Notifications**: Messages containing a `method` field but no `id` are forwarded to the optional notification handler set via `setNotificationHandler()`
- **Server Requests**: Messages containing both `id` and `method` receive an automatic JSON-RPC error response with code `-32601` (Method Not Found), as the client does not expose callable methods to the server

## Error Handling Strategy

The implementation distinguishes between transport failures and protocol-level errors through specific error construction.

### ProtocolError Construction

The `createProtocolError()` function (lines 48‑55) generates error objects with an optional `rpcCode` property. Standard JSON-RPC error codes are used, including:
- `-32001`: Broker busy
- `-32601`: Method not found (for server requests)

**Source:** `createProtocolError` in `plugins/codex/scripts/lib/app-server.mjs`

### JSON Parsing Resilience

Malformed JSON lines trigger protocol errors with descriptive messages including the offending line content (lines 124‑128). These errors propagate through the notification stream rather than crashing the client, allowing graceful degradation during transport interruptions.

## Practical Usage Examples

### Connecting to a Spawned App Server

```javascript
import { CodexAppServerClient } from 
  "plugins/codex/scripts/lib/app-server.mjs";

async function startReview() {
  // Connect spawns a local process unless broker endpoint is set
  const client = await CodexAppServerClient.connect(process.cwd());
  
  // Handle server notifications
  client.setNotificationHandler((msg) => {
    console.log("Notification:", msg.method, msg.params);
  });
  
  // Start a review thread
  const thread = await client.request("thread/start", {
    title: "Security Review",
    language: "python"
  });
  
  console.log("Thread ID:", thread.id);
  await client.close();
}

```

### Connecting via Broker Endpoint

```javascript
// Set environment variable before importing
process.env.CODEX_COMPANION_APP_SERVER_ENDPOINT = "/tmp/codex-broker.sock";

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

// Now communicates through Unix socket instead of spawned process

```

## Summary

- **Protocol**: JSON-RPC 1.0 over JSONL in `plugins/codex/scripts/lib/app-server.mjs`
- **Definitions**: TypeScript interfaces in [`plugins/codex/scripts/lib/app-server-protocol.d.ts`](https://github.com/openai/codex-plugin-cc/blob/main/plugins/codex/scripts/lib/app-server-protocol.d.ts)
- **Transports**: Child process spawning (`SpawnedCodexAppServerClient`) and Unix socket connections (`BrokerCodexAppServerClient`)
- **Correlation**: Numeric request IDs map promises to responses via the `pending` Map in the base client
- **Errors**: Structured `ProtocolError` objects with standard JSON-RPC codes generated by `createProtocolError()`

## Frequently Asked Questions

### What version of JSON-RPC does the Codex app server use?

The implementation uses **JSON-RPC 1.0** transported over line-delimited JSON streams. This version predates the 2.0 specification but provides sufficient functionality for request/response correlation and notification handling through the `method`, `params`, `id`, and `error` fields.

### How does the client handle server-initiated requests?

When the server sends a message containing both an `id` and a `method` field, the client treats it as an incoming server request. The `handleServerRequest` method (lines 156‑160) automatically responds with a JSON-RPC error object containing code `-32601` (Method Not Found), as the client implementation does not expose callable methods to the server.

### What transport mechanisms are supported?

The protocol supports two transport mechanisms: **stdin/stdout pipes** via `SpawnedCodexAppServerClient` for locally spawned processes, and **Unix domain sockets** via `BrokerCodexAppServerClient` when the `CODEX_COMPANION_APP_SERVER_ENDPOINT` environment variable points to a socket path. Both extend the same base class and implement the abstract `sendMessage` method.

### Where are protocol errors defined and handled?

Protocol errors are constructed by the `createProtocolError()` function in `plugins/codex/scripts/lib/app-server.mjs` (lines 48‑55). These errors include an optional `rpcCode` field following JSON-RPC conventions. Transport-level JSON parsing failures are caught in the `handleLine` method and converted to protocol errors containing the raw line content for debugging purposes.