Async Python client for Codex app-server over stdio and websocket.
Project description
codex-app-server-client
High-level async Python client for codex app-server.
It gives you a convenient conversation API over stdio or websocket without having to manage raw protocol events yourself.
Highlights
- simple one-shot turns with
chat_once(...) - step-streaming turns with
chat(...)(thinking,exec,codex, etc.), non-delta - built-in thread/turn lifecycle handling
- inactivity timeout continuation for long-running turns
- turn cancellation with unread-step/event drain via
cancel(...) - optional low-level
request(...)access when needed
Install
uv sync
Quick start
Stdio
import asyncio
from codex_app_server_client import CodexClient
async def main() -> None:
client = await CodexClient.connect_stdio()
try:
result = await client.chat_once("Hello from Python")
print(result.final_text)
finally:
await client.close()
asyncio.run(main())
By default, stdio transport runs:
- command:
codex app-server
You can override via:
connect_stdio(command=[...])- environment variable:
CODEX_APP_SERVER_CMD
Websocket
import asyncio
from codex_app_server_client import CodexClient
async def main() -> None:
client = await CodexClient.connect_websocket()
try:
result = await client.chat_once("Hello over websocket")
print(result.final_text)
finally:
await client.close()
asyncio.run(main())
Websocket defaults:
- URL:
CODEX_APP_SERVER_WS_URLorws://127.0.0.1:8765 - Bearer token:
CODEX_APP_SERVER_TOKEN(optional)
Continuation on inactivity timeout
Both high-level APIs support resuming the same running turn.
import asyncio
from codex_app_server_client import CodexClient, CodexTurnInactiveError
async def main() -> None:
client = await CodexClient.connect_stdio(inactivity_timeout=120.0)
try:
continuation = None
while True:
try:
if continuation is None:
result = await client.chat_once("Do a longer task")
else:
result = await client.chat_once(continuation=continuation)
print(result.final_text)
break
except CodexTurnInactiveError as exc:
continuation = exc.continuation
finally:
await client.close()
asyncio.run(main())
Example clients
More complete examples are under examples/.
Rich step-stream example (thinking/exec/codex blocks)
Recommended example for step-oriented API and continuation behavior.
Stdio:
uv run python examples/chat_steps_rich.py
Websocket:
uv run python examples/chat_steps_rich.py --transport websocket --url ws://127.0.0.1:8765
With extra payload summaries:
uv run python examples/chat_steps_rich.py --show-data
Cancel timed-out turns instead of auto-resume:
uv run python examples/chat_steps_rich.py --cancel-on-timeout
Common options:
--transport {stdio,websocket}--cmd "codex app-server"(stdio mode)--url ws://127.0.0.1:8765(websocket mode)--token "$CODEX_APP_SERVER_TOKEN"(websocket mode)--prompt "..."--user "..."--inactivity-timeout 120--show-data--cancel-on-timeout
Stdio example (multi-turn, one thread)
uv run python examples/chat_session_stdio.py
Custom command and prompts:
uv run python examples/chat_session_stdio.py \
--cmd "codex app-server" \
--prompt "First prompt" \
--prompt "Second prompt"
Websocket example (multi-turn, one thread)
uv run python examples/chat_session_websocket.py
With explicit endpoint/token:
uv run python examples/chat_session_websocket.py \
--url ws://127.0.0.1:8765 \
--token "$CODEX_APP_SERVER_TOKEN"
Or via environment:
export CODEX_APP_SERVER_WS_URL=ws://127.0.0.1:8765
export CODEX_APP_SERVER_TOKEN=your-token
uv run python examples/chat_session_websocket.py
API reference (quick)
CodexClient (src/codex_app_server_client/client.py)
connect_stdio(...): create + connect client over subprocess stdio.connect_websocket(...): create + connect client over websocket.start(): connect transport and start receive loop.initialize(params=None, timeout=None): perform JSON-RPC initialize handshake.request(method, params=None, timeout=None): low-level JSON-RPC request helper.chat(text=None, thread_id=None, user=None, metadata=None, inactivity_timeout=None, continuation=None): async iterator yielding completed non-delta step blocks.chat_once(text=None, thread_id=None, user=None, metadata=None, inactivity_timeout=None, continuation=None): send one user message and wait for completed turn.cancel(continuation, timeout=None): interrupt running turn, return unread steps/events, and clean turn state.interrupt_turn(turn_id, timeout=None): low-level turn interruption request.close(): cancel receive loop and close transport.
Transport and implementations (src/codex_app_server_client/transport.py)
Transport.connect/send/recv/close: abstract interface.StdioTransport: line-delimited JSON over subprocess stdin/stdout.WebSocketTransport: JSON messages over websocket frames.
Data models (src/codex_app_server_client/models.py)
InitializeResult: parsed initialize response (protocol_version,server_info,capabilities,raw).ConversationStep: completed step fromchat(...)(step_type,item_type,text,item_id,thread_id,turn_id,data).ChatResult: buffered turn output (thread_id,turn_id,final_text,raw_events,assistant_item_id,completion_source).ChatContinuation: continuation token for timed-out running turns (thread_id,turn_id,cursor,mode).CancelResult: cancellation result with unreadsteps/raw_eventsplus terminal flags.
Exceptions (src/codex_app_server_client/errors.py)
CodexError: base exception.CodexTransportError: transport/connectivity problems.CodexTimeoutError: request timeout (and base for timeout-related flow).CodexTurnInactiveError: per-turn inactivity timeout with resumablecontinuation.CodexProtocolError: protocol/JSON-RPC error (optionalcodeanddata).
Behavior notes
- This version does not expose token-delta streaming as a public API.
chat(...)provides async streaming of completed step blocks (non-delta).chat_once(...)resolves final text from completedagentMessageitems (item/completed), withthread/read(includeTurns=true)fallback.turn_timeoutis intentionally removed to avoid conflicting timeout semantics.- Turn waits are controlled by
inactivity_timeout(or unbounded whenNone). cancel(...)interrupts a continuation turn, returns unread buffered data, and cleans internal session state so the same thread can be reused safely.- The client uses modern thread/turn methods (
thread/start,thread/resume,turn/start,turn/interrupt). initializecurrently sendsprotocolVersion: "1"as handshake metadata.- Websocket transport targets
websockets(>=16,<17), usesadditional_headers, and disables compression by default (compression=None) for codex app-server compatibility. - After dependency changes, run
uv syncto refresh the virtual environment.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file codex_app_server_sdk-0.1.0.tar.gz.
File metadata
- Download URL: codex_app_server_sdk-0.1.0.tar.gz
- Upload date:
- Size: 92.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
783f2cf46364e8e5f7973c6b31e251480b95054ca4da248afd1943b9e607821c
|
|
| MD5 |
10dd2286c94743188cde2f10575e98b7
|
|
| BLAKE2b-256 |
fb262da048ff9a602f17cb4eb076a65dc45aa2be54cfe2f5277b00e4140890f9
|
File details
Details for the file codex_app_server_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: codex_app_server_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 41.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
842875075aec19074044c2dc43626832e207133e7cf5f8385d819e054e46dfd7
|
|
| MD5 |
1b3900e7b1878cea11badf08ed7456b4
|
|
| BLAKE2b-256 |
2cb5472ace7bac34294605f648806e449fca0f7952cef965fc2119a5b7beb147
|