Test the launch command hiding inside each client config
Shipped
I added a Codex project configuration that launches the same local-fitness stdio MCP entrypoint already available to Claude. The v0.63.1 release adds compatibility tests for that command and for the instruction file that points Codex to the canonical repository guidance. The scheduled brief backend was still Claude-backed at this tag; adding another client did not replace it.
The useful test is small: do both client configurations launch the same program with the same arguments? You can answer that before either client starts.
Compare meaning after parsing
Use Python 3.11 or newer, which includes tomllib. Create an empty folder and save the following as contract.py. The configurations are deliberately embedded fixtures so you can run the example without installing an MCP server.
The Codex shape follows its documented MCP configuration. The JSON fixture uses the common mcpServers wrapper. We are comparing launch data, not claiming the two hosts have identical configuration semantics.
import json
import tomllib
CLAUDE_JSON = '''{
"mcpServers": {
"build_tools": {
"command": "uv",
"args": ["run", "build-tools", "mcp-stdio"]
}
}
}'''
CODEX_TOML = '''
[mcp_servers.build_tools]
command = "uv"
args = ["run", "build-tools", "mcp-stdio"]
startup_timeout_sec = 30
'''
def launch_contract(entry: dict) -> tuple[str, tuple[str, ...]]:
command = entry.get("command")
args = entry.get("args")
if not isinstance(command, str) or not command.strip():
raise ValueError("command must be nonempty")
if not isinstance(args, list) or not all(isinstance(a, str) for a in args):
raise ValueError("args must be a list of strings")
if "env" in entry:
raise ValueError("keep credentials out of shared project fixtures")
return command, tuple(args)
def compare(claude_text: str, codex_text: str) -> bool:
claude = json.loads(claude_text)["mcpServers"]["build_tools"]
codex = tomllib.loads(codex_text)["mcp_servers"]["build_tools"]
return launch_contract(claude) == launch_contract(codex)
The invented build-tools executable is fixture data. This script never invokes it and is not an MCP implementation. In your repository, replace the fixture strings with reads of the actual checked-in files and replace the service key with your own.
Only normalize fields that should be equal. A host-specific timeout does not belong in this tuple unless your application requires parity there. Credentials do not belong in this shared fixture at all. This check adopts a conservative project policy by rejecting env; that is not a claim that the clients prohibit environment configuration.
Make the test prove it notices drift
Append the following block. The negative case changes an argument while leaving both documents syntactically valid. A parser-only test would miss it.
if __name__ == "__main__":
assert compare(CLAUDE_JSON, CODEX_TOML)
drifted = CODEX_TOML.replace('"mcp-stdio"', '"serve-http"')
assert not compare(CLAUDE_JSON, drifted)
print("matching launch contract: accepted")
print("changed transport argument: rejected")
Run:
python3 contract.py
You should see:
matching launch contract: accepted
changed transport argument: rejected
That is the complete static check. It does not prove that uv is on a client’s PATH, that a project is trusted, or that the server answers MCP messages. Keep those claims out of the test name and out of the release note.
For a real integration check, start your existing MCP server through each configured client and call one known read-only tool. The MCP stdio specification defines the subprocess boundary: protocol messages use standard input and output, while diagnostic logging belongs on standard error. A successfully launched process can still break that boundary with an innocent startup print.
Gotchas
These are reader-side failure modes to watch for, not incidents reported by this patch.
Equivalent files can contain different commands. Valid JSON and valid TOML only prove syntax. The symptom is one client exposing the tools while another starts the wrong transport. Compare the parsed command and ordered argument list, then keep a deliberately drifted fixture to prove the check has teeth.
A checked-in configuration may not be active. Codex documents project configuration in the context of trusted projects. A correct static test does not establish the user’s effective configuration. Inspect the client’s loaded server list before debugging the server implementation.
A local transport is not the remote security model. The tagged local entrypoint is stdio; the deployed HTTP endpoint remains a separate path. Do not copy an auth-free local example into a network service. The transport specification calls out authentication and origin validation for Streamable HTTP.
Sources
- Python tomllib — parse TOML before comparing values.
- Codex MCP configuration — command, arguments, and client configuration.
- MCP transports — stdio behavior and the HTTP security boundary.
Changelog
- feat: add Codex compatibility alongside Claude (0.63.1) (#252) (#253) (983f889)