Align codex-acp serde_json key ordering with codex CLI to avoid MCP keychain hash mismatches#155
Merged
benbrandt merged 1 commit intozed-industries:mainfrom Feb 16, 2026
Conversation
Co-authored-by: Codex <codex@openai.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Codex MCP OAuth credential keys are derived from a JSON serialization that includes
type,url, andheaders. The hash changes if JSON object key order changes. Codex CLI binaries (Homebrew cask, GitHub release, and a local source build) preserve insertion order becausecodex-clidepends oncodex-tui, andcodex-tuienablesserde_json/preserve_order. codex-acp does not depend oncodex-tui, so it defaults to sorted key order and computes a different key hash.This PR aligns codex-acp with codex CLI by enabling
serde_json/preserve_orderso the hashed key matches the CLI behavior and avoids keychain/account mismatches.My biggest concern is that this alignment feels fragile but I'm not aware of any other way to do the alignment or fix the underlying issue.
Furthermore the entire approach of reusing the Agent's keychain entries feels a bit untenable to me. The UX of it is that because the
-acpshim is the binary responsible for accessing the keychain item you get prompted to allow access to it from the binary. This is arguably correct but also arguably surprising and scary. I'm inclined to say that what I've built here is better than it not working at all but I'm open to other ideas about how to make the UX better. One I had is to somehow make it so that we use entirely separate keychain items than Codex or Claude so that the -acp binary itself is what's making and reading the items. The UX of that (if it's even possible) would be that you'd need to signin via acp if you wanted to use MCP from ACP.Where the hash comes from
MCP OAuth credential keys are computed by serializing a JSON payload and hashing the result. The payload is built with keys
type,url, andheaders, then passed throughsha_256_prefix:The JSON string differs by feature:
{"headers":{},"type":"http","url":"https://example.com/mcp"}{"type":"http","url":"https://example.com/mcp","headers":{}}Those serialize to different hashes and therefore different keychain account IDs.
Why codex CLI preserves order
codex-tuienablesserde_json/preserve_order, andcodex-clidepends oncodex-tui. Cargo feature unification makes preserve_order apply to the whole build:codex-acp does not depend on
codex-tui, so it does not inherit preserve_order unless explicitly enabled.Evidence and repro
Black-box repro (no keychain / no Atlassian required)
The script
x.cask-order-repro.shseedsCODEX_HOME/.credentials.jsonwith both possible keys and then runscodex mcp logout demo. The key that disappears indicates which JSON ordering was used to compute the hash.x.cask-order-repro.sh
Usage examples:
CODEX_BIN=/opt/homebrew/bin/codex ./x.cask-order-repro.shCODEX_BIN=/path/to/codex-aarch64-apple-darwin ./x.cask-order-repro.shCODEX_BIN=/path/to/codex-rs/target/debug/codex ./x.cask-order-repro.shInsertion-order key (preserve_order):
Sorted-order key (default):
Observed behavior:
codex0.101.0 deletesdemo|9a70b85417749d5c(preserve_order)rust-v0.101.0deletesdemo|9a70b85417749d5c(preserve_order)demo|9a70b85417749d5c(preserve_order)codex-acp behavior
A minimal test in
tests/serde_json_ordering.rsshows codex-acp defaults to sorted order without preserve_order. Enablingserde_json/preserve_orderin codex-acp flips the serialization to insertion order, matching codex CLI.Change in this PR
Enable
serde_json/preserve_orderin codex-acp (Cargo.toml) to match codex CLI behavior and avoid mismatched MCP OAuth key hashes.