Skip to content
NLEN
← Apps overview

OpenCode: technical tutorial

By Ivo Donker · — created with AI assistance (writer: DeepSeek V4.1 Flash; critic: Gemini 3.8 Flash)

OpenCode: ReviewInstall & set upNewsTechnical tutorial

As of September 2026, compiled from our own notes and news archive; text machine-generated and editorially checked. Check versions and commands in the official documentation.

This tutorial covers setting up OpenCode for serious work: configuration layers, custom and local model providers (including via your own router), custom agents, pattern-based permissions, MCP servers and unattended use via opencode run, opencode serve and GitHub Actions. Keys and commands have been checked against the official documentation (as of September 2026).

Configuration: files and order

OpenCode reads opencode.json or opencode.jsonc (JSON with comments). The layers are merged, not replaced; a later layer wins for the same field:

  1. Remote configuration (.well-known/opencode, for organisations).
  2. Global: ~/.config/opencode/opencode.json.
  3. A custom path via the environment variable OPENCODE_CONFIG.
  4. Project: opencode.json in the root.
  5. Inline configuration.
  6. Managed configuration files and, on macOS, managed preferences (the highest priority).

Always put the schema line at the top for autocomplete and validation in your editor. Two types of variables keep secrets out of your files:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "anthropic/claude-opus-5-5",
  "small_model": "anthropic/claude-haiku-4-5",
  "instructions": ["./docs/conventies.md", "packages/*/AGENTS.md"],
  "share": "disabled",
  "autoupdate": "notify"
}

Custom, local and router providers

You add any service with an OpenAI-compatible API via the @ai-sdk/openai-compatible package. First the key: /connect → Other → a custom ID → paste the key. Then in the configuration:

{
  "provider": {
    "mijnrouter": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Eigen LLM-router",
      "options": { "baseURL": "https://router.example.com/v1" },
      "models": {
        "code":      { "name": "Router: code" },
        "goedkoop":  { "name": "Router: goedkoop" }
      }
    }
  },
  "model": "mijnrouter/code",
  "small_model": "mijnrouter/goedkoop"
}

This way, all OpenCode traffic goes through a single router, which manages the providers' keys itself and can track costs per client. The model names are then aliases that the router translates.

Locally with Ollama:

{
  "provider": {
    "ollama": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama (lokaal)",
      "options": { "baseURL": "http://localhost:11434/v1" },
      "models": { "qwen3-coder:30b": { "name": "Qwen3 Coder 30B" } }
    }
  }
}

For LM Studio, use http://127.0.0.1:1234/v1 as the baseURL. You must list models manually for both. Small local models often handle tool calls poorly; test a model first with a small task in Plan mode.

Agents

OpenCode has two main agents, which you switch between with Tab:

There are also subagents that you invoke with @:

You define custom agents as a markdown file in .opencode/agents/ (per project) or ~/.config/opencode/agents/ (global). An example, .opencode/agents/reviewer.md:

---
description: Beoordeelt code op beveiliging en onderhoudbaarheid
mode: subagent
model: anthropic/claude-opus-5-5
temperature: 0.1
permission:
  edit: deny
  bash: deny
---
Je bent een strenge code-reviewer. Zoek naar injectie, lekken van
geheimen en ontbrekende foutafhandeling. Noem per bevinding het
bestand en de regel. Wijzig zelf niets.

Pattern-based permissions

Every tool has a permission with the value allow, ask or deny. Known keys are read, edit, glob, grep, bash, task, skill, webfetch, websearch, external_directory and doom_loop. Most default to allow. Exceptions: external_directory (working outside your project) and doom_loop (an agent stuck in a loop) are set to ask, and read denies .env files by default.

For bash and other tools, you can choose per pattern. The last matching rule wins, so put the general rule at the top:

{
  "permission": {
    "*": "ask",
    "read": "allow",
    "grep": "allow",
    "bash": {
      "*": "ask",
      "git *": "allow",
      "npm test": "allow",
      "rm *": "deny",
      "git push *": "deny"
    },
    "webfetch": "ask"
  }
}

* stands for any characters, ? for exactly one character.

MCP servers

In opencode.json under mcp. A local server; note that command is an array:

{
  "mcp": {
    "database": {
      "type": "local",
      "command": ["npx", "-y", "mijn-db-mcp"],
      "environment": { "DATABASE_URL": "{env:DATABASE_URL}" },
      "enabled": true
    }
  }
}

A remote server:

{
  "mcp": {
    "docs": {
      "type": "remote",
      "url": "https://docs.example.com/mcp",
      "headers": { "Authorization": "Bearer {env:DOCS_TOKEN}" },
      "enabled": true
    }
  }
}

OAuth is automatically detected for external servers. Set "oauth": false if you don't want that, for example because you're working with a fixed header. With "enabled": false you temporarily disable a server without removing the configuration. Every server adds tools to the context, so only enable what you need for the project.

Unattended: run, serve and GitHub

opencode run executes a single command without an interface:

opencode run --agent plan -m anthropic/claude-opus-5-5 \
  "Beschrijf de architectuur van dit project in vijf punten"

opencode run --format json "Draai de tests en repareer wat faalt" > run.jsonl

opencode serve starts a headless HTTP server, so that you can control OpenCode from other programs. Always set a password:

OPENCODE_SERVER_PASSWORD="sterk-wachtwoord" opencode serve

Never expose that server to a network without a password: anyone who can access it can run commands on your machine.

GitHub Actions: opencode github install sets up a workflow in your repository. In the action, opencode github run runs the agent, for example in response to a comment in an issue or pull request. Set the keys as repository secrets and do not give the agent broader permissions than necessary in CI.

Troubleshooting

Documentation: opencode.ai/docs.