The Quest for Model Routing Starts with a Local Cross-Provider Gateway

Kenny Vaneetvelde
Written by Kenny Vaneetvelde
August 7, 2026

A brass selector dial on a wooden panel, with three cables running out to three different machines

I wanted model routing. Getting it meant building a gateway first, and that’s the whole story of this plugin.

Every piece of work should go to whichever model actually suits it: mechanical edits to something cheap, real engineering judgment to a mid tier, the genuinely hard stuff to the expensive one, Opus over the top to plan and review. That only works if one client can reach every one of those models. Claude Code couldn’t reach the GPT ones, so I was choosing from a third of my actual options.

So I built the piece that fixed it, and then split it out into its own plugin on the off chance it was useful to anyone else: model-gateway (MIT, in my Eigenwise Toolshed marketplace). It runs entirely on localhost, it fronts three providers at once, and it puts the models from my ChatGPT and Grok subscriptions right in Claude Code’s /model picker, next to Opus. Workbench does the setup for you, so there’s basically nothing to configure.

As of August 2026, four out of five of the jobs I hand off run on a GPT model, 1,475 out of 1,838. Every one of them ran from inside Claude Code.

The reasonable question is why I don’t just use Codex CLI for that four-fifths. I did for a while, and Grok Build, and Gemini CLI before Google retired it in June. They’re all genuinely good now. The router isn’t the only thing that would have to move, though: twenty-odd plugins, the hooks that fire on every single turn, the skills, the MCP servers. Moving models takes a minute. Moving everything built around them takes a week I’m not going to spend.

What It Actually Does

The hard part isn’t mine. raine/claude-code-proxy does the ChatGPT OAuth and translates the Anthropic Messages API to OpenAI’s Codex backend. It’s Rust, MIT, and it had commits land the same day I wrote this. setup pulls the release for your platform and verifies its sha256 before anything runs.

What my plugin adds is a small router that sits in front. Claude Code points at it through ANTHROPIC_BASE_URL, and it makes three different decisions:

Claude Code ──▶ shim (127.0.0.1:18764)

                  ├── claude-gpt-*  ──▶ claude-code-proxy (:18765) ──▶ Codex
                  ├── claude-grok-* ──▶ cli-chat-proxy.grok.com
                  └── everything else ──▶ api.anthropic.com

That third arrow is the one that matters. Point a plain proxy at Claude Code and it takes over your endpoint outright, so everything you do goes through it. This splits the traffic instead. Claude requests go straight to Anthropic carrying your own credential, with the original request bytes forwarded untouched so prompt caching still keys on them. Your claude.ai auth never travels toward the proxy at all. So Opus and GPT-5.6 live in the same session, and I can switch between them mid-conversation without either one noticing.

The Grok path is my own code rather than raine’s: about 450 lines translating Anthropic Messages to xAI’s Responses API, with its own streaming transformer. It reads the token the Grok CLI already stored, so there’s no second login.

The Trick Is a Prefix, and So Is the Price

The Claude Code model picker listing GPT-5.6 and Grok rows, each marked From gateway

Claude Code can populate /model from a gateway’s /v1/models, and the documentation is precise about the catch: it ”ignores entries whose id doesn’t begin with claude or anthropic.”

So the router lies a little. It advertises GPT-5.6 Terra as claude-gpt-5.6-terra and strips the prefix back off on the way through. Routing keys off the family segment after the prefix, which is why claude-opus-5 and claude-gpt-5.6-sol share a namespace without colliding. Discovery is off by default and needs v2.1.129 or newer, so it’s one environment variable and a restart.

That prefix is not free. As of Claude Code 2.1.207, the context-window resolver never reads a discovered model’s advertised max_input_tokens for a claude- prefixed id. It hardwires 200,000. And the escape hatch that would let you override it is gated behind the id not starting with claude-.

Read that twice, because I had to. The prefix is what gets these models into the picker, and the same prefix caps them at 200k. My router advertises 370,000 tokens on the Codex rows and Claude Code throws every one of them away. You cannot have the picker without the cap, and I can’t fix it from my side.

Where Claude Code Is Still Ahead

The honest answer here is less flattering than the one I’d have given six months ago.

As of August 2026, Codex CLI has hooks, plugins, marketplaces, skills, subagents, MCP, an SDK, and an enterprise policy layer. Grok Build shipped AGENTS.md, plugins, hooks, skills and parallel subagents in worktrees while still in an early beta. SKILL.md is now an open cross-vendor standard that OpenAI builds on directly. On a few things Codex is straight-up ahead: hook trust is bound to the hook’s hash, so editing one re-triggers review; subagents pin their own sandbox mode; MCP tools get per-tool approval. The categories converged, and anyone telling you otherwise hasn’t opened the docs this year.

What’s left is depth, and OpenAI documents the gap themselves. Codex defines five hook handler types. Its hooks reference says prompt and agent handlers ”are parsed but skipped.” One of the five actually executes. The same page lists async, suppressOutput, updatedMCPToolOutput and permissionDecision: "ask" as parsed and unimplemented too.

Claude Code runs all five, across roughly 32 lifecycle events against Codex’s 12, and the extra ones aren’t padding: FileChanged, ConfigChange, WorktreeCreate, TaskCompleted, PostToolBatch are exactly what an infrastructure plugin needs to exist at all. A Claude Code plugin can also ship LSP servers, background monitors, and executables that join the Bash tool’s PATH while it’s enabled. That’s the difference between a surface that exists and a surface that’s finished, and it’s the whole reason I stay.

What I Actually Run

A routing profile table mapping each tier to a model, an effort level, the work it gets, and its ticket count

Terra is my default. Sol takes the hard escalations, Luna does the mechanical work, and Opus barely executes anything at all: it plans, reviews, and gatekeeps the audits. Fable I’ve reached for maybe a dozen times, for the rare thing that stalls Opus and Sol outright.

There’s an objection to this that I think is right. Use a second model purely as a cheap executor downstream of a Claude plan and you mostly just get a slower Claude. The real value of a second model is that it disagrees with the first one.

That’s why Opus stays in the review seat. On one project, three separate audit passes over the same renderer all ran on Opus, checking work that Sol had produced. The cheaper tokens are the least interesting part of this, and if the savings were the only reason, I’d have stopped bothering months ago.

Grok, honestly: it works and I’ve barely touched it. One 17-minute session in late July. I built the backend because I wanted the option sitting there, not because it’s part of my day.

The Trade-Off

The only real cost is Remote Control. It only activates when ANTHROPIC_BASE_URL points at the actual Anthropic host, and the gateway needs that variable pointed at the router instead. There’s a compatibility mode that gets Remote Control back through a hosts-file entry, and it costs you the dropdown rows: you type /model claude-gpt-5.6-terra by hand and it still routes fine. On Linux and macOS the port it needs is root-reserved, so it quietly stays in normal mode rather than asking.

The rest, briefly. Codex and Grok are not equally supported: the Codex path has a context sentry, error normalization, a compaction retry guard and a WebSocket retry, and the Grok path has none of them. Plan mode is stripped on Codex models because GPT models call it spuriously and an approved exit downgrades your permission mode. Codex returns no reasoning stream. Compaction on Codex was unreliable enough that it needed a dedicated buffered retry to work at all.

And it’s one more thing to maintain, sitting downstream of OpenAI’s client fingerprinting, which they’ve tightened before. The blast radius is bounded, at least: Claude models never break, and one command puts everything back to stock.

It’s Modular, and It Wires Itself In

Output of model-gateway doctor showing the proxy, shim, auth, model count and wiring all checking out

On its own, the gateway is just the picker rows. Install it by itself and that’s exactly what you get, and honestly for a lot of people that’s the whole use case: your other subscription, in the client you already like, no ceremony.

The reason it ends up near zero-config is Workbench, which is the piece of the Toolshed that owns setup. It notices the gateway isn’t configured and offers to run it. It downloads the proxy, verifies the checksum, and swaps binaries by rename so it can update without making you close sessions. Then it restarts the thing and tells you what state it actually ended in rather than assuming. The only step it can’t do for you is the ChatGPT browser sign-in, because that’s a browser sign-in.

The wiring gets written once, globally, and it covers every project and every executor worktree. That second half is the part that makes routing work at all: a dispatched subagent running off in its own git worktree reaches the GPT models exactly the same way my main session does, with no per-project setup and nothing to remember.

My own router, Sidequest, sits on top and matches each job to a model and an effort level. The gateway is what makes any of that possible, and it’s the reason it exists at all.

None of these require each other, which was the point of splitting them up. The gateway on its own just gives you a better model picker, and for most people that’s the whole thing. The routing layer above it is mine, and you can ignore it entirely.

Where to Get It

Three commands inside Claude Code, in this order:

/plugin marketplace add Eigenwise/eigenwise-toolshed
/plugin install workbench@eigenwise-toolshed --scope user
/workbench:init-workspace

The third one is where the gateway actually happens. Workbench notices it isn’t configured and offers to do it, so say yes, sign in to ChatGPT when the browser opens, and restart the session. The new rows show up in /model after that, or you can skip the picker and name one outright:

/model claude-gpt-5.6-terra

If you want to add a provider, here’s what that actually costs: there’s no config file for it. It’s a code change in five places plus a backend module, and grok-backend.js is the complete worked example at about 450 lines. Gemini isn’t in there today. I’d take the help.

If you only take one idea from this: the model you use and the tool you use it in are two separate decisions, and you’re allowed to make them separately.

And if you’re at a company trying to get real value out of your AI stack, this is a big part of what I do: the plugins, hooks, and routing that make Claude Code actually productive on your own codebase and your own model budget. If you’d want a hand getting it working, feel free to reach out.