Monday, August 3, 2026

Running Claude Code Against Anything: Profiles, Other Models, and a Deliberately Raw One


Running Claude Code Against Anything: Profiles, Other Models, and a Deliberately Raw One pay for Claude. I also run Ollama locally — which turns out to front a pile of cloud-hosted models as well as local ones — and I keep an OpenRouter key around for the long tail. For a while those felt like separate worlds: Claude Code for real work, everything else through some other tool.

That turned out to be a false split. Claude Code is an agent harness that happens to ship pointed at Anthropic. Point it somewhere else and it is still the same harness — same tool loop, same permissions model, same skills. The switch is one environment variable.

What that one variable buys is not really "other models." It is the ability to switch skill sets, providers, and accounts independently of each other — three things that are otherwise welded into one setup you either use or do not.

This is how I set up five parallel Claude Code identities on one machine without touching my existing install, and what I learned about which parts actually isolate.

Why bother

Four reasons, in rough order of how often they matter to me.

Grunt work is not worth premium tokens. Renaming things across a repo, summarizing a log, first-pass triage on a stack trace. A local model does that fine. Every one of those tasks I push off my subscription is capacity left for work that needs the good model.

Some work should not leave the machine. Client code under NDA, anything with credentials in the scrollback. A local Ollama model means the question never crosses the network. That is a much easier conversation with a security team than a policy promise.

Two subscriptions can be cheaper than one bigger subscription. This one gets its own section below, because it is the reason the whole setup pays for itself. When you upgrade a plan you are usually not buying a smarter model — you are buying more of the one you have. Rate limits are what actually interrupt your day. There is more than one way to buy headroom, and the obvious one is not the cheapest.

You cannot evaluate a model through a stack of instructions. If the scaffolding is always on, you have no control group. More on this below, along with what it costs to run.

The mechanism

CLAUDE_CONFIG_DIR. Point it at a directory and Claude Code keeps everything there — credentials, session history, per-user MCP config, skills.

CLAUDE_CONFIG_DIR=$HOME/.claude-ollama claude

That is the whole trick. The rest is packaging.

What genuinely isolates: credentials, .claude.json (my default is 207 KB of accumulated project state; a fresh profile is 389 bytes), user-scoped MCP servers, and skills.

What does not: a .mcp.json committed inside a repo still loads under every profile. That is correct — it belongs to the project, not the identity — but it is worth knowing before you assume a profile is hermetic.

I wrap it in a small launcher that sources a per-profile env file and execs through:

#!/usr/bin/env bash
set -euo pipefail
name=$1; shift
set -a; source "$HOME/.claude-profiles/$name.env"; set +a
: "${CLAUDE_CONFIG_DIR:?profile '$name' did not set CLAUDE_CONFIG_DIR}"
exec claude "$@"

Because it execs with "$@", every Claude Code flag still works. claude-ollama --model something-else needs no config edit.

Bare claude is untouched throughout. Nothing here runs unless I name a profile — that was a hard requirement, because a broken experiment must never take out my working setup.

First: does your provider actually speak Anthropic?

Claude Code speaks the Anthropic Messages API and nothing else. Some providers serve that natively. Others serve only OpenAI-style chat completions, and those need a translating proxy in between — a much bigger lift.

Find out in one command before you build anything:

curl -sS -X POST <base>/v1/messages -H "Content-Type: application/json" -d '{}'

An Anthropic-shaped error means native support, and you are done in five minutes:

{"type":"error","error":{"type":"invalid_request_error","message":"model is required"}}

A 404 means you need a proxy. Always compare against a path you know is wrong (/v1/bogus) so you can tell "endpoint exists, request was bad" from "endpoint does not exist." A 400 and a 404 look similar in a terminal and mean completely different amounts of work.

Run this before you plan anything. Two of my providers answered natively and took five minutes each. A third 404'd on every Anthropic-shaped path I tried, which meant a translating daemon, a config format, and a process to babysit. Same ambition, wildly different afternoons — and the only thing that told me which was coming was one curl.

Example 1: Ollama, local and free

Ollama serves the Anthropic Messages API natively at /v1/messages. Verified:

POST 127.0.0.1:11434/v1/messages  -> 400 {"type":"error","error":{"type":"invalid_request_error",...}}
POST 127.0.0.1:11434/v1/bogus     -> 404

Native. No proxy, no translation layer. The whole profile:

CLAUDE_CONFIG_DIR=$HOME/.claude-ollama
ANTHROPIC_BASE_URL=http://127.0.0.1:11434
ANTHROPIC_AUTH_TOKEN=ollama          # any non-empty string
ANTHROPIC_API_KEY=                   # must be explicitly blank
ANTHROPIC_MODEL=glm-5.2:cloud
ANTHROPIC_SMALL_FAST_MODEL=gemma4:31b-cloud

That blank ANTHROPIC_API_KEY is not decoration. Leave it set and it collides with ANTHROPIC_AUTH_TOKEN, and the failure is not obvious from the error message.

Worth knowing: Ollama also fronts cloud-hosted models — 21 :cloud models on my box today — so one local-looking profile reaches well beyond what fits in local VRAM. Truly local models and hosted ones sit behind the same endpoint, which is either elegant or a footgun depending on whether you set this up for privacy reasons. If the point was that data never leaves the machine, pin a genuinely local model and know the difference.

One caveat: not every local model survives the translation. One of mine returns tokenize error: _Map_base::at through the Anthropic endpoint while working fine elsewhere. The :cloud models have been reliable.

Example 2: OpenRouter, one key and the long tail

OpenRouter serves an Anthropic-compatible endpoint too. Same probe, same answer:

POST https://openrouter.ai/api/v1/messages -> 400 "Invalid Anthropic Messages API request"
POST https://openrouter.ai/api/v1/bogus    -> 404
: "${OPENROUTER_API_KEY:?OPENROUTER_API_KEY not set — source ~/.bashrc}"
CLAUDE_CONFIG_DIR=$HOME/.claude-openrouter
ANTHROPIC_BASE_URL=https://openrouter.ai/api
ANTHROPIC_AUTH_TOKEN=$OPENROUTER_API_KEY
ANTHROPIC_API_KEY=
ANTHROPIC_MODEL=qwen/qwen3-coder-next
ANTHROPIC_SMALL_FAST_MODEL=z-ai/glm-4.7-flash

Two habits worth copying. The key is referenced, never duplicated into the profile — it lives in one place and this file points at it. And the ${VAR:?} guard makes a missing key fail loudly instead of silently issuing unauthenticated calls. Test that it works:

env -u OPENROUTER_API_KEY claude-profile openrouter   # exits 1, says why

A guard you have not watched fail is a guard you do not have.

Example 3: the empty profile

This one is not about a provider at all. It is the skill-set dial.

Most of us have accumulated a CLAUDE.md, a rules directory, and a pile of skills, built up against models that needed the help. All of it is prepended to every request. And there is a persistent claim going around that the newer models do better without it — that the scaffolding we wrote to compensate for older models is now getting in their way.

Maybe. But you cannot check, because your scaffolding is always on.

So I built a profile with nothing in it. No CLAUDE.md, no rules, no persona, an empty skills directory.

The important part is what did not change: same Linux user, same machine, same working directory, and the same Claude account — it takes its own /login, but to the subscription I already pay for. The only variable is the context. That is what makes it an experiment rather than an anecdote. If the bare profile does better, it is not because it is a different account, a different plan tier, or a different model.

The whole profile is one meaningful line, plus a guard:

# a provider override left in the shell would silently redirect this profile
unset ANTHROPIC_BASE_URL ANTHROPIC_AUTH_TOKEN ANTHROPIC_API_KEY
unset ANTHROPIC_MODEL ANTHROPIC_SMALL_FAST_MODEL

CLAUDE_CONFIG_DIR=$HOME/.claude-noskills

Those unset lines matter more than they look. If you have been experimenting with providers, a stray ANTHROPIC_BASE_URL in your shell will quietly send your "clean control group" to somebody else's model, and nothing in the output will tell you. Test it: export a bogus base URL and confirm the profile still reaches Anthropic.

