Skip to content

fix: codex uses static bearer for inkwell MCP + retire legacy 'pcp' server (by Wren) - #444

Merged
conoremclaughlin merged 2 commits into
mainfrom
wren/fix/codex-inkwell-static-bearer
Jul 29, 2026
Merged

fix: codex uses static bearer for inkwell MCP + retire legacy 'pcp' server (by Wren)#444
conoremclaughlin merged 2 commits into
mainfrom
wren/fix/codex-inkwell-static-bearer

Conversation

@conoremclaughlin

Copy link
Copy Markdown
Owner

Problem: ink -a lumen -b codex fails at TUI bootstrap with required 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:

  1. Static bearer instead of OAuth (core fix). Switch both the CLI adapter (backends/codex.ts) and server runner (codex-runner.ts) from an Authorization env_http_header to codex bearer_token_env_var pointing 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 mechanism mcp.ts already uses when generating codex config.)
  2. Retire the legacy pcp server name. Server was renamed pcp → inkwell. ink init/ink mcp sync already emit only inkwell, but injectSessionHeaders (shared/runner/mcp-config) and the gemini settings builder still fell back to a pcp server 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.
  3. Fix spawnSync sqlite3 ENOBUFS 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 exec with 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.

  • yarn build: clean (exit 0, 0 TS errors)
  • adapters.test.ts + mcp-config.test.ts + spawn-reflect.test.ts: 47 passing

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

conoremclaughlin and others added 2 commits July 29, 2026 17:53
…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>
@conoremclaughlin
conoremclaughlin merged commit 516c026 into main Jul 29, 2026
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant