Skip to content

Add Codex CLI direct-mode support to thv llm setup - #5789

Merged
JAORMX merged 4 commits into
mainfrom
worktree-wondrous-questing-dragonfly
Jul 14, 2026
Merged

JAORMX merged 4 commits into
mainfrom
worktree-wondrous-questing-dragonfly

Conversation

@jerm-dro

@jerm-dro jerm-dro commented Jul 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Codex CLI isn't a supported target for thv llm setup yet — it can't be
pointed at the LLM gateway the way Claude Code, Cursor, and other clients
already are. This adds direct-mode support for Codex.

  • Codex gets a custom model_provider entry written into its
    ~/.codex/config.toml (the same file its MCP-server registration already
    uses), authenticated via a command-backed bearer token
    ([model_providers.<id>.auth] invoking thv llm token --skip-browser)
    rather than a static API key — mirroring Claude Code's apiKeyHelper,
    but shaped for Codex's TOML/argv config instead of JSON/shell-string.
  • Codex's Responses-API client appends /responses directly onto
    base_url (the same convention OpenAI's own base_url=".../v1" follows),
    so the gateway's /v1 prefix is baked into base_url at write time
    rather than left to Codex to add.
  • Setup/teardown are idempotent and preserve any other model_providers.*
    entries or mcp_servers config already in the file; revert only clears
    ToolHive's own provider and only clears model_provider if it still
    points at ours.
  • New dedicated TOML writer (pkg/client/llm_gateway_codex.go) since
    Codex's config format and auth shape don't fit the existing JSON-Pointer
    LLMGatewayKeys mechanism the other direct-mode clients share.

Closes #5783

Type of change

  • New feature

Test plan

  • Unit tests (task test)
  • E2E tests (task test-e2e)
  • Linting (task lint-fix)
  • Manual testing (describe below)

Manually ran thv llm setup --client codex and thv llm teardown --client codex
against a live gateway, confirming ~/.codex/config.toml gets the expected
model_provider/model_providers.toolhive-gateway table on setup and a clean
removal (with foreign entries untouched) on teardown.

Implementation plan

Approved implementation plan

Add Codex CLI direct-mode support to thv llm setup (#5783)

Why: thv llm setup doesn't support Codex CLI yet. We're adding direct mode: Codex's ~/.codex/config.toml gets a custom model_provider pointed at the gateway, using Codex's command-backed auth ([model_providers.<id>.auth]) to invoke thv llm token, same idea as Claude Code's apiKeyHelper but TOML/argv-shaped instead of JSON/shell-string.

What:

  • pkg/llmgateway/config.go: new ModeCodexAuth constant; add TokenHelperPath/TokenHelperArgs to ApplyConfig (argv form, vs. Claude Code's shell-string TokenHelperCommand).
  • pkg/client/config.go: give the existing Codex client entry LLMGatewayMode: ModeCodexAuth, LLMBinaryName: "codex", LLMSettingsFile/RelPath pointing at ~/.codex/config.toml (same file its MCP config already uses).
  • pkg/client/llm_gateway_codex.go (new): configureCodexAuth/revertCodexAuth, reusing readTOMLConfig/writeTOMLConfig from config_editor.go. Writes model_provider = "toolhive-gateway" + a model_providers.toolhive-gateway table (name, base_url, wire_api = "responses", auth.command/auth.args), preserving any other model_providers.* entries. Revert deletes just that sub-table and clears model_provider only if still ours.
  • pkg/client/llm_gateway.go: add a dispatch branch for ModeCodexAuth next to the existing ModeCredentialHelper one.
  • pkg/llm/setup.go: buildTokenHelperArgv() (mirrors buildTokenHelperCommand but returns argv (path, []string{"llm","token","--skip-browser"}); always --skip-browser since Codex has no interactive-context signal like Claude Desktop's shim); wire into configureDetectedTools; add a warnTLSSkipVerify case for Codex ("not supported", like the Gemini CLI case). usesAnthropicBaseURL stays unchanged — Codex is deliberately excluded so it gets plain GatewayURL, not the Anthropic-prefixed URL.
  • Tests: unit tests for configure/revert round-trip + idempotency in pkg/client; a dedicated e2e Describe block in cli_llm_all_clients_test.go (parallel to the existing claude-desktop block, since the generic matrix asserts via JSON and can't parse TOML).
  • task docs after, per CLAUDE.md.

Verification: task test, task lint-fix, task test-e2e, then manually run thv llm setup --client codex + codex against staging to confirm an actual request round-trips through the gateway, then thv llm teardown --client codex to confirm clean removal.

Does this introduce a user-facing change?

Yes — thv llm setup/thv llm teardown now support Codex CLI as a direct-mode
client, alongside the existing supported clients.

Special notes for reviewers

The gateway's /v1 path segment is baked directly into the written base_url,
unlike the optional Anthropic path prefix other clients use — this is required
because Codex's Responses-API client always appends /responses straight onto
whatever base_url is configured, with no separate path-prefix concept of
its own.

Generated with Claude Code

Codex is a supported client but has no llm gateway integration yet.
Add direct mode: a custom model_provider in ~/.codex/config.toml
pointed at the gateway, authenticated via a command-backed bearer
token (thv llm token) rather than an API key file, mirroring Claude
Code's apiKeyHelper but shaped for Codex's TOML/argv config.

Codex's Responses-API client appends "/responses" straight onto
base_url, so the gateway's "/v1" prefix is baked into base_url rather
than left to Codex, unlike the optional Anthropic path prefix.
@github-actions github-actions Bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Jul 13, 2026
@codecov

codecov Bot commented Jul 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.72093% with 14 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.78%. Comparing base (687ce42) to head (d564073).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
pkg/llm/setup.go 72.41% 5 Missing and 3 partials ⚠️
pkg/client/llm_gateway_codex.go 88.23% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5789      +/-   ##
==========================================
- Coverage   70.78%   70.78%   -0.01%     
==========================================
  Files         684      685       +1     
  Lines       69353    69435      +82     
==========================================
+ Hits        49091    49147      +56     
- Misses      16654    16691      +37     
+ Partials     3608     3597      -11     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds first-class support for configuring the OpenAI Codex CLI as a direct-mode client of ToolHive’s LLM gateway via thv llm setup/teardown. It introduces a Codex-specific “codex-auth” mode that patches ~/.codex/config.toml using TOML semantics (including Codex’s command/argv-based bearer token auth) rather than the existing JSON-pointer patch mechanism used by other clients.

Changes:

  • Add new LLM-gateway mode codex-auth and plumb argv-form token helper fields (TokenHelperPath/TokenHelperArgs) through setup/config application.
  • Implement a dedicated Codex TOML writer to create/remove model_provider and model_providers.toolhive-gateway entries (including /v1 base URL handling and token-helper command/args).
  • Add unit + e2e coverage for Codex setup/teardown behavior and the new token-helper argv builder.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/e2e/cli_llm_all_clients_test.go Adds dedicated Codex TOML assertions for setup/teardown outside the JSON-based matrix.
pkg/llmgateway/config.go Introduces ModeCodexAuth and extends ApplyConfig with argv-style token helper fields.
pkg/llm/setup.go Wires argv-form token helper into tool configuration and adds a TLS-skip note for Codex mode.
pkg/llm/setup_test.go Adds unit tests for buildTokenHelperArgv and Codex-specific TLS-skip messaging.
pkg/client/llm_gateway.go Dispatches configure/revert to Codex’s TOML writer for codex-auth mode.
pkg/client/llm_gateway_codex.go New TOML-based configure/revert implementation for Codex gateway auth configuration.
pkg/client/llm_gateway_codex_test.go New unit tests for Codex TOML writer (write, idempotency, preservation, revert semantics).
pkg/client/config.go Registers Codex as an LLM gateway client using codex-auth mode and ~/.codex/config.toml.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/llm/setup.go Outdated
Comment thread pkg/client/llm_gateway_codex.go
Comment thread pkg/client/llm_gateway_codex.go
Fixes three issues from PR review: setup could fail unnecessarily
for Codex-only runs when thv's own executable path had shell-unsafe
characters, even though Codex's argv-based auth never uses the
shell-string helper; configureCodexAuth silently wrote an invalid
provider entry when the token helper was unset; and a gateway URL
already ending in "/v1" was doubled to "/v1/v1" in Codex's base_url.
@github-actions github-actions Bot added size/M Medium PR: 300-599 lines changed and removed size/M Medium PR: 300-599 lines changed labels Jul 13, 2026
@jerm-dro
jerm-dro requested a review from Copilot July 14, 2026 00:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Comment thread pkg/llm/setup.go Outdated

@JAORMX JAORMX left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panel review (spec / standards / domain axes). Strong PR overall — clean, well-documented, good test coverage, and it reuses the in-codebase TOML/lock/atomic-write helpers correctly rather than reinventing them. Security posture is sound: the token is never persisted, the config is written 0600, and the argv path correctly drops the shell-metachar validation because there's no shell downstream.

Inline comments cover the findings. Summary:

Worth addressing before merge

  • Codex is missing from the Long help text and docs/cli/ (see config.go comment) — the one real blocker.
  • Non-table model_providers is silently clobbered on setup (llm_gateway_codex.go).
  • refresh_interval_ms from the issue's auth spec isn't written.
  • --tls-skip-verify warning severity reads backwards for Codex.

Judgement calls (non-blocking)

  • Per-mode behavior is now spread across ~5 sites (two dispatch chains + warnTLSSkipVerify + usesAnthropicBaseURL + tokenHelperCommandNeeded); a mode-capability descriptor would collapse them, but deferring is defensible at 3 modes.
  • Direct-mode-only: the issue flagged confirming gateway Responses-API support before direct mode and offered proxy mode as the dependency-free fallback. The manual round-trip test in the description is good evidence it works — worth noting that confirmation explicitly.
  • Revert removes model_provider rather than restoring the prior value — document the semantics.

No duplication or library-reuse findings — the parallel Codex writer is structurally similar to the credential-helper one but behaviorally independent, so a shared abstraction would be the wrong call.

Happy to help push fixes for the concrete items.

Comment thread pkg/client/config.go
Comment thread pkg/client/llm_gateway_codex.go Outdated
Comment thread pkg/client/llm_gateway_codex.go
Comment thread pkg/llm/setup.go Outdated
Comment thread pkg/client/llm_gateway_codex.go
Follow-up on the review of #5789, fixing the concrete findings while
leaving the design judgement calls (mode-capability descriptor, proxy
fallback) for separate discussion.

- Write refresh_interval_ms into Codex's auth table via a new
  CodexHelperTTL constant (= ClaudeCodeHelperTTL), so Codex's
  token-helper cadence stays inside the token source's preemptive
  refresh window like the other clients. Guard it in the invariant test.
- Refuse to overwrite a pre-existing non-table model_providers instead
  of silently clobbering the user's config; mirrors the revert path's
  type check.
- Surface Codex in "thv llm setup" help text and regenerate CLI docs so
  the new client is discoverable.
- Promote the Codex --tls-skip-verify message from Note to Warning and
  state plainly that the flag was not applied.
- Document revertCodexAuth's remove-not-restore semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JAORMX

JAORMX commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Pushed a follow-up commit (17682ef) addressing the concrete findings from the review above, to save you a round-trip:

  • refresh_interval_ms — added via a new CodexHelperTTL constant (= ClaudeCodeHelperTTL), so Codex's token-helper cadence stays inside the token source's preemptive-refresh window like the other clients. Guarded in the invariant test.
  • Non-table model_providers — setup now errors instead of silently clobbering, mirroring the revert path's type check (+ test).
  • Discoverability — Codex added to thv llm setup help text; task docs regenerated.
  • --tls-skip-verify — promoted Note: → Warning: and it now states the flag was not applied.
  • Revert semantics — documented the remove-not-restore behaviour on revertCodexAuth.

Left alone as design calls for you/the team rather than mechanical fixes: the mode-capability-descriptor refactor (per-mode behaviour is now spread across ~5 sites) and the direct-mode-vs-proxy-fallback question. task lint, task docs, and the affected package tests all pass. Feel free to squash, amend, or drop any of it.

@github-actions github-actions Bot added size/L Large PR: 600-999 lines changed and removed size/M Medium PR: 300-599 lines changed labels Jul 14, 2026
Follow-up to the review nitpick on warnTLSSkipVerify: its switch mixed
raw "direct"/"proxy" string literals with the llmgateway.ModeCodexAuth
constant. The llmgateway package already declares these as the single
source of truth, so switch on the constants for all cases.

Also update the now-stale ToolConfig.Mode doc comment, which still
listed only "direct"/"proxy" though "codex-auth" is now a valid value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions github-actions Bot added size/L Large PR: 600-999 lines changed and removed size/L Large PR: 600-999 lines changed labels Jul 14, 2026
@JAORMX

JAORMX commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Opened #5790 as the follow-up cleanup: it sweeps the remaining "direct"/"proxy" string literals (the client registrations in pkg/client/config.go and hasProxyMode) to the llmgateway.ModeDirect/ModeProxy constants. The warnTLSSkipVerify switch is converted here in d564073 rather than there, since this PR already modifies that function — so the two PRs do not touch overlapping lines.

@JAORMX
JAORMX merged commit e9473d5 into main Jul 14, 2026
47 checks passed
@JAORMX
JAORMX deleted the worktree-wondrous-questing-dragonfly branch July 14, 2026 08:31
@github-actions github-actions Bot mentioned this pull request Jul 14, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large PR: 600-999 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Codex CLI support to thv llm setup

4 participants