Measured from an identical working directory, same trivial prompt:

profile prompt tokens
fully loaded (instructions + 20 skills) 30,078
empty 20,236

9,842 tokens on every single request. Thirty-three percent.

The distribution is the interesting part. Almost all of it is CLAUDE.md plus the rules tree — about 9,600 tokens. The twenty skills barely register, because Claude Code injects only a name and short description per skill and loads the body on demand. Skills are cheap. Prose rules are not.

Which is the opposite of how I had been economising. I had been reluctant to add skills and relaxed about adding rules.

If you want to A/B a model against your own instructions, this is the control group. It is also just a cheaper way to ask a quick question.

The trap that nearly invalidated my first measurement: I ran it from my home directory and got 29,971 — barely different from the loaded profile. Claude Code treats ~/.claude/ as a project-level config directory when your working directory is $HOME, so it re-injected the entire rules tree into the profile I had carefully emptied. Run comparisons from a neutral directory or you will measure nothing and believe you measured something.

The simplest profile, and the cheapest upgrade

A second Anthropic account needs no base URL and no key:

CLAUDE_CONFIG_DIR=$HOME/.claude-second

That is the entire profile. Log in once and the two accounts never see each other again.

It is also, I think, the most useful thing in this whole post.

When you outgrow a single Claude subscription, the obvious move is to upgrade the plan. But look at what you are actually buying. Pro runs about $17/month on annual billing. Max is about $100/month. You are not paying six times more for a smarter model — you are paying six times more for more capacity. Rate limits are what actually interrupt your day.

Two Pro subscriptions cost $34/month and give you two independent limit pools. That is a third of the price of Max. Hit the ceiling on one, claude-second, keep working.

The honest tradeoff: the pools do not merge. Max gives you one large bucket; two Pro plans give you two smaller buckets and a manual switch between them. If your single hardest session needs more headroom than one Pro plan provides, two Pro plans will not fix that — you will hit the same wall, just twice. Max is buying a bigger bucket, not more buckets, and sometimes a bigger bucket is genuinely what you need.

But that is a narrower case than the upgrade prompt implies. If your problem is "I run out partway through the afternoon" rather than "one task exceeds my ceiling," a second cheap seat solves it for a third of the money. And it scales in increments you control: a third seat is another $17, not another $100.

This is the part that makes the whole setup pay for itself. Everything else here is about routing work to cheaper models. This one is about not overpaying for the good model in the first place.

The mistake to not repeat

I tried to seed a new profile by copying .credentials.json from an existing one. Do not do this.

The copy was byte-identical. It still failed: the token was past expiry, the refresh was rejected, and Claude Code then blanked the copy. Worse, the refresh attempt exercised the source account's refresh token server-side — so I may have disturbed a session belonging to an account whose on-disk file I never modified.

OAuth credentials rotate. A copy is a stale snapshot of a moving target, and refreshing one snapshot can invalidate the other. Run /login in the new profile. It takes fifteen seconds.

Skills, and sharing them across accounts

Skills live in $CLAUDE_CONFIG_DIR/skills/, so a new profile starts with none until you point it at some. That is the right default — which skills a profile sees should be a decision, not something it inherits by accident. It is also the dial itself: a profile has exactly the skills you give it, and that is the whole point.

What makes this cheap is having a canonical set that lives outside any single identity. Mine is under /opt. A new profile gets symlinked at it and immediately has everything; update the source once and every profile that points there is current. Copies drift; symlinks cannot.

The one thing to be deliberate about: nothing tells you which way a profile is configured. If you meant to wire a profile up and did not, it will not complain — it will just work, less well, and you will be guessing why. Check it rather than assume it.

Putting it under /opt rather than in a home directory does more work than it looks: it is readable by more than one Linux account. That part is not new here — I had skills shared across two Linux accounts long before any of this, and that was the whole reason for the location.

