AI Development Tooling
Configuring OpenCode to Work Across Multiple LLM Providers
A practical, verified guide to running one coding agent against Anthropic, OpenAI, Google, and local models — with credentials that never touch your repository.
Daniel Dayto
Forward Deployed Engineer · Co-Founder & Technical Lead at Playmaker
- Published
- Reading time
- 12 min read
On This Page
Why run multiple providers
On This Page
Why run multiple providers
OpenCode's most useful property is one most coding agents don't have: it treats the model as configuration. One terminal interface, one set of project instructions, one agent system — pointed at Anthropic today, an OpenAI-compatible endpoint tomorrow, and a local model on a plane. This guide covers the configuration that makes that real, with every path, key, and command verified against the official documentation at the time of writing. OpenCode moves fast; when this article and the current docs disagree, trust the docs.
Why run multiple providers at all
Three practical reasons, none of them ideological. Cost shaping — routine work doesn't need your most expensive model, and OpenCode's small_model setting exists precisely to route lightweight internal tasks somewhere cheaper. Availability — provider outages and rate limits are real; a second configured provider turns an outage into an inconvenience. Privacy tiers — some repositories can use hosted frontier models, some client work can't, and a local model behind the same interface means the workflow doesn't change when the privacy requirements do. What multi-provider setup does not buy you is interchangeability — models behave differently on the same prompt, and pretending otherwise is how teams ship inconsistency. More on that boundary at the end.
Where configuration lives
OpenCode reads two config files, both JSON (JSONC — comments allowed), both validated by the same schema:
- Global:
~/.config/opencode/opencode.json— your personal defaults across every project. - Per-project:
opencode.jsonin the project root — checked into Git, shared with the team. The docs are explicit that this file is designed to be committed.
~/.config/opencode/opencode.json # personal defaults (global)
~/.local/share/opencode/auth.json # credentials — managed by OpenCode, never committed
~/.local/share/opencode/log/ # logs for debugging
your-project/
├── opencode.json # team config — committed
├── .opencode/agents/ # project agent definitions — committed
├── AGENTS.md # project instructions (created by /init)
└── .env # local secrets — gitignoredStart every config file with the schema line — it turns editor autocomplete into documentation:
{
"$schema": "https://opencode.ai/config.json"
}Credentials without leaks
There are two documented ways to get credentials into OpenCode, and they serve different purposes:
- 01Interactive auth. Run
/connectinside the TUI (oropencode auth loginfrom the shell), pick a provider, and paste a key or complete the browser OAuth flow. Credentials land in~/.local/share/opencode/auth.json— outside your repository, which is exactly where they belong. - 02Environment interpolation. Anywhere in
opencode.json, the value{env:VARIABLE_NAME}is replaced with the environment variable at runtime, and{file:path}reads a file's contents. This is how a committed team config references secrets without containing any.
# .env.example — copy to .env (gitignored) and fill in
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
GEMINI_API_KEY=
# Local endpoints need no keys — Ollama/LM Studio run unauthenticated on localhostOne licensing caution from the official docs: Anthropic explicitly prohibits routing Claude subscription (Pro/Max) access through third-party tools. Use API credentials with OpenCode, not subscription plumbing.
Provider configuration
OpenCode ships with built-in knowledge of 75+ providers via the AI SDK and the models.dev database, so for major providers you often need zero provider config — authenticate and go. The provider block exists for customization: OpenAI-compatible endpoints, base-URL overrides, model allowlists, and options like timeouts. A complete multi-provider project config:
{
"$schema": "https://opencode.ai/config.json",
// Default model for real work, cheaper model for lightweight internal tasks
"model": "anthropic/claude-sonnet-4-5",
"small_model": "anthropic/claude-haiku-4-5",
"provider": {
// Built-in providers usually need no block at all.
// Add one only to customize — e.g. restrict which models appear:
"anthropic": {
"whitelist": ["claude-sonnet-4-5", "claude-haiku-4-5"]
},
// OpenAI-compatible endpoint (works for any conforming gateway)
"internal-gateway": {
"npm": "@ai-sdk/openai-compatible",
"name": "Internal Gateway",
"options": {
"baseURL": "https://llm-gateway.example.com/v1",
"apiKey": "{env:GATEWAY_API_KEY}"
},
"models": {
"code-model-v2": {
"name": "Code Model v2",
"limit": { "context": 200000, "output": 65536 }
}
}
},
// Local models — see next section
"ollama": {
"npm": "@ai-sdk/openai-compatible",
"name": "Ollama (local)",
"options": { "baseURL": "http://localhost:11434/v1" },
"models": {
"qwen2.5-coder": { "name": "Qwen 2.5 Coder (local)" }
}
}
}
}The keys that matter: npm selects the AI SDK adapter package (for anything OpenAI-compatible, that's @ai-sdk/openai-compatible); options carries baseURL, apiKey, headers, and timeout; models declares what appears in the picker, with optional context/output limits so OpenCode budgets correctly. Per-provider whitelist and blacklist arrays control which of a provider's models are exposed — useful for keeping a team on approved models.
Model selection
Every model reference uses one format: provider_id/model_id — anthropic/claude-sonnet-4-5, openai/gpt-5.1-codex, ollama/qwen2.5-coder. Three places consume it: the top-level model key (your default), small_model (lightweight internal tasks like title generation), and per-agent overrides. At runtime, opencode models lists everything currently available in exactly this format, and opencode run -m provider/model "..." pins a model for a single non-interactive run.
| Provider | Auth mechanism | Provider block needed? | Typical use |
|---|---|---|---|
| Anthropic | /connect or {env:...} | Optional (whitelist, options) | Primary implementation and review work |
| OpenAI | /connect or {env:...} | Optional | Second frontier opinion; specific model strengths |
/connect; Vertex via GOOGLE_APPLICATION_CREDENTIALS | Optional | Long-context work; GCP-standard shops | |
| OpenAI-compatible gateway | {env:...} in options | Yes — npm + baseURL | Corporate gateways, OpenRouter-style routers |
| Ollama / LM Studio / llama.cpp | None (localhost) | Yes — baseURL block | Privacy-constrained repos, offline work |
Local models
OpenCode's local-model story is deliberately boring: anything that speaks the OpenAI-compatible API is a provider. The officially documented endpoints — Ollama at http://localhost:11434/v1, LM Studio at http://127.0.0.1:1234/v1, llama.cpp's server at http://127.0.0.1:8080/v1 — all use the same @ai-sdk/openai-compatible adapter shown above. Two honest caveats from production use of local models generally: tool-calling reliability varies much more across local models than across frontier APIs, and a model that fits in local memory will not match frontier repository comprehension. Local models earn their place on privacy and availability, not parity.
Restricting providers
Governance goes in the committed project config. Two documented keys control the provider surface: enabled_providers (allowlist — only these appear) and disabled_providers (blocklist). For a client repository with data-residency constraints, an allowlist plus a model whitelist is a legible, reviewable policy:
{
"$schema": "https://opencode.ai/config.json",
"enabled_providers": ["anthropic"],
"provider": {
"anthropic": { "whitelist": ["claude-sonnet-4-5"] }
},
"model": "anthropic/claude-sonnet-4-5"
}Per-agent models
OpenCode agents — defined under the agent key in config, or as markdown files in .opencode/agents/ (project) and ~/.config/opencode/agents/ (global) — each accept their own model, so role and provider decouple cleanly. A plan-only agent can run a different model than the build agent; a review subagent can run a third. Note that the older tools field is deprecated in favor of permission, which takes allow/ask/deny per capability:
---
description: Reviews diffs against the plan; never edits or runs commands
mode: subagent
model: anthropic/claude-sonnet-4-5
temperature: 0.1
permission:
edit: deny
bash: deny
---
You are a code reviewer. Read the diff and the plan. Report defects,
deviations from the plan, and risks. You do not fix anything yourself.This is the configuration seam that makes the planner–builder–reviewer pattern practical in OpenCode: roles are files, models are fields, and both are code-reviewed like everything else in the repo.
Team configuration
The pattern that works: commit opencode.json (defaults, provider policy, agent definitions), commit .opencode/agents/, commit AGENTS.md (the project-instructions file /init generates), and commit an .env.example. Keep .env gitignored and let each engineer authenticate via /connect — personal credentials in ~/.local/share/opencode/auth.json never enter the repository. The result: a new engineer clones, copies the env template, runs /connect once, and inherits the team's exact model policy and agents.
Verifying the setup
opencode auth list # which providers have credentials
opencode models # every available model, as provider/model
opencode models --refresh # refresh the cached model list
opencode run -m ollama/qwen2.5-coder "Say OK" # cheap end-to-end check per provider
opencode --print-logs # run the TUI with logs streaming to stderrTroubleshooting provider failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Auth failure on a provider | Expired/invalid credential | Re-run /connect (or opencode auth login); confirm the key is active in the provider console |
| "Model not available" | Wrong identifier format or model not exposed | Use provider_id/model_id exactly as opencode models prints it; check whitelist/blacklist |
ProviderInitError | Broken provider setup or corrupted state | Verify the provider block against the docs; as a last resort clear ~/.local/share/opencode and re-authenticate |
| Stale provider behaving oddly after upgrade | Cached provider packages | rm -rf ~/.cache/opencode and restart |
| Silent local-model failures | Endpoint not running or wrong port | curl the baseURL directly; confirm the server exposes /v1 |
| Anything unclear | — | Logs live in ~/.local/share/opencode/log/; run with --log-level DEBUG |
What's OpenCode-specific vs. provider-specific
Worth keeping the layers straight, because debugging depends on it. OpenCode owns: the config schema, {env:...} interpolation, the provider_id/model_id namespace, agent definitions, permission policy, and credential storage. The provider owns: actual model behavior, tool-calling reliability, context limits, rate limits, pricing, and outages. A provider block can make a model reachable; it cannot make it good at your task. When something misbehaves, first ask which layer you're in — config errors reproduce identically every run; provider errors vary with load, model version, and prompt.
Key takeaways
- Two files, two audiences:
~/.config/opencode/opencode.jsonfor personal defaults, a committedopencode.jsonfor team policy. Both JSONC, both schema-validated. - Secrets never enter the repo: interactive
/connectauth stores credentials in~/.local/share/opencode/auth.json; committed config references secrets only as{env:VAR}. - One model namespace:
provider_id/model_ideverywhere — default viamodel, cheap tasks viasmall_model, roles via per-agentmodelfields. - Local models are just another provider behind an OpenAI-compatible
baseURL— same interface, honestly weaker tool-calling. - No automatic fallback exists — plan provider failure as an operator runbook, not an assumption about the tool.
Sources & further reading
About the author
Daniel Dayto
Forward Deployed Engineer · Co-Founder & Technical Lead at Playmaker
Daniel Dayto builds and deploys production conversational AI systems for customer operations. His work spans voice agents, RAG assistants, CRM and dispatch integrations, multi-tenant infrastructure, and workflow automation.
Related articles
Agentic Engineering · July 24, 2026
When Multi-Agent Orchestration Actually Improves Software Delivery
A practical framework for multi-agent coding systems: when decomposition helps, what coordination actually costs, handoff contracts, an evaluation framework, and a decision matrix for choosing an architecture.
Claude Code · July 24, 2026
How to Give Claude Code Autonomy Without Losing Control
A risk-tiered permission design for Claude Code: branch and filesystem isolation, command allowlists, permission modes, sandboxing, stop conditions, and which tasks deserve full autonomy versus supervision.
Deploying AI into a real operation?
I work with teams shipping voice agents, RAG systems, and workflow automation into production. Open to Forward Deployed Engineering, Applied AI, and founding technical roles.