unlob Docs
Browse documentation

Codex CLI: connect unlob over MCP

TOML rather than JSON, and a bridge if your build predates remote servers.

Configuration

Codex reads ~/.codex/config.toml. Servers go under mcp_servers.

Recent versions can connect to a Streamable HTTP server directly:

[mcp_servers.unlob]
url = "https://api.unlob.com/mcp"

[mcp_servers.unlob.http_headers]
"x-api-key" = "ulb_…"

Remote-server support and the exact spelling of these keys have changed as Codex has evolved, so check your own codex --version and its documentation before assuming the above. If it does not take, the bridge below works on every version.

The bridge

[mcp_servers.unlob]
command = "npx"
args = ["-y", "mcp-remote", "https://api.unlob.com/mcp", "--header", "x-api-key:${UNLOB_API_KEY}"]

[mcp_servers.unlob.env]
UNLOB_API_KEY = "ulb_…"

No space after the colon in the header argument — argument splitting eats it.

Check it

codex mcp list

The server should be listed with its tools. Inside a session, ask Codex what tools it has.

Why this one matters

Codex is a strict MCP client. It is the client that finds handshake bugs other clients paper over — if it registers zero tools against a server, that server has a real conformance problem, not a Codex problem. Against api.unlob.com it should register the tools the profile advertises; if it does not, tell us rather than working around it.

Rate limits look the same as running out of credits

A 429 from unlob means two different things, and Codex has no way to tell them apart on its own — it sees a failed tool call either way. The distinction lives in one header:

retry-after present     → per-minute rate limit; retry once, after that many seconds
retry-after absent      → out of monthly credits; retrying changes nothing

If you drive tool calls through a wrapper script rather than letting Codex call the server directly, branch on the header there rather than on the status code alone — a bare 429 retried on a blind loop against a hard-capped key produces nothing but identical failures. 402 is separate again: the account itself is suspended, and no amount of retrying reaches a working key. Rate limits and credits has the full table.

Why this one matters, restated for the config layer

The strictness that makes Codex useful for finding handshake bugs also means it is unforgiving of a config that is nearly right. A url key where unlob expects the header table nested one level differently, or a TOML table name that does not match what a given Codex build parses, produces the same symptom as a real server bug: zero tools, no error. That is worth remembering before assuming a fresh conformance issue — check the config against codex mcp list first, since a malformed entry there often does not even appear as a server Codex attempted to reach, rather than one it reached and failed to talk to.

Scripting outside a session

codex mcp list and the config above are for an interactive session. For a headless script that only ever needs web_search or assemble_context and does not want to drive an MCP handshake at all, calling the REST endpoint directly is usually less code — see the SDK quickstarts for a client in five languages. Reach for MCP when Codex itself is the caller; reach for REST when a shell script or CI step is.

A CI job that runs codex exec non-interactively is a middle case worth naming specifically: it still goes through the MCP config, so the same mcp_servers entry applies, but it is worth confirming the key is present in whatever environment that job runs under — a config file committed with an ${UNLOB_API_KEY} reference is only as good as the pipeline actually setting that variable.

Next