Skip to content
Yoink

Use any provider in opencode

To give opencode a custom provider, yoink adds a block under provider in ~/.config/opencode/opencode.json, choosing the AI SDK package that matches the endpoint. It edits the file in place, so JSONC comments and your own formatting survive every re-sync. Save the key with yoink add, then run yoink connect <name> --to opencode.

On this page

At a glance

opencode facts
FactValue
Harness idopencode
Config yoink writes~/.config/opencode/opencode.json
FormatJSON or JSONC
Protocols, in order of preferenceopenai-chat, anthropic-messages, openai-responses
Default model"model": "<provider>/<model>"
Path overrideXDG_CONFIG_HOME
Providers at oncemany, side by side
Experimentalno
Presets that reach it7 of 7: openai, kimi-code, moonshot, openrouter, deepseek, zai, ollama
Connect commandyoink connect <name> --to opencode

Generated from the yoink CLI source and docs/harnesses.md.

What yoink writes

opencode loads each custom provider through an AI SDK package named in npm. yoink picks it from the protocol it connects with:

Protocol yoink picksnpm packageoptions.baseURL
OpenAI Chat@ai-sdk/openai-compatiblethe provider URL as saved, usually ending in /v1
Anthropic Messages@ai-sdk/anthropicthe provider URL with /v1 appended
OpenAI Responses@ai-sdk/openaithe provider URL as saved

Here is the block for an OpenRouter key saved as openrouter, with the key redacted:

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "openrouter": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "OpenRouter",
      "options": {
        "baseURL": "https://openrouter.ai/api/v1",
        "apiKey": "<your key>"
      },
      "models": {
        "z-ai/glm-4.7": {
          "name": "GLM-4.7",
          "tool_call": true,
          "reasoning": true,
          "limit": { "context": 204800, "output": 131072 }
        }
      }
    }
  },
  "model": "openrouter/z-ai/glm-4.7"
}

Every model gets tool_call: true, plus reasoning and limit from models.dev. A model that accepts images also gets modalities with its input types. The custom provider section of opencode's docs describes the same npm, options.baseURL and models shape for hand-written entries.

Comments and formatting survive

yoink never reparses and reprints your whole config. It changes only the provider.<id> block and, when asked, the model key, as text edits in place. If your file is opencode.jsonc with comments, the comments are still there after a connect, a model change, or a disconnect. If opencode.json does not exist yet, yoink creates it with opencode's $schema line.

Inside a model entry, yoink owns name, tool_call, reasoning, limit and modalities. Any other key you add to a model by hand, such as a custom option, is kept on re-sync.

Which providers work

opencode accepts all three protocols, so every preset reaches it, including ollama for local models. Its order is OpenAI Chat, then Anthropic Messages, then OpenAI Responses. Two results follow from that order:

  • The openai preset offers Responses and Chat, and lands in opencode as @ai-sdk/openai-compatible because Chat comes first.
  • A gateway that speaks only Responses and Anthropic Messages lands as @ai-sdk/anthropic, not @ai-sdk/openai.
yoink connect openrouter --to opencode
yoink connect deepseek --to opencode --default <model>
yoink connect ollama --to opencode

Set the default model

--default <model> sets the top-level "model" key to <provider>/<model>, which is the format opencode expects. Leave it out and your current default stays. On disconnect, yoink clears model and small_model only when they start with that provider's id.

Backups and disconnect

The first change to an existing opencode.json saves opencode.json.yoink.bak once (see Backups). yoink disconnect openrouter --from opencode removes provider.openrouter, and drops the provider object entirely if it is left empty.

Detection and gotchas

  • Desktop app. yoink treats opencode as installed when the opencode binary is on your PATH, when ~/.config/opencode exists, or when it finds the OpenCode desktop app (/Applications/OpenCode.app on macOS, /opt/OpenCode or the .desktop file on Linux, %LOCALAPPDATA%\Programs\OpenCode\OpenCode.exe on Windows).
  • XDG_CONFIG_HOME. When it is set, the config is read from $XDG_CONFIG_HOME/opencode instead of ~/.config/opencode.
  • Import. yoink import skips entries whose apiKey is {env:VAR}, {file:...}, $VAR or !command, since yoink cannot sync a key it never sees.
  • Restart opencode after a change so it reloads the file.

FAQ

Will yoink strip the comments from my opencode.jsonc?

No. yoink edits only the provider block and the model key in place, and leaves the rest of the text, comments included, as it was.

Why does my OpenAI key show up as @ai-sdk/openai-compatible?

opencode's preference order puts OpenAI Chat first, and yoink takes the first protocol the provider offers. The openai preset has a Chat endpoint, so opencode gets the OpenAI-compatible package instead of @ai-sdk/openai.

  • Use OpenRouter in your coding agents

    Add your OpenRouter key once and yoink writes it into Claude Code, opencode, pi, Qwen Code, Crush and 7 more. Codex is not supported: no Responses endpoint.

  • One API key in every coding agent

    Add an API key once and yoink writes it into the native config of up to 13 coding agents: pi, opencode, codex, Claude Code, Qwen Code, Crush and more.

  • Connect a custom OpenAI- or Anthropic-compatible provider

    Paste a base URL and key, and yoink probes /v1/models, /v1/messages, /chat/completions and /responses to find what a gateway or vLLM server speaks.

  • Use any provider in pi

    Connect any OpenAI- or Anthropic-compatible provider to pi. yoink writes ~/.pi/agent/models.json with model limits from models.dev and sets the default.

  • Use any provider in omp

    Connect a provider to omp. yoink writes ~/.omp/agent/models.yml (or models.yaml), carries over a legacy models.json and sets modelRoles.default in config.yml.

  • Use a custom provider in Codex CLI

    yoink writes a [model_providers] table into ~/.codex/config.toml with wire_api = "responses". Codex needs a Responses endpoint, so few presets fit.