DEV Community

Cover image for Controlling Codex via JSON-RPC with Codex App Server
jjoo
jjoo

Posted on

Controlling Codex via JSON-RPC with Codex App Server

Have you heard of Codex App Server?

Instead of interacting with Codex through the CLI, Codex App Server lets you control a Codex agent over JSON-RPC.

In other words, you can build your own GUI, TUI, task queue, or orchestration layer on top of Codex.

This article focuses on keeping a fast pace, so if you're looking for precise protocol details and specifications, please refer to the official README:
https://github.com/openai/codex/blob/main/codex-rs/app-server/README.md

Starting Codex App Server is as simple as running:

codex app-server
Enter fullscreen mode Exit fullscreen mode

That's it.

The server will begin listening on standard input. From there, you simply send JSON requests that follow the protocol (technically JSONL, so each request must be on a single line).

This allows your own applications to launch and control Codex agents programmatically through Codex App Server.

Conversation Flow

A typical interaction looks like this:

  1. Send an initialize request to initialize the server.
  2. Start an agent conversation with thread/start.
  3. Give the agent work using turn/start.
  4. Wait until you receive turn/completed.
  5. Repeat steps 3–4 until the task is finished.
  6. Done.

Understanding Thread and Turn

When I first started learning the protocol, I found it difficult to picture the relationship between Thread and Turn.

The following example helped everything click:

Thread
├── Turn 1
│   User:
│     "Please investigate this issue."
│
│   Agent:
│     - Search the codebase
│     - Read relevant files
│     - Identify the root cause
│
│   turn/completed
│
├── Turn 2
│   User:
│     "Please fix it."
│
│   Agent:
│     - Edit files
│     - Run tests
│
│   turn/completed
│
├── Turn 3
│   User:
│     "Create a pull request."
│
│   Agent:
│     - Commit changes
│     - Push to Git
│     - Create a PR
│
│   turn/completed
│
└── End of Thread
Enter fullscreen mode Exit fullscreen mode

A Thread is roughly equivalent to a Codex CLI session, while a Turn represents a single exchange between the user and the agent.

Hands-on

By default, Codex App Server accepts JSON-RPC messages through standard input.

If you copy and paste the following JSON into a running server (after converting it into a single-line JSONL message), you'll be able to interact with a persistent Codex agent.