What profiles changed is not the sharing. It is that they pulled apart three things that used to be welded together — skill set, provider, and account. To use the other subscription you became the other Linux user, and you inherited that user's entire environment along with it: their skills, their config, their permissions. One switch, everything moved.

Now each is its own dial, and you can turn one without touching the others. Same account and same skills, different provider. Same provider and same account, no skills. Same everything, different account. That is the whole idea, and each dial pays off somewhere different:

  • Provider — route cheap work off your subscription. Ollama, OpenRouter.
  • Skill set — vary the scaffolding while holding everything else fixed. The empty profile.
  • Account — the handoff, and the reason two cheap plans beat one expensive one.

The handoff

This is the part that actually changed how I work.

I have two Linux accounts on this box, each with its own Claude subscription. Both point at the same /opt skills tree, so the agent behaves identically on either side — same skills, same versions, same conventions.

Here is what using that second subscription used to cost me:

sudo -iu otheraccount        # become a different Linux user
                             # confirm that user can actually reach the code
                             # ...and the shared folders
                             # ...with the right group permissions
claude                       # finally
                             # then have it read the handoff doc

Every one of those steps is a place to get stuck, and the permissions step is the one that actually bites — a different Unix user means different file ownership, and you find out by watching the agent fail to write something.

Now:

claude-second                # done

Same Linux user. Same shell, same working directory, same file ownership, same permissions — because I never left my own account. Only the Claude identity changed.

That is what makes the handoff practical. Write a handoff document, exit, start the other profile, pick up where the document left off. The skills and rules are already there because they come from the same /opt tree, so the second agent needs no re-explaining.

The difference is between a capability you have and a capability you actually use. sudo plus a permissions audit is enough friction that you skip it and burn one subscription harder instead. An alias is not. That is what makes two cheap subscriptions practical rather than a spreadsheet exercise.

Verify rather than trust. Ask a profile to list its skills, or if you are proxying, read the outgoing request and confirm the names are really in there.

Is this realistic, honestly?

For daily use, yes. ANTHROPIC_BASE_URL and CLAUDE_CONFIG_DIR are documented Claude Code settings, not undocumented internals, and both Ollama and OpenRouter clearly built their Anthropic-shaped endpoints on purpose — you do not ship a /v1/messages route that returns correctly-formed Anthropic errors by accident. Nothing here is a hack. Setup is about twenty minutes per provider; after that it is five aliases.

Three honest caveats.

Non-Claude models in an agent loop are not the same experience. Tool calling is where the gap shows. A model that writes lovely prose can still fumble a multi-step edit-test-fix cycle, and you will feel that long before you feel any difference in raw output quality. Route the easy, bounded work.

Advertised capability is not capability. I hit a model advertising tool support and a million-token context that rejected every request Claude Code sent, because it refused an Anthropic-specific field on message content. Test the actual client against the actual model. The model card will not tell you.

Not everything is five minutes. Providers that speak only OpenAI-style chat completions need a translating daemon in front of them, and now you own a background process, its config, and its failure modes. Run the probe first and decide whether the model is worth the plumbing. Often it is not.

I have one of these running. It involved a router pinned to an old version because the current one needs a newer Node than I want to install, an undocumented transformer I found by reading minified source, a vendor system prompt quietly adding 1,600 tokens to every request, and a model that advertises everything you want and rejects every request Claude Code sends. It works now, and it is genuinely useful. It was not five minutes. That is a whole post of its own, and it probably will be.

What I would tell someone starting

Start with the second account, not the alternative providers. It is the least technical thing here and the only one that puts money back in your pocket immediately. Before you upgrade a plan, work out whether you need a bigger bucket or just more buckets — the answer decides whether you spend $100 a month or $34.

Then run the probe. If your provider answers with an Anthropic-shaped error, you are twenty minutes from a working second identity, and the only thing you risk is a new directory. If it 404s, think harder about whether you want that model badly enough.

Build the empty profile early. Not because you will use it daily, but because until you have a control group, every opinion you hold about your own instructions is untested. Mine cost 33% of every request, and I had no idea.

No comments:

Post a Comment