Local Codex App Server + persistent bridge experiment.
This project explores a local integration path:
AI client / MCP client
│ MCP stdio
▼
core/codex_mcp_bridge.py
│ WebSocket
▼
Codex App Server
│
▼
Local project workspace
The MCP bridge exposes ask_codex(prompt: str). It forwards requests to a local Codex App Server and returns the final text response.
The project also contains a long-running persistent service:
tests/persistent_test.py
│ TCP JSON Lines :4600
▼
core/persistent_bridge.py
│ WebSocket :4500
▼
Codex App Server
The persistent service keeps one CodexSession alive between requests. The first request creates a thread; later requests reuse the WebSocket connection and thread_id.
codex-mcp-persistent-bridge/
├── core/
│ ├── codex_mcp_bridge.py
│ └── persistent_bridge.py
├── tests/
│ └── persistent_test.py
├── docs/
│ └── Codex_MCP_Bridge_Development_Log.md
├── README.md
├── requirements.txt
├── start_persistent_bridge.ps1
└── .gitignore
Runtime logs are written under logs/ when the startup script uses redirected output. The directory and log files are excluded from Git.
Open a terminal at the project root and create an environment if needed:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txtThe startup script currently uses the Python interpreter from the verified local setup. When moving the project to another machine, review that interpreter setting before starting the service.
From the project root:
.\start_persistent_bridge.ps1The script:
- Starts
codex app-server --listen ws://127.0.0.1:4500when the App Server is not already listening. - Waits for TCP port
4500. - Starts
core/persistent_bridge.py. - Waits for TCP port
4600. - Prints the process IDs and keeps both child processes alive.
Press Ctrl+C in the startup window to stop the child processes started by the script.
With the service running, open another terminal at the project root:
python .\tests\persistent_test.pyThe test sends three requests over one TCP connection. Expected diagnostic behavior is:
CALL 0
current thread_id = None
created new thread_id = <id>
CALL 1
current thread_id = <id>
CALL 2
current thread_id = <id>
The exact ID is generated by Codex. The important result is that the ID remains the same for all three calls.
core/codex_mcp_bridge.py remains the standard MCP entry point and exposes ask_codex(prompt: str). A compatible MCP host should launch it with its configured Python interpreter and a project-root-relative configuration, for example:
{
"mcpServers": {
"codex-mcp-bridge": {
"command": "<python-interpreter>",
"args": ["<project-root>/core/codex_mcp_bridge.py"],
"env": {
"CODEX_APP_SERVER_URL": "ws://127.0.0.1:4500",
"CODEX_MCP_WORKDIR": "<project-root>"
}
}
}
}The stdio entry point and the long-running TCP service are separate launch modes. The persistent service exists to keep the Session alive after a short-lived client exits; it does not change the MCP tool interface or the WebSocket protocol.
- The project is a local Codex App Server and MCP bridge experiment, not a hosted service.
- ChatGPT Desktop has not been verified to provide a direct local MCP connection for this project.
- ChatGPT Web cannot automatically access a local process just because the process listens on
127.0.0.1. - The persistent service currently uses a local TCP JSON Lines interface; it is not itself a replacement for the standard MCP stdio entry point.
- The current setup assumes the local Codex CLI, Python dependencies, permissions and App Server are available in the host environment.
- The bridge uses local execution settings that should be reviewed before any broader or write-enabled deployment.
The engineering history, failed attempts and debugging conclusions are recorded in docs/Codex_MCP_Bridge_Development_Log.md.