{
  "id": 1,
  "method": "initialize",
  "params": {
    "clientInfo": {
      "name": "tutorial",
      "title": "Tutorial",
      "version": "1.0"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

After sending it, you'll receive a response similar to this:

% codex app-server
{"id":1,"method":"initialize","params":{"clientInfo":{"name":"tutorial","title":"Tutorial","version":"1.0"}}}
{"id":1,"result":{"userAgent":"tutorial/0.144.5 (Mac OS 26.5.0; arm64) xterm-256color (tutorial; 1.0)","codexHome":"~/.codex","platformFamily":"unix","platformOs":"macos"}}
{"method":"remoteControl/status/changed","params":{"status":"disabled","serverName":"xxx.local","installationId":"2124e102-01b2-4b60-94c3-dabb22d2913f","environmentId":null}}
Enter fullscreen mode Exit fullscreen mode

At this point, the server has been successfully initialized.

Next, start a conversation with an agent by sending a thread/start request:

{"id":2,"method":"thread/start","params":{}}
Enter fullscreen mode Exit fullscreen mode

The response will look something like this:

{"id":2,"result":{"thread":{"id":"019f7960-95a5-7140-8749-a3cf59e0ffab","extra":null,"sessionId":"019f7960-95a5-7140-8749-a3cf59e0ffab","forkedFromId":null,"parentThreadId":null,"preview":"","ephemeral":false,"historyMode":"legacy","modelProvider":"openai","createdAt":1784447800,"updatedAt":1784447800,"recencyAt":1784447800, ...}
{"method":"thread/started","params":{"thread":{"id":"019f7960-95a5-7140-8749-a3cf59e0ffab","extra":null,"sessionId":"019f7960-95a5-7140-8749-a3cf59e0ffab", ...}
{"method":"mcpServer/startupStatus/updated","params":{"threadId":"019f7960-95a5-7140-8749-a3cf59e0ffab","name":"codex_apps","status":"starting","error":null,"failureReason":null}}
{"method":"mcpServer/startupStatus/updated","params":{"threadId":"019f7960-95a5-7140-8749-a3cf59e0ffab","name":"context7","status":"starting","error":null,"failureReason":null}}
{"method":"mcpServer/startupStatus/updated","params":{"threadId":"019f7960-95a5-7140-8749-a3cf59e0ffab","name":"notion-mcp-server","status":"starting","error":null,"failureReason":null}}
{"method":"mcpServer/startupStatus/updated","params":{"threadId":"019f7960-95a5-7140-8749-a3cf59e0ffab","name":"notion-mcp-server","status":"ready","error":null,"failureReason":null}}
{"method":"mcpServer/startupStatus/updated","params":{"threadId":"019f7960-95a5-7140-8749-a3cf59e0ffab","name":"codex_apps","status":"ready","error":null,"failureReason":null}}
{"method":"mcpServer/startupStatus/updated","params":{"threadId":"019f7960-95a5-7140-8749-a3cf59e0ffab","name":"context7","status":"ready","error":null,"failureReason":null}}
Enter fullscreen mode Exit fullscreen mode

The output may look a little noisy, but you'll notice events such as thread/started, followed by the startup of MCP servers.

If you've used the codex command before, this should feel very familiar—the same initialization process is happening behind the scenes.

Next, let's send a task to the agent using turn/start.

For this example, we'll ask it to introduce itself with the following prompt:

{"id":3,"method":"turn/start","params":{"threadId":"019f7960-95a5-7140-8749-a3cf59e0ffab","input":[{"type":"text","text":"Hello! Please introduce yourself."}]}}
Enter fullscreen mode Exit fullscreen mode

The response is quite long, so I'll omit most of it here. The message worth paying attention to is this one:

{
  "method": "item/completed",
  "params": {
    "item": {
      "type": "agentMessage",
      "id": "msg_0e30b319d2fc7416016a5c854b85a0819192efde569032bd15",
      "text": "Hello! I'm Codex.\n\nI can help with code investigation, implementation, testing, code reviews, Git operations, pull request preparation, and more within this workspace. My general approach is to understand the existing architecture and repository conventions before making small, safe changes.\n\nFor this repository, I'll follow your instructions and respond primarily in Japanese.",
      "phase": "final_answer",
      "memoryCitation": null
    },
    "threadId": "019f7960-95a5-7140-8749-a3cf59e0ffab",
    "turnId": "019f7968-a116-7f13-bf24-c56fddd4373d",
    "completedAtMs": 1784448333474
  }
}
Enter fullscreen mode Exit fullscreen mode

The response is divided into several types of events.

The most important one is item/agentMessage/delta, which streams the agent's response in small chunks as it is generated. Once the response is complete, the full message is available in the item/completed event.

At this point, you've seen the complete interaction flow. From here on, you simply continue sending turn/start requests to assign new tasks. By repeating this process until all work is finished, you can drive the agent through an entire workflow programmatically.

Why Use Codex App Server?

When working with Codex day to day, you typically launch the interactive console with the codex command.

However, if you keep a Codex App Server running locally and send JSONL requests to it instead, you unlock a number of interesting possibilities:

  • Run Codex agents in headless mode.
  • Send tasks directly to the App Server without opening a new codex session and manually entering prompts for each task.
  • Build your own custom clients, such as GUIs, TUIs, or web applications.

OpenAI has also introduced Codex Symphony](https://openai.com/ja-JP/index/open-source-codex-orchestration-symphony/), a specification for orchestrating multiple coding agents. Building your own orchestration layer on top of Codex App Server would be an interesting project.

Since Codex App Server continuously emits events that reflect the agent's state, you can also monitor its progress in real time. This opens the door to building tools similar to Herdr, with features for tracking and visualizing the state of long-running agent workflows.

Tasq: An Open-Source Project I Built

As a bit of self-promotion, I'd also like to introduce Tasq, an open-source project I built on top of Codex App Server.

If you're curious about how it works, I recommend checking out the demo video in the README—it gives a good sense of the overall workflow and user experience.

Tasq is built around local issue management. Once you add implementation plans created by tools like Claude Code or Codex to your backlog, Tasq can automatically drive coding agents through the entire workflow—from implementation all the way to creating a pull request.

Thanks for reading!

If Tasq sounds interesting, I'd love for you to give it a try. Feedback, issues, and contributions are always welcome.

Top comments (1)

Collapse
 
marcusykim profile image
Marcus Kim

The Thread/Turn split is the detail that makes this protocol usable: investigation, implementation, and pull-request creation become separate reviewable boundaries instead of one opaque run. I also like the distinction between noisy MCP startup events, streaming item/agentMessage/delta updates, and the final item/completed payload; a client that collapses those states will either feel unresponsive or lose durable state. For a founder or engineer, the key product decision is where to put approval gates-headless execution is powerful, but autonomy only compounds when each turn has explicit cancellation, permissions, and auditability.