Chapter 6 — Providers, models & API keys
thClaws talks to ten providers, auto-detected from the model name.
Switch any time with /model or /provider.
Provider overview
| Provider | Model prefix | Auth env var | Notes |
|---|---|---|---|
| Agentic Press | ap/* |
AGENTIC_PRESS_LLM_API_KEY |
OpenAI-compatible gateway; many backends under one key |
| Anthropic | claude-* |
ANTHROPIC_API_KEY |
Extended thinking, prompt caching (system + tools) |
| Anthropic Agent SDK | agent/* |
— (uses Claude Code’s own auth) | Drives the claude CLI under your Claude Pro / Max subscription instead of API billing. ⚠ thClaws’s tool registry doesn’t cross the subprocess boundary — the model only sees Claude Code’s built-in toolset. KMS / MCP / Agent Teams tools are unreachable from this provider; switch to claude-* for those. |
| OpenAI | gpt-*, o1-*, o3*, o4-* |
OPENAI_API_KEY |
Chat Completions; automatic prompt caching |
| OpenAI Responses | codex/* |
OPENAI_API_KEY |
Responses API — newer agentic-native shape |
| OpenRouter | openrouter/* |
OPENROUTER_API_KEY |
Unified gateway to 300+ models across every major LLM vendor |
| Gemini | gemini-*, gemma-* |
GEMINI_API_KEY |
Gemma served via Google AI Studio |
| Ollama | ollama/* |
— (local) | NDJSON streaming; no auth |
| Ollama Anthropic | oa/* |
— (local, v0.14+) | Ollama’s Anthropic-compatible /v1/messages endpoint |
| DashScope | qwen-*, qwq-* |
DASHSCOPE_API_KEY |
Alibaba Qwen; automatic caching |
The default on first run is claude-sonnet-4-6; change it with
--model on the command line or persist in settings.json.
Switching providers
❯ /providers
agentic-press → ap/gemma4-12b
* anthropic → claude-sonnet-4-6
anthropic-agent → agent/claude-sonnet-4-6
openrouter → openrouter/anthropic/claude-sonnet-4-6
...
❯ /provider openai
provider → openai (model: gpt-4o, saved to .thclaws/settings.json; new session sess-…)
❯ /provider
current provider: openai (model: gpt-4o)
Switching always forks a fresh session (see Chapter 7) — the old conversation is saved, a new one starts with the new provider.
Switching models
/model takes the full model id or a short alias:
| Alias | Resolves to |
|---|---|
sonnet |
claude-sonnet-4-6 |
opus |
claude-opus-4-6 |
haiku |
claude-haiku-4-5 |
flash |
gemini-2.0-flash |
❯ /model sonnet
(alias 'sonnet' → 'claude-sonnet-4-6')
model → claude-sonnet-4-6 (saved to .thclaws/settings.json; new session sess-…)
❯ /models
claude-haiku-4-5
claude-opus-4-6
claude-sonnet-4-6
...
/model validates the name against list_models before committing.
A typo like /model gemma4-9999 leaves the current model in place and
prints unknown model '…' — try /models.
/models lists the server’s reported catalogue for the current
provider. For Ollama and Agentic Press, IDs come back prefixed (e.g.
ollama/llama3.2, ap/gemma4-26b) so you can paste them straight
into /model.
Reasoning / “thinking” models
Models in the families below emit a reasoning_content field
alongside their normal content, and the provider requires the
prior reasoning_content to be sent back on every subsequent turn —
otherwise the API rejects with HTTP 400 “The reasoning_content in the
thinking mode must be passed back to the API”.
thClaws handles this transparently: it captures the reasoning into a
hidden Thinking content block on the assistant message, and on the
next request echoes it back only for providers that need it.
| Family | Example model id | Provider |
|---|---|---|
| DeepSeek v4 | deepseek/deepseek-v4-flash, deepseek/deepseek-v4-pro |
OpenRouter |
| DeepSeek r1 | deepseek/deepseek-r1, deepseek-r1 |
OpenRouter, native |
| OpenAI o-series | openai/o1-mini, openai/o3, openai/o4-* |
OpenRouter |
For everything outside those families (gpt-4o, claude-sonnet-4-6,
qwen3.6-plus, etc.) the Thinking block is dropped during
serialization — no extra input tokens, no risk of the provider
rejecting an unknown field.
If you switch from a thinking model to a non-thinking model mid-session, the prior reasoning blocks stay in your session JSONL but don’t go on the wire. No token leak.
For contributors — make catalogue
If you build from source and want to refresh the bundled
model_catalogue.json (the compile-time baseline that ships with the
binary), the workspace Makefile has a target:
make catalogue
It pulls the model lists from OpenRouter (always, no key required)
plus Anthropic / OpenAI / Gemini if their API keys are set, plus
Ollama if reachable at localhost:11434, and merges into the
catalogue insert-only (hand-curated rows are never overwritten).
The report shows new IDs added per provider plus counts of unchanged +
skipped (no context_length) entries, then prints git diff --stat so
you can review before committing.
API key hierarchy
Keys are never stored in settings.json. thClaws looks in four
places, highest priority wins:
| Level | Location | Scope |
|---|---|---|
| Shell export | ~/.zshrc, CI env, etc. |
Every process |
| OS keychain | macOS Keychain / Windows Credential Manager / Linux Secret Service | Every thClaws session on this machine |
User .env |
~/.config/thclaws/.env |
Every thClaws session |
Project .env |
./.env in the working directory |
This project only |
Recommended: use the Settings modal (GUI) — it saves keys to
the OS keychain, which is materially more secure than any .env
path:
| OS keychain (via Settings modal) | .env file |
|
|---|---|---|
| At-rest encryption | ✓ derived from your login password (Secure Enclave on modern Macs) | ✗ plaintext |
| Access control | ✓ tied to your user account | ✗ any process with filesystem read access |
| Accidental git commit | ✓ impossible (not a file in the repo) | ⚠ easy (people forget .gitignore) |
| Leaks via Time Machine / cloud sync / rsync | ✓ no | ⚠ yes — the file goes where your backups go |
| Works headless / in CI | ✗ no Secret Service on most headless Linux | ✓ yes |
So: use the Settings modal on your laptop or workstation; fall back
to .env only when you’re in an environment that lacks a keychain
(CI runners, minimal Docker images, headless servers).
Secrets backend chooser
The first time you launch thClaws, right after you pick a working
directory (Chapter 3), a dialog asks you to pick how your secrets
should be stored. This runs before thClaws touches the OS keychain at
all — pick .env and no keychain prompt ever fires.

Two choices:
- OS keychain (recommended) — macOS Keychain / Windows Credential Manager / Linux Secret Service. Encrypted at rest and tied to your user account. The first time thClaws reads a key you’ll get a one-time OS access prompt; click “Always Allow” and it’s silent after.
.envfile — plain-text at~/.config/thclaws/.env. No keychain prompts ever. Works on headless Linux boxes that lack Secret Service. Trade-off: anyone with read access to your home directory can read the file, so treat it like any other secret.
Your choice is saved to ~/.config/thclaws/secrets.json and respected
forever after. You can change your mind later: Settings → Provider API
keys → “Change…” link in the modal header re-opens the chooser.
Single-entry keychain bundle (one prompt per launch)
When you pick the keychain backend, all provider keys live inside one
keychain item — service thclaws, account api-keys, storing a JSON map
{"anthropic": "sk-ant-…", "openai": "sk-…", …}. This matters because macOS
Keychain ACLs are per-item: with N separate items you’d get N prompts at every
launch of a rebuilt binary. With one bundle, you see one prompt, click
“Always Allow”, and subsequent launches of the signed binary are silent.
Migration is automatic — first time thClaws reads the bundle, any legacy per-provider entries get pulled into the bundle and the bundle is written back.
Cross-process key visibility
The desktop GUI and the PTY-child REPL are separate OS processes. When you save a key in Settings, the GUI sets the env var for itself, but the already- running REPL child can’t see the GUI process’s env changes. To keep both in sync, every request reads the keychain live if the env var isn’t present — so a key saved in Settings is immediately usable in the Terminal tab’s REPL.
Auto-switch on key save
The tricky case: you save an Anthropic key, but config.model is still
gpt-4o (OpenAI). Without auto-switch you’d still see the red “no API key”
indicator.
thClaws handles this: right after a successful key save, if the currently
configured model’s provider has no credentials, the active model rewrites to
the newly-usable provider’s default (Anthropic → claude-sonnet-4-6, OpenAI →
gpt-4o, etc.). The sidebar flips to green within a second, and the next
chat turn just works.
Diagnostic env vars
| Env var | Effect |
|---|---|
THCLAWS_DISABLE_KEYCHAIN=1 |
Skip keychain entirely. Use for tests and to diagnose flakiness. |
THCLAWS_KEYCHAIN_TRACE=1 |
Print purple diagnostic lines every time a keychain call is made. Shows process ID and the “already loaded” flag. |
THCLAWS_KEYCHAIN_LOADED=1 |
Set automatically by the GUI after first keychain read so the spawned PTY child skips its own walk. You shouldn’t need to touch this. |
The Settings modal (GUI)
Click the gear icon in the bottom status bar. Each provider card shows:
- API Key field — pre-filled with
*****(asterisks sized to the stored key’s length, capped at 64). Typing anything replaces the sentinel; the field flips from plain text to masked. Save is disabled until you type a real new value. - Base URL field (Ollama only) — pre-filled with the current
configured value or the default placeholder. Stored in
~/.config/thclaws/endpoints.json.
DashScope is locked to its default in the Settings UI but can be
pointed at a regional endpoint with the DASHSCOPE_BASE_URL env var
if you need it (e.g. the Alibaba Cloud International URL).
Clear a key with the trash icon; the keychain entry is deleted and the env var unset for the running session.

.env files (CI, headless, quick-start)
When the keychain isn’t an option — CI runners, headless Linux boxes
without Secret Service, or when you want a key that CLI-only tools
(scripts, thclaws -p in pipelines) can read — the classic .env
path still works:
# ~/.config/thclaws/.env
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
OPENROUTER_API_KEY=sk-or-v1-...
GEMINI_API_KEY=AI...
AGENTIC_PRESS_LLM_API_KEY=llm_v1_...
DASHSCOPE_API_KEY=sk-...
OLLAMA_BASE_URL=http://localhost:11434 # defaults to this anyway
⚠️ If you use git, add
.envto.gitignoreimmediately — before you paste any key into it. An.envcommitted to a public (or even private shared) repo is the single most common way API keys leak. The project-scope./.envis especially risky because it lives in your repo root; the user-scope~/.config/thclaws/.envis outside any repo so it’s safe there, but still treat it as a secret file.A one-line fix:
bash $ echo ".env" >> .gitignore && git add .gitignoreAnd if you think you already committed one — rotate the key right away at the provider’s dashboard. Git history preserves deleted files forever; rewriting history is messy and anyone who cloned before you noticed already has the key.
Using Ollama locally
- Install Ollama:
brew install ollama(macOS) or see ollama.com. - Pull a model:
ollama pull llama3.2. - Tell thClaws:
/model ollama/llama3.2.
No API key. For a remote Ollama server, set OLLAMA_BASE_URL
(Settings modal or env var).
Using Agentic Press (hosted multi-model)
Agentic Press is a gateway that serves several backends (Gemma 3, GPT 4o-mini, Claude Sonnet, Llama 4, Qwen 3) under one API key. Great for trying different models without signing up everywhere.
- Get a key from your Agentic Press dashboard.
- Paste it into Settings → API Keys (Agentic Press) — or set
AGENTIC_PRESS_LLM_API_KEY. /model ap/gemma4-26b(or any listed model).
The ap/ prefix routes requests through the gateway. /models lists
everything the gateway currently serves.
Using OpenRouter (300+ models via one key)
OpenRouter is a unified gateway to every major LLM vendor (Anthropic, OpenAI, Google, Meta, Mistral, xAI, DeepSeek, Alibaba, and more). One API key, 300+ models.
- Get a key from openrouter.ai/keys.
- Paste it into Settings → API Keys (OpenRouter) — or set
OPENROUTER_API_KEY. - Pick a model:
/model openrouter/anthropic/claude-sonnet-4-6(or any of the hundreds listed by/models).
Model IDs follow the openrouter/<vendor>/<model> shape — copy them
from openrouter.ai/models or paste the
exact string you see in /models output.
Good for:
- Comparing responses across vendors without signing up everywhere
- Testing a new model without a separate account
- Single billing relationship for hobby / small-team use
Note: OpenRouter adds a small markup on top of each vendor’s cost. For high-volume production use, go direct to the source provider.