fix: codex uses static bearer for inkwell MCP + retire legacy 'pcp' server (by Wren) - #444
Merged
Merged
Conversation
…fresh (by Wren) Codex independently manages OAuth credentials for the inkwell MCP server in the macOS Keychain and refreshes them on startup. When that server-side refresh token expires (90d lifetime), the refresh returns invalid_grant and codex aborts MCP init — even though ink already injects a valid Authorization bearer via env_http_headers. This blocked `ink -a lumen -b codex` from starting at all. Switch both the CLI adapter (backends/codex.ts) and the server runner (codex-runner.ts) to codex's `bearer_token_env_var` mechanism, pointing at INK_ACCESS_TOKEN (raw token; codex prepends "Bearer "). This makes codex authenticate with the ink-owned token AND skip its own managed OAuth discovery/refresh for the server, so the expired keychain credential is never consulted. Also: - Remove the now-dead INK_AUTH_BEARER env var (mcp-config, claude spawn). - Fix spawnSync sqlite3 ENOBUFS when listing codex local sessions (default ~1MB buffer overflowed on large state DBs; add 64MB maxBuffer), which was adding retry latency to every codex startup. Verified: with the expired keychain token still present, codex exec with the bearer override initializes inkwell MCP and responds normally, no invalid_grant. Co-Authored-By: Wren <noreply@anthropic.com>
…y Wren) The MCP server was renamed pcp → inkwell. `ink init` and `ink mcp sync` already emit only 'inkwell', but two runtime paths still fell back to a 'pcp' server key when one was present, which re-fed and perpetuated stale config (and left it running its own broken OAuth): - injectSessionHeaders (shared/runner/mcp-config) - the gemini settings builder (cli/backends/gemini) Both now target 'inkwell' exclusively; gemini-runner already did. No code path creates or feeds a 'pcp' server block anymore. Co-Authored-By: Wren <noreply@anthropic.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.
Problem:
ink -a lumen -b codexfails at TUI bootstrap withrequired MCP servers failed to initialize: inkwell: ... invalid_grant: Invalid refresh token.Root cause: Codex independently manages its own OAuth credential for the inkwell MCP server (macOS Keychain) and refreshes it on startup. That refresh token has a 90-day server-side lifetime; once expired the server returns invalid_grant. In the codex TUI path MCP init is required, so codex aborts the whole session — even though ink already injects a valid Authorization bearer via env_http_headers. Codex was running a redundant OAuth dance anyway.
Fix:
backends/codex.ts) and server runner (codex-runner.ts) from an Authorization env_http_header to codexbearer_token_env_varpointing at INK_ACCESS_TOKEN (raw token; codex prepends "Bearer "). This authenticates with the ink-owned token AND skips codex managed OAuth discovery/refresh, so the expired keychain credential is never consulted. (Same mechanismmcp.tsalready uses when generating codex config.)pcpserver name. Server was renamed pcp → inkwell.ink init/ink mcp syncalready emit only inkwell, butinjectSessionHeaders(shared/runner/mcp-config) and the gemini settings builder still fell back to apcpserver key when present, reviving stale config. Both now target inkwell exclusively (gemini-runner already did). No code path creates/feeds a pcp server block anymore.spawnSync sqlite3ENOBUFS listing codex local sessions — default ~1MB buffer overflowed on large state_5.sqlite, silently dropping to the slower jsonl fallback and adding ~5s retry latency to every codex startup. Now 64MB maxBuffer.Also removes the now-dead INK_AUTH_BEARER env var (mcp-config, codex spawn site in claude.ts).
Validation: reproduced with the expired keychain token still present —
codex execwith the bearer override initializes inkwell MCP and replies normally (no invalid_grant, exit 0). Mirror-image confirmed: a same-URL duplicate server without the override still logs invalid_grant (non-fatal in exec, fatal in TUI), which is why the legacy pcp duplicate had to go.Operational note (not code): removed a stale [mcp_servers.pcp] block from the local ~/.codex/config.toml (same URL as inkwell, no auth injection, always OAuth-failed) so the interactive TUI boots clean.
🤖 Generated with Claude Code
— Wren