codex-cli-mcp-slim 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- codex_cli_mcp_slim-0.1.0/LICENSE +21 -0
- codex_cli_mcp_slim-0.1.0/PKG-INFO +354 -0
- codex_cli_mcp_slim-0.1.0/README.md +319 -0
- codex_cli_mcp_slim-0.1.0/pyproject.toml +81 -0
- codex_cli_mcp_slim-0.1.0/setup.cfg +4 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim/__init__.py +8 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim/py.typed +0 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim/server.py +699 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim.egg-info/PKG-INFO +354 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim.egg-info/SOURCES.txt +16 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim.egg-info/dependency_links.txt +1 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim.egg-info/entry_points.txt +2 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim.egg-info/requires.txt +8 -0
- codex_cli_mcp_slim-0.1.0/src/codex_cli_mcp_slim.egg-info/top_level.txt +1 -0
- codex_cli_mcp_slim-0.1.0/tests/test_basic.py +123 -0
- codex_cli_mcp_slim-0.1.0/tests/test_call_tool.py +312 -0
- codex_cli_mcp_slim-0.1.0/tests/test_subprocess_stdin_isolation.py +72 -0
- codex_cli_mcp_slim-0.1.0/tests/test_timeout_handling.py +249 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tksfjt1024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codex-cli-mcp-slim
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A thin, auditable MCP server wrapping the Codex CLI (codex exec). Same tools as the deprecated `codex mcp-server`; forward-compatible with future CLI flags via extra_args passthrough.
|
|
5
|
+
Author-email: tksfjt1024 <tksfjt1024@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/tksfjt1024/codex-cli-mcp-slim
|
|
8
|
+
Project-URL: Repository, https://github.com/tksfjt1024/codex-cli-mcp-slim
|
|
9
|
+
Project-URL: Issues, https://github.com/tksfjt1024/codex-cli-mcp-slim/issues
|
|
10
|
+
Keywords: mcp,model-context-protocol,codex,codex-cli,openai,claude,anthropic,ai,llm,coding-agent
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: POSIX
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: Utilities
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: mcp<3,>=2.0.0
|
|
29
|
+
Provides-Extra: test
|
|
30
|
+
Requires-Dist: pytest>=8.0; extra == "test"
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# codex-cli-mcp-slim
|
|
37
|
+
|
|
38
|
+
A thin, auditable [MCP](https://modelcontextprotocol.io) server wrapping the [Codex CLI](https://github.com/openai/codex) (`codex exec`).
|
|
39
|
+
|
|
40
|
+
[](https://pypi.org/project/codex-cli-mcp-slim/)
|
|
41
|
+
[](https://pypi.org/project/codex-cli-mcp-slim/)
|
|
42
|
+
[](https://opensource.org/licenses/MIT)
|
|
43
|
+
[](https://github.com/tksfjt1024/codex-cli-mcp-slim/actions/workflows/ci.yml)
|
|
44
|
+
|
|
45
|
+
## Why
|
|
46
|
+
|
|
47
|
+
`codex mcp-server`, the command that let other MCP clients call Codex, is
|
|
48
|
+
deprecated, and its removal has been merged upstream
|
|
49
|
+
([openai/codex#42993](https://github.com/openai/codex/pull/42993)): releases up
|
|
50
|
+
to 0.153.x still ship it, later ones will not. Its replacement, the Codex app
|
|
51
|
+
server, speaks its own JSON-RPC protocol rather than MCP. This server keeps the old
|
|
52
|
+
integration point alive: it exposes the same two tools, `codex` and
|
|
53
|
+
`codex-reply`, and runs `codex exec` underneath. `codex exec` is the Codex CLI's
|
|
54
|
+
non-interactive mode: one prompt in, the agent works on its own, one final
|
|
55
|
+
message out.
|
|
56
|
+
|
|
57
|
+
When you add an MCP server to your AI coding tool, every prompt and code snippet
|
|
58
|
+
you send flows through that wrapper. Most CLI-wrapping MCP servers are small,
|
|
59
|
+
individually maintained packages, and recent supply-chain incidents
|
|
60
|
+
(`xz-utils`, `postmark-mcp`, the npm `chalk`/`debug` compromise) show that
|
|
61
|
+
"small and useful" is not the same as "safe to trust blindly."
|
|
62
|
+
|
|
63
|
+
This project takes the opposite stance: instead of asking you to trust it, it
|
|
64
|
+
tries to be **easy to audit**.
|
|
65
|
+
|
|
66
|
+
- **Single file** — the whole server is `src/codex_cli_mcp_slim/server.py`,
|
|
67
|
+
readable end-to-end in one sitting
|
|
68
|
+
- **One third-party dependency** (`mcp`) — minimal supply-chain surface
|
|
69
|
+
- **Faithful CLI mapping** — every typed parameter mirrors a real `codex exec`
|
|
70
|
+
flag by name, so it is obvious which flags an invocation actually sets
|
|
71
|
+
- **Prompt over stdin** — the prompt never appears in the process list and is
|
|
72
|
+
not bounded by the argv size limit
|
|
73
|
+
- **Forward-compatible** — any new or uncommon `codex exec` flag is reachable via
|
|
74
|
+
`extra_args` without touching this server
|
|
75
|
+
- **Configurable binary path** — `$CODEX_CMD` lets you swap or wrap the `codex`
|
|
76
|
+
binary
|
|
77
|
+
- **Transparent** — every invocation logs the exact argv to stderr
|
|
78
|
+
|
|
79
|
+
Read `server.py` before you install. That is the point.
|
|
80
|
+
|
|
81
|
+
## Prerequisites
|
|
82
|
+
|
|
83
|
+
- The `codex` CLI installed and on `$PATH` (or pointed to via `$CODEX_CMD`). See
|
|
84
|
+
the [official Codex CLI repository](https://github.com/openai/codex). This server
|
|
85
|
+
always passes `--json` and reads the prompt from stdin (`codex exec -`), both of
|
|
86
|
+
which `codex exec` documents.
|
|
87
|
+
- `codex` already **authenticated** — this wrapper does not manage login; it
|
|
88
|
+
surfaces `codex`'s own error output if the CLI is not ready.
|
|
89
|
+
|
|
90
|
+
## Installation
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Run directly without installing
|
|
94
|
+
uvx codex-cli-mcp-slim
|
|
95
|
+
|
|
96
|
+
# Install from PyPI
|
|
97
|
+
pip install codex-cli-mcp-slim
|
|
98
|
+
|
|
99
|
+
# Run from GitHub HEAD
|
|
100
|
+
uvx --from git+https://github.com/tksfjt1024/codex-cli-mcp-slim codex-cli-mcp-slim
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Usage as an MCP server
|
|
104
|
+
|
|
105
|
+
### Claude Code
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
claude mcp add codex uvx codex-cli-mcp-slim
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Or manually in `~/.claude.json`:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"mcpServers": {
|
|
116
|
+
"codex": {
|
|
117
|
+
"type": "stdio",
|
|
118
|
+
"command": "uvx",
|
|
119
|
+
"args": ["codex-cli-mcp-slim"]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
If `codex` is not on the launching process's `$PATH`, point `$CODEX_CMD` at it:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"mcpServers": {
|
|
130
|
+
"codex": {
|
|
131
|
+
"type": "stdio",
|
|
132
|
+
"command": "uvx",
|
|
133
|
+
"args": ["codex-cli-mcp-slim"],
|
|
134
|
+
"env": { "CODEX_CMD": "/absolute/path/to/codex" }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Replacing `codex mcp-server`
|
|
141
|
+
|
|
142
|
+
An entry that used to launch `codex mcp-server` keeps its server name and its
|
|
143
|
+
tool names; only `command` and `args` change. Before:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"mcpServers": {
|
|
148
|
+
"codex": {
|
|
149
|
+
"type": "stdio",
|
|
150
|
+
"command": "codex",
|
|
151
|
+
"args": ["mcp-server"]
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
After:
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"mcpServers": {
|
|
162
|
+
"codex": {
|
|
163
|
+
"type": "stdio",
|
|
164
|
+
"command": "uvx",
|
|
165
|
+
"args": ["codex-cli-mcp-slim"]
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Parameter names differ from the old server where `codex exec` names the flag
|
|
172
|
+
differently: `cwd` is now `cd` (the `-C/--cd` flag), and `codex-reply` takes
|
|
173
|
+
`thread_id` instead of `threadId`. The result's `structuredContent` field keeps
|
|
174
|
+
the shape the old server returned, `{"threadId": ..., "content": ...}`.
|
|
175
|
+
|
|
176
|
+
### Other MCP clients
|
|
177
|
+
|
|
178
|
+
Any MCP-compatible client can launch the server via stdio:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
uvx codex-cli-mcp-slim
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Server-level flags
|
|
185
|
+
|
|
186
|
+
Everything on the server's own command line is placed right after `codex exec`
|
|
187
|
+
on every invocation. One MCP-client entry can therefore pin a reasoning effort, a
|
|
188
|
+
model or a working directory for all of its calls. Two entries that differ only
|
|
189
|
+
in reasoning effort look like this:
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"mcpServers": {
|
|
194
|
+
"codex-medium": {
|
|
195
|
+
"type": "stdio",
|
|
196
|
+
"command": "uvx",
|
|
197
|
+
"args": ["codex-cli-mcp-slim", "-c", "model_reasoning_effort=medium"]
|
|
198
|
+
},
|
|
199
|
+
"codex-high": {
|
|
200
|
+
"type": "stdio",
|
|
201
|
+
"command": "uvx",
|
|
202
|
+
"args": ["codex-cli-mcp-slim", "-c", "model_reasoning_effort=high"]
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
A call to `codex-high` runs
|
|
209
|
+
`codex exec -c model_reasoning_effort=high [per-call flags] --json -`. Per-call
|
|
210
|
+
flags come after the server-level ones, and `-c` may repeat with the last one
|
|
211
|
+
winning, so a per-call `config` entry overrides a server-level `-c`. Single-value
|
|
212
|
+
flags such as `-m` and `-C` may not repeat: `codex` rejects the second one, and
|
|
213
|
+
the tool result carries that error. Keep server-level flags and per-call
|
|
214
|
+
parameters disjoint for those.
|
|
215
|
+
|
|
216
|
+
## Tool: `codex`
|
|
217
|
+
|
|
218
|
+
Runs a single non-interactive Codex session (`codex exec`). `codex` is an
|
|
219
|
+
agentic assistant: it reads and, depending on the sandbox, edits files in the
|
|
220
|
+
working directory to fulfil the request, then prints its final message.
|
|
221
|
+
|
|
222
|
+
The tool returns that final message followed by one metadata line:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
[codex] thread_id=019a2b3c-1d4e-7f60-8a9b-0c1d2e3f4a5b status=completed input_tokens=13894 cached_input_tokens=11904 output_tokens=612
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`thread_id` and `status` are always present; the token fields appear when the
|
|
229
|
+
run reported them. `isError` is the flag on an MCP tool result that tells the
|
|
230
|
+
client a call failed. This server sets it when `codex` exited non-zero, when the
|
|
231
|
+
subprocess timed out, and when the turn itself failed. The last case matters
|
|
232
|
+
because `codex exec` exits 0 after a failure inside the model API; the tool
|
|
233
|
+
result then carries the error text instead of coming back as a successful call:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
[ERROR] codex failed
|
|
237
|
+
|
|
238
|
+
returncode=0
|
|
239
|
+
|
|
240
|
+
errors:
|
|
241
|
+
Unsupported value: 'none' is not supported with the ... model.
|
|
242
|
+
|
|
243
|
+
[codex] thread_id=019a2b3c-... status=failed
|
|
244
|
+
|
|
245
|
+
argv: ['codex', 'exec', '--json', '-']
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Pass the `thread_id` to `codex-reply` to continue the same session.
|
|
249
|
+
|
|
250
|
+
| Parameter | Type | Description |
|
|
251
|
+
| --------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
|
|
252
|
+
| `prompt` (required) | string | Prompt sent verbatim to `codex` on stdin |
|
|
253
|
+
| `cd` | string | Pass `-C <DIR>`: the working directory; defaults to the server's own |
|
|
254
|
+
| `model` | string | Pass `-m <MODEL>` |
|
|
255
|
+
| `config` | string[] | `key=value` overrides; each maps to one `-c` (repeatable, last wins) |
|
|
256
|
+
| `sandbox` | string | Pass `--sandbox`: `read-only`, `workspace-write` or `danger-full-access`. See note below |
|
|
257
|
+
| `add_dir` | string[] | Extra writable directories; each maps to one `--add-dir` (repeatable, not comma-joined) |
|
|
258
|
+
| `profile` | string | Pass `-p <PROFILE>` |
|
|
259
|
+
| `ephemeral` | bool | Pass `--ephemeral` (do not write the session transcript codex keeps under `$CODEX_HOME/sessions`) |
|
|
260
|
+
| `skip_git_repo_check` | bool | Pass `--skip-git-repo-check` (allow a working directory outside a git repository) |
|
|
261
|
+
| `extra_args` | string[] | Raw CLI flags appended verbatim. Do not pass `--json` or a prompt; the server adds both |
|
|
262
|
+
| `env` | object | Extra environment variables for the `codex` subprocess |
|
|
263
|
+
| `timeout_seconds` | int | Hard wall-clock timeout for the subprocess, 30 to 3600 (default 1800) |
|
|
264
|
+
|
|
265
|
+
Unknown parameters are refused rather than ignored, so a call that still uses
|
|
266
|
+
the old server's `cwd` gets an error naming `cd` instead of running in the
|
|
267
|
+
wrong directory.
|
|
268
|
+
|
|
269
|
+
### Security note: `sandbox`
|
|
270
|
+
|
|
271
|
+
`codex exec` reads its sandbox mode from its own configuration file
|
|
272
|
+
(`~/.codex/config.toml` by default) unless `--sandbox` is given.
|
|
273
|
+
`danger-full-access` removes the filesystem and network sandbox entirely;
|
|
274
|
+
`workspace-write` makes the working directory (and any `add_dir`) writable.
|
|
275
|
+
`--sandbox` overrides only the mode; whether `workspace-write` gets network
|
|
276
|
+
access still follows the `[sandbox_workspace_write]` section of `config.toml`.
|
|
277
|
+
The parameter mirrors the flag so that whichever mode a call runs
|
|
278
|
+
under is visible in the arguments and in the logged argv. This server does not
|
|
279
|
+
pass `--dangerously-bypass-approvals-and-sandbox`; reach it via `extra_args` if
|
|
280
|
+
you really mean it.
|
|
281
|
+
|
|
282
|
+
## Tool: `codex-reply`
|
|
283
|
+
|
|
284
|
+
Continues a previous session (`codex exec resume <THREAD_ID>`) with a follow-up
|
|
285
|
+
prompt and returns the new final message. Only the flags `codex exec resume`
|
|
286
|
+
accepts are exposed, so `cd`, `sandbox`, `add_dir` and `profile` are refused
|
|
287
|
+
here. The working directory and sandbox of a reply come from the current
|
|
288
|
+
configuration, that is, the server-level flags and `config.toml`, not from the
|
|
289
|
+
original session.
|
|
290
|
+
|
|
291
|
+
| Parameter | Type | Description |
|
|
292
|
+
| ----------------------- | -------- | ----------------------------------------------------------------- |
|
|
293
|
+
| `thread_id` (required) | string | The `thread_id` from a previous result's `[codex]` line |
|
|
294
|
+
| `prompt` (required) | string | Follow-up prompt, sent on stdin |
|
|
295
|
+
| `model` | string | Pass `-m <MODEL>` |
|
|
296
|
+
| `config` | string[] | `key=value` overrides; each maps to one `-c` |
|
|
297
|
+
| `ephemeral` | bool | Pass `--ephemeral` |
|
|
298
|
+
| `skip_git_repo_check` | bool | Pass `--skip-git-repo-check` |
|
|
299
|
+
| `extra_args` | string[] | Raw CLI flags appended verbatim |
|
|
300
|
+
| `env` | object | Extra environment variables for the `codex` subprocess |
|
|
301
|
+
| `timeout_seconds` | int | Hard wall-clock timeout for the subprocess, 30 to 3600 (default 1800) |
|
|
302
|
+
|
|
303
|
+
## Timeout configuration
|
|
304
|
+
|
|
305
|
+
`timeout_seconds` is this wrapper's hard wall-clock limit (default 1800, or
|
|
306
|
+
`$CODEX_CLI_MCP_SLIM_TIMEOUT`). On timeout, the wrapper kills the subprocess's
|
|
307
|
+
whole process group and then waits up to 20 additional seconds to collect any
|
|
308
|
+
buffered output and reap the process, so the effective ceiling is
|
|
309
|
+
`timeout_seconds + 20`. A timed-out call is flagged `isError` and carries
|
|
310
|
+
whatever `codex` had printed so far.
|
|
311
|
+
|
|
312
|
+
## Forward-compatibility example
|
|
313
|
+
|
|
314
|
+
If a future `codex exec` release adds a new flag (say `--super-mode`), use it
|
|
315
|
+
immediately without updating this server:
|
|
316
|
+
|
|
317
|
+
```jsonc
|
|
318
|
+
{
|
|
319
|
+
"name": "codex",
|
|
320
|
+
"arguments": {
|
|
321
|
+
"prompt": "...",
|
|
322
|
+
"extra_args": ["--super-mode"]
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Configuration
|
|
328
|
+
|
|
329
|
+
| Environment variable | Default | Purpose |
|
|
330
|
+
| ------------------------------ | ------- | ---------------------------------------- |
|
|
331
|
+
| `CODEX_CMD` | `codex` | Path to the `codex` CLI binary |
|
|
332
|
+
| `CODEX_CLI_MCP_SLIM_TIMEOUT` | `1800` | Default subprocess timeout in seconds |
|
|
333
|
+
| `CODEX_CLI_MCP_SLIM_LOG_LEVEL` | `INFO` | Logging level for stderr diagnostics |
|
|
334
|
+
|
|
335
|
+
`codex` itself reads its configuration file and credentials from `$CODEX_HOME`
|
|
336
|
+
(`~/.codex` by default), so an MCP-client entry can point a server at a
|
|
337
|
+
dedicated configuration directory through its `env` block.
|
|
338
|
+
|
|
339
|
+
## Development
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# Install dev dependencies
|
|
343
|
+
pip install -e ".[test,dev]"
|
|
344
|
+
|
|
345
|
+
# Lint
|
|
346
|
+
ruff check .
|
|
347
|
+
|
|
348
|
+
# Test
|
|
349
|
+
pytest
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## License
|
|
353
|
+
|
|
354
|
+
[MIT](./LICENSE) © tksfjt1024
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# codex-cli-mcp-slim
|
|
2
|
+
|
|
3
|
+
A thin, auditable [MCP](https://modelcontextprotocol.io) server wrapping the [Codex CLI](https://github.com/openai/codex) (`codex exec`).
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/codex-cli-mcp-slim/)
|
|
6
|
+
[](https://pypi.org/project/codex-cli-mcp-slim/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://github.com/tksfjt1024/codex-cli-mcp-slim/actions/workflows/ci.yml)
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
`codex mcp-server`, the command that let other MCP clients call Codex, is
|
|
13
|
+
deprecated, and its removal has been merged upstream
|
|
14
|
+
([openai/codex#42993](https://github.com/openai/codex/pull/42993)): releases up
|
|
15
|
+
to 0.153.x still ship it, later ones will not. Its replacement, the Codex app
|
|
16
|
+
server, speaks its own JSON-RPC protocol rather than MCP. This server keeps the old
|
|
17
|
+
integration point alive: it exposes the same two tools, `codex` and
|
|
18
|
+
`codex-reply`, and runs `codex exec` underneath. `codex exec` is the Codex CLI's
|
|
19
|
+
non-interactive mode: one prompt in, the agent works on its own, one final
|
|
20
|
+
message out.
|
|
21
|
+
|
|
22
|
+
When you add an MCP server to your AI coding tool, every prompt and code snippet
|
|
23
|
+
you send flows through that wrapper. Most CLI-wrapping MCP servers are small,
|
|
24
|
+
individually maintained packages, and recent supply-chain incidents
|
|
25
|
+
(`xz-utils`, `postmark-mcp`, the npm `chalk`/`debug` compromise) show that
|
|
26
|
+
"small and useful" is not the same as "safe to trust blindly."
|
|
27
|
+
|
|
28
|
+
This project takes the opposite stance: instead of asking you to trust it, it
|
|
29
|
+
tries to be **easy to audit**.
|
|
30
|
+
|
|
31
|
+
- **Single file** — the whole server is `src/codex_cli_mcp_slim/server.py`,
|
|
32
|
+
readable end-to-end in one sitting
|
|
33
|
+
- **One third-party dependency** (`mcp`) — minimal supply-chain surface
|
|
34
|
+
- **Faithful CLI mapping** — every typed parameter mirrors a real `codex exec`
|
|
35
|
+
flag by name, so it is obvious which flags an invocation actually sets
|
|
36
|
+
- **Prompt over stdin** — the prompt never appears in the process list and is
|
|
37
|
+
not bounded by the argv size limit
|
|
38
|
+
- **Forward-compatible** — any new or uncommon `codex exec` flag is reachable via
|
|
39
|
+
`extra_args` without touching this server
|
|
40
|
+
- **Configurable binary path** — `$CODEX_CMD` lets you swap or wrap the `codex`
|
|
41
|
+
binary
|
|
42
|
+
- **Transparent** — every invocation logs the exact argv to stderr
|
|
43
|
+
|
|
44
|
+
Read `server.py` before you install. That is the point.
|
|
45
|
+
|
|
46
|
+
## Prerequisites
|
|
47
|
+
|
|
48
|
+
- The `codex` CLI installed and on `$PATH` (or pointed to via `$CODEX_CMD`). See
|
|
49
|
+
the [official Codex CLI repository](https://github.com/openai/codex). This server
|
|
50
|
+
always passes `--json` and reads the prompt from stdin (`codex exec -`), both of
|
|
51
|
+
which `codex exec` documents.
|
|
52
|
+
- `codex` already **authenticated** — this wrapper does not manage login; it
|
|
53
|
+
surfaces `codex`'s own error output if the CLI is not ready.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
# Run directly without installing
|
|
59
|
+
uvx codex-cli-mcp-slim
|
|
60
|
+
|
|
61
|
+
# Install from PyPI
|
|
62
|
+
pip install codex-cli-mcp-slim
|
|
63
|
+
|
|
64
|
+
# Run from GitHub HEAD
|
|
65
|
+
uvx --from git+https://github.com/tksfjt1024/codex-cli-mcp-slim codex-cli-mcp-slim
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage as an MCP server
|
|
69
|
+
|
|
70
|
+
### Claude Code
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
claude mcp add codex uvx codex-cli-mcp-slim
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Or manually in `~/.claude.json`:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"codex": {
|
|
82
|
+
"type": "stdio",
|
|
83
|
+
"command": "uvx",
|
|
84
|
+
"args": ["codex-cli-mcp-slim"]
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
If `codex` is not on the launching process's `$PATH`, point `$CODEX_CMD` at it:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"mcpServers": {
|
|
95
|
+
"codex": {
|
|
96
|
+
"type": "stdio",
|
|
97
|
+
"command": "uvx",
|
|
98
|
+
"args": ["codex-cli-mcp-slim"],
|
|
99
|
+
"env": { "CODEX_CMD": "/absolute/path/to/codex" }
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Replacing `codex mcp-server`
|
|
106
|
+
|
|
107
|
+
An entry that used to launch `codex mcp-server` keeps its server name and its
|
|
108
|
+
tool names; only `command` and `args` change. Before:
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"codex": {
|
|
114
|
+
"type": "stdio",
|
|
115
|
+
"command": "codex",
|
|
116
|
+
"args": ["mcp-server"]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
After:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcpServers": {
|
|
127
|
+
"codex": {
|
|
128
|
+
"type": "stdio",
|
|
129
|
+
"command": "uvx",
|
|
130
|
+
"args": ["codex-cli-mcp-slim"]
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Parameter names differ from the old server where `codex exec` names the flag
|
|
137
|
+
differently: `cwd` is now `cd` (the `-C/--cd` flag), and `codex-reply` takes
|
|
138
|
+
`thread_id` instead of `threadId`. The result's `structuredContent` field keeps
|
|
139
|
+
the shape the old server returned, `{"threadId": ..., "content": ...}`.
|
|
140
|
+
|
|
141
|
+
### Other MCP clients
|
|
142
|
+
|
|
143
|
+
Any MCP-compatible client can launch the server via stdio:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uvx codex-cli-mcp-slim
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Server-level flags
|
|
150
|
+
|
|
151
|
+
Everything on the server's own command line is placed right after `codex exec`
|
|
152
|
+
on every invocation. One MCP-client entry can therefore pin a reasoning effort, a
|
|
153
|
+
model or a working directory for all of its calls. Two entries that differ only
|
|
154
|
+
in reasoning effort look like this:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"mcpServers": {
|
|
159
|
+
"codex-medium": {
|
|
160
|
+
"type": "stdio",
|
|
161
|
+
"command": "uvx",
|
|
162
|
+
"args": ["codex-cli-mcp-slim", "-c", "model_reasoning_effort=medium"]
|
|
163
|
+
},
|
|
164
|
+
"codex-high": {
|
|
165
|
+
"type": "stdio",
|
|
166
|
+
"command": "uvx",
|
|
167
|
+
"args": ["codex-cli-mcp-slim", "-c", "model_reasoning_effort=high"]
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
A call to `codex-high` runs
|
|
174
|
+
`codex exec -c model_reasoning_effort=high [per-call flags] --json -`. Per-call
|
|
175
|
+
flags come after the server-level ones, and `-c` may repeat with the last one
|
|
176
|
+
winning, so a per-call `config` entry overrides a server-level `-c`. Single-value
|
|
177
|
+
flags such as `-m` and `-C` may not repeat: `codex` rejects the second one, and
|
|
178
|
+
the tool result carries that error. Keep server-level flags and per-call
|
|
179
|
+
parameters disjoint for those.
|
|
180
|
+
|
|
181
|
+
## Tool: `codex`
|
|
182
|
+
|
|
183
|
+
Runs a single non-interactive Codex session (`codex exec`). `codex` is an
|
|
184
|
+
agentic assistant: it reads and, depending on the sandbox, edits files in the
|
|
185
|
+
working directory to fulfil the request, then prints its final message.
|
|
186
|
+
|
|
187
|
+
The tool returns that final message followed by one metadata line:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
[codex] thread_id=019a2b3c-1d4e-7f60-8a9b-0c1d2e3f4a5b status=completed input_tokens=13894 cached_input_tokens=11904 output_tokens=612
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`thread_id` and `status` are always present; the token fields appear when the
|
|
194
|
+
run reported them. `isError` is the flag on an MCP tool result that tells the
|
|
195
|
+
client a call failed. This server sets it when `codex` exited non-zero, when the
|
|
196
|
+
subprocess timed out, and when the turn itself failed. The last case matters
|
|
197
|
+
because `codex exec` exits 0 after a failure inside the model API; the tool
|
|
198
|
+
result then carries the error text instead of coming back as a successful call:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
[ERROR] codex failed
|
|
202
|
+
|
|
203
|
+
returncode=0
|
|
204
|
+
|
|
205
|
+
errors:
|
|
206
|
+
Unsupported value: 'none' is not supported with the ... model.
|
|
207
|
+
|
|
208
|
+
[codex] thread_id=019a2b3c-... status=failed
|
|
209
|
+
|
|
210
|
+
argv: ['codex', 'exec', '--json', '-']
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Pass the `thread_id` to `codex-reply` to continue the same session.
|
|
214
|
+
|
|
215
|
+
| Parameter | Type | Description |
|
|
216
|
+
| --------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
|
|
217
|
+
| `prompt` (required) | string | Prompt sent verbatim to `codex` on stdin |
|
|
218
|
+
| `cd` | string | Pass `-C <DIR>`: the working directory; defaults to the server's own |
|
|
219
|
+
| `model` | string | Pass `-m <MODEL>` |
|
|
220
|
+
| `config` | string[] | `key=value` overrides; each maps to one `-c` (repeatable, last wins) |
|
|
221
|
+
| `sandbox` | string | Pass `--sandbox`: `read-only`, `workspace-write` or `danger-full-access`. See note below |
|
|
222
|
+
| `add_dir` | string[] | Extra writable directories; each maps to one `--add-dir` (repeatable, not comma-joined) |
|
|
223
|
+
| `profile` | string | Pass `-p <PROFILE>` |
|
|
224
|
+
| `ephemeral` | bool | Pass `--ephemeral` (do not write the session transcript codex keeps under `$CODEX_HOME/sessions`) |
|
|
225
|
+
| `skip_git_repo_check` | bool | Pass `--skip-git-repo-check` (allow a working directory outside a git repository) |
|
|
226
|
+
| `extra_args` | string[] | Raw CLI flags appended verbatim. Do not pass `--json` or a prompt; the server adds both |
|
|
227
|
+
| `env` | object | Extra environment variables for the `codex` subprocess |
|
|
228
|
+
| `timeout_seconds` | int | Hard wall-clock timeout for the subprocess, 30 to 3600 (default 1800) |
|
|
229
|
+
|
|
230
|
+
Unknown parameters are refused rather than ignored, so a call that still uses
|
|
231
|
+
the old server's `cwd` gets an error naming `cd` instead of running in the
|
|
232
|
+
wrong directory.
|
|
233
|
+
|
|
234
|
+
### Security note: `sandbox`
|
|
235
|
+
|
|
236
|
+
`codex exec` reads its sandbox mode from its own configuration file
|
|
237
|
+
(`~/.codex/config.toml` by default) unless `--sandbox` is given.
|
|
238
|
+
`danger-full-access` removes the filesystem and network sandbox entirely;
|
|
239
|
+
`workspace-write` makes the working directory (and any `add_dir`) writable.
|
|
240
|
+
`--sandbox` overrides only the mode; whether `workspace-write` gets network
|
|
241
|
+
access still follows the `[sandbox_workspace_write]` section of `config.toml`.
|
|
242
|
+
The parameter mirrors the flag so that whichever mode a call runs
|
|
243
|
+
under is visible in the arguments and in the logged argv. This server does not
|
|
244
|
+
pass `--dangerously-bypass-approvals-and-sandbox`; reach it via `extra_args` if
|
|
245
|
+
you really mean it.
|
|
246
|
+
|
|
247
|
+
## Tool: `codex-reply`
|
|
248
|
+
|
|
249
|
+
Continues a previous session (`codex exec resume <THREAD_ID>`) with a follow-up
|
|
250
|
+
prompt and returns the new final message. Only the flags `codex exec resume`
|
|
251
|
+
accepts are exposed, so `cd`, `sandbox`, `add_dir` and `profile` are refused
|
|
252
|
+
here. The working directory and sandbox of a reply come from the current
|
|
253
|
+
configuration, that is, the server-level flags and `config.toml`, not from the
|
|
254
|
+
original session.
|
|
255
|
+
|
|
256
|
+
| Parameter | Type | Description |
|
|
257
|
+
| ----------------------- | -------- | ----------------------------------------------------------------- |
|
|
258
|
+
| `thread_id` (required) | string | The `thread_id` from a previous result's `[codex]` line |
|
|
259
|
+
| `prompt` (required) | string | Follow-up prompt, sent on stdin |
|
|
260
|
+
| `model` | string | Pass `-m <MODEL>` |
|
|
261
|
+
| `config` | string[] | `key=value` overrides; each maps to one `-c` |
|
|
262
|
+
| `ephemeral` | bool | Pass `--ephemeral` |
|
|
263
|
+
| `skip_git_repo_check` | bool | Pass `--skip-git-repo-check` |
|
|
264
|
+
| `extra_args` | string[] | Raw CLI flags appended verbatim |
|
|
265
|
+
| `env` | object | Extra environment variables for the `codex` subprocess |
|
|
266
|
+
| `timeout_seconds` | int | Hard wall-clock timeout for the subprocess, 30 to 3600 (default 1800) |
|
|
267
|
+
|
|
268
|
+
## Timeout configuration
|
|
269
|
+
|
|
270
|
+
`timeout_seconds` is this wrapper's hard wall-clock limit (default 1800, or
|
|
271
|
+
`$CODEX_CLI_MCP_SLIM_TIMEOUT`). On timeout, the wrapper kills the subprocess's
|
|
272
|
+
whole process group and then waits up to 20 additional seconds to collect any
|
|
273
|
+
buffered output and reap the process, so the effective ceiling is
|
|
274
|
+
`timeout_seconds + 20`. A timed-out call is flagged `isError` and carries
|
|
275
|
+
whatever `codex` had printed so far.
|
|
276
|
+
|
|
277
|
+
## Forward-compatibility example
|
|
278
|
+
|
|
279
|
+
If a future `codex exec` release adds a new flag (say `--super-mode`), use it
|
|
280
|
+
immediately without updating this server:
|
|
281
|
+
|
|
282
|
+
```jsonc
|
|
283
|
+
{
|
|
284
|
+
"name": "codex",
|
|
285
|
+
"arguments": {
|
|
286
|
+
"prompt": "...",
|
|
287
|
+
"extra_args": ["--super-mode"]
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Configuration
|
|
293
|
+
|
|
294
|
+
| Environment variable | Default | Purpose |
|
|
295
|
+
| ------------------------------ | ------- | ---------------------------------------- |
|
|
296
|
+
| `CODEX_CMD` | `codex` | Path to the `codex` CLI binary |
|
|
297
|
+
| `CODEX_CLI_MCP_SLIM_TIMEOUT` | `1800` | Default subprocess timeout in seconds |
|
|
298
|
+
| `CODEX_CLI_MCP_SLIM_LOG_LEVEL` | `INFO` | Logging level for stderr diagnostics |
|
|
299
|
+
|
|
300
|
+
`codex` itself reads its configuration file and credentials from `$CODEX_HOME`
|
|
301
|
+
(`~/.codex` by default), so an MCP-client entry can point a server at a
|
|
302
|
+
dedicated configuration directory through its `env` block.
|
|
303
|
+
|
|
304
|
+
## Development
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
# Install dev dependencies
|
|
308
|
+
pip install -e ".[test,dev]"
|
|
309
|
+
|
|
310
|
+
# Lint
|
|
311
|
+
ruff check .
|
|
312
|
+
|
|
313
|
+
# Test
|
|
314
|
+
pytest
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## License
|
|
318
|
+
|
|
319
|
+
[MIT](./LICENSE) © tksfjt1024
|