OpenCode: the open source coding agent, installed then taken apart

OpenCode installs with no account and responds from the first command with a free model. It also quietly loads the skills already installed for other tools if you forget to isolate your home directory.

OpenCode: the open source coding agent, installed then taken apart
Quick answer

OpenCode is an open source coding agent (Anomaly, MIT licence) that runs in a terminal, as an editor extension, or as a desktop app. Installed without touching the system, it works from the first command thanks to a free model, and supports MCP servers and the SKILL.md skills format, including skills already installed for other agents if they aren't isolated. Local models go through a generic OpenAI-compatible provider, not a dedicated Ollama integration.

An open source coding agent promises not to lock you into a single editor. You choose the model, the code stays on your machine, the licence is permissive. I installed OpenCode without touching the system or typing a single credential, before discovering that one forgotten variable export is enough to make it load the skills already installed for other tools.

OpenCode, three interfaces under the MIT licence

OpenCode is developed by Anomaly and published under the MIT licence in the anomalyco/opencode repo, which showed around 206,000 stars on 7 September 2026. The tool describes itself as “an open source agent that helps you write code in your terminal, IDE, or desktop”. It comes in three interfaces: a terminal client (TUI), an editor extension, and a desktop app in beta for macOS, Windows and Linux.

An LSP is supposed to load automatically based on the detected language, and more than 75 model providers are supported via Models.dev and the AI SDK, from paid APIs to local models. In my test mini-project, though, the log noted “all LSPs are disabled”: detecting the language isn’t enough, the matching server has to be present for the feature to activate. The site also claims “OpenCode does not store any of your code or context data.” Nothing goes out to Anomaly’s servers by default, but everything stays in a local SQLite database, as the next section shows.

OpenCode joins an already long list of command-line agents: our overview of coding agents compares them all. This article focuses on what you find by installing and taking this one apart, piece by piece.

Installing OpenCode without touching the system

The opencode-ai npm package installs a compiled binary (opencode-darwin-arm64 on this Mac) behind an opencode.exe executable. To leave nothing behind in the global locations, we used a temporary lab prefix, with a local npm cache and an isolated HOME for every command that follows:

bash
mkdir -p docker/articles/2026-09-07/lab/opencode-agent-code-open-source/{prefix,home,projet-demo}
cd docker/articles/2026-09-07/lab/opencode-agent-code-open-source

npm install opencode-ai --prefix ./prefix --cache ./prefix/.npm-cache

export HOME="$PWD/home"
./prefix/node_modules/.bin/opencode --version

The command returns 1.18.29, released on 4 September 2026 at 23:47 according to the GitHub releases, three days before this test. Fifth version in eight days: 1.18.25 to 1.18.29 between 28 August and 4 September. The npm registry confirms it’s the latest version available. The documentation also offers a curl -fsSL https://opencode.ai/install | bash script and Homebrew, Scoop or pacman packages, I stuck with npm so the whole install would fit in a folder I could delete afterwards.

The installing doesn’t necessarily stop there. Once a few commands had run from the project, OpenCode had installed 54 MB of dependencies into .opencode/node_modules on its own, without asking: its own plugin SDK, the effect library, and a more surprising presence, kubernetes-types. A .opencode/.gitignore was already in place to keep them out of Git.

Where OpenCode reads its configuration

Once HOME is isolated, the opencode debug paths command, a diagnostic sub-tool missing from the main menu, shows the XDG-style structure OpenCode built for itself inside this fake home directory (paths shortened for readability):

bash
opencode debug paths

home    .../lab/opencode-agent-code-open-source/home
config  .../home/.config/opencode
data    .../home/.local/share/opencode
cache   .../home/.cache/opencode
state   .../home/.local/state/opencode
tmp     /var/folders/.../T/opencode

The opencode.log file written to data/log spells out the exact read order at startup: first the global config folder (opencode.json, .jsonc), then, starting from the current folder, the project’s opencode.json and .opencode/opencode.json. The skill built into the binary, readable with opencode debug skill, documents the same hierarchy, with a table worth copying out in full:

Scope Path
Project config ./opencode.json, ./opencode.jsonc or .opencode/opencode.json (searched by walking up from the current folder to the Git repo’s root)
Global config ~/.config/opencode/opencode.json or .jsonc, not ~/.opencode/
Project skills .opencode/skill(s)/<nom>/SKILL.md
Global skills ~/.config/opencode/skill(s)/<nom>/SKILL.md
External skills (auto-loaded) ~/.claude/skills/<nom>/SKILL.md, ~/.agents/skills/<nom>/SKILL.md

Configurations from each scope are deep-merged, with the project taking precedence over the global one. The last row of the table carries the furthest. OpenCode reads, with no further declaration, the skills already installed for Claude Code and for other agents compatible with the open Agent Skills standard, described in our guide to SKILL.md.

Skills: how far does auto-discovery go?

To check what the bundled documentation claims, I added two decoy skills directly into the fake HOME, exactly where OpenCode says it looks for them, then reran the same diagnostic:

bash
mkdir -p home/.claude/skills/decoy-claude home/.agents/skills/decoy-agents
# a minimal SKILL.md (name + description frontmatter) in each folder
opencode debug skill

Result: four skills detected, the built-in skill, the project skill, and the two decoys, each with its full path under home/.claude/skills/… and home/.agents/skills/…. OpenCode simply respects whatever HOME variable you give it, it only looks wider than its own configuration folder.

Without HOME re-exported in the command, the same listing falls back to the machine’s real home directory and pulls in 120 skills, the ones actually installed for Claude Code and other agents. No sandbox escape in any of that. A single command with no explicit HOME is enough to reconnect a test you thought was isolated to your real home directory, and so to the skills already installed for other tools, never declared in the project. The command itself is telling: there’s no opencode skill list at the top level, you have to go through opencode debug skill, a diagnostic tool.

MCP: configuring and querying a local server

The project’s configuration file declares a local MCP server, its associated permissions, and restricts tool access for the plan agent:

json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "slug": {
      "type": "local",
      "command": ["node", "./mcp/serveur.mjs"],
      "enabled": true
    }
  },
  "permission": {
    "edit": { "*": "ask", "docs/**": "allow" },
    "bash": { "*": "ask", "git status": "allow", "git diff *": "allow", "rm *": "deny" },
    "webfetch": "deny",
    "skill": { "*": "allow" }
  },
  "agent": {
    "plan": { "tools": { "slug_*": false } }
  }
}

The server, mcp/serveur.mjs, is a self-contained Node script of about forty lines that speaks JSON-RPC 2.0 over standard input and exposes a single tool, slug_fr:

javascript
const TOOLS = [{
  name: "slug_fr",
  description: "Transforme un titre francais en slug d'URL (minuscules, sans accent).",
  inputSchema: {
    type: "object",
    properties: { titre: { type: "string", description: "Le titre a convertir" } },
    required: ["titre"]
  }
}];

function slug(titre) {
  return titre.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
    .toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
}

opencode mcp list confirms the connection:

bash
opencode mcp list

MCP Servers
  slug   connected
    node ./mcp/serveur.mjs

1 server(s)

A project skill, slug-gekkode, explains the business rule (five words maximum, no articles) and points to the tool. One round trip with opencode run triggers both in sequence (output cleaned of colour codes and icons, content unchanged):

bash
opencode run "Utilise la regle de slug du projet pour convertir ce titre : Les secrets d'une bonne architecture PHP"

> build - big-pickle
Skill "slug-gekkode"
slug_slug_fr {"titre":"Les secrets d'une bonne architecture PHP"}

Le slug genere par l'outil MCP est : les-secrets-d-une-bonne-architecture-php

Mais selon les regles du skill (max 5 mots, sans article ni preposition),
le slug correct est : secrets-bonne-architecture-php

The tool name shown, slug_slug_fr, reveals the naming convention. OpenCode prefixes every MCP tool with the name of the server declared in the config (slug), glued to the name of the exposed tool (slug_fr), hence the repetition. The model picked the skill from its description, called the tool, then reworked the result by hand to satisfy the five-word rule. A skill that complements a tool instead of replacing it behaves exactly like that. For a remote server, the documentation describes a "type": "remote" block with url and headers or oauth, plus opencode mcp auth for the OAuth flow. The subject is covered in depth in our article on building an MCP server in PHP.

Agents and permissions, rule by rule

opencode agent list shows eight agents in this project: build (the default main agent, broad access), plan (main, read and planning), compaction, summary and title (internal), explore and general (subagents), and relecteur, declared in .opencode/agents/relecteur.md:

markdown
---
description: Relit un article et signale les fautes, sans jamais modifier de fichier.
mode: subagent
temperature: 0.1
permission:
  edit: deny
  bash: deny
---
Vous relisez un article de blog en francais. Vous signalez les erreurs, vous ne corrigez rien.

Each permission accepts allow, ask or deny, matched by glob pattern, with the most specific rule winning. The setting applies to edit, bash, webfetch, skill, and even to a specific MCP tool like slug_*. The project’s plan agent illustrates the principle: its configuration disables the slug_* tool and only allows writes to .opencode/plans/*.md, never to the code. In non-interactive mode, with no terminal to answer a prompt, an ask permission isn’t left pending, it’s automatically refused:

bash
opencode run "Execute la commande shell : echo test-permission"

> build - big-pickle
! permission requested: bash (echo test-permission); auto-rejecting
Error: The user rejected permission to use this specific tool call.

$ echo $?
0

The default behaviour is therefore closed: with no human watching, OpenCode fails rather than run a command that wasn’t explicitly allowed. The exit code nonetheless stays at 0 despite the failure, a trap for anyone scripting a check around it. The subject of permissions and sandboxing, common to all these agents, is covered in depth in our article on sandboxing coding agents and their permissions.

Models: a free trial, and the case of local models

With no account and no key, opencode run "dis bonjour" works from the very first command. The default model, big-pickle (a “stealth model” according to its card), is one of six free models on OpenCode Zen, the in-house gateway. The other five are MiMo-V2.5 Free, Ling 3.0 Flash Fin Free, two variants of Nemotron 3, and Muse Spark 1.3 Contributor Free. The local SQLite database (the message table) keeps a record of the exchange. The credential and account tables stay at zero rows: no credential requested or stored for that model, and a cost of zero in the session log.

A name-brand model from the same catalogue (GPT, Claude, Gemini, Grok, DeepSeek, Qwen…) requires an OpenCode Zen account and a payment method. Billing is by the token, from $0.20 to several dollars a million depending on the model. The balance tops itself up by $20 whenever it drops below $5.

For a local model, checking is simple:

bash
which ollama
# ollama not found

The model catalogue OpenCode caches only offers one cloud provider, ollama-cloud (key OLLAMA_API_KEY), no dedicated entry for a local Ollama. For a local server, Ollama, llama.cpp or LM Studio, the documentation describes a generic OpenAI-compatible provider you declare yourself:

json
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "ollama-local": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama (local)",
      "options": { "baseURL": "http://127.0.0.1:11434/v1" },
      "models": { "mon-modele": { "name": "Mon modele local" } }
    }
  }
}

Port 11434 and the native API (/api/generate, among others) are documented at docs.ollama.com. The OpenAI compatibility that @ai-sdk/openai-compatible requires comes down to two calls, checked against an Ollama 0.34.0 running in a container:

bash
curl -s http://127.0.0.1:11434/v1/models
# {"object":"list","data":[{"id":"qwen2.5:0.5b","object":"model",
#   "created":1789038968,"owned_by":"library"}]}

curl -s http://127.0.0.1:11434/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"qwen2.5:0.5b","messages":[{"role":"user",
       "content":"Réponds par le seul mot OK."}],"max_tokens":5}'
# {"id":"chatcmpl-704","object":"chat.completion","model":"qwen2.5:0.5b",
#  "choices":[{"index":0,"message":{"role":"assistant","content":"OK."},
#   "finish_reason":"stop"}],
#  "usage":{"prompt_tokens":38,"completion_tokens":3,"total_tokens":41}}

The shape is exactly what the SDK expects: object: "list" on the catalogue, a full chat.completion object with choices, finish_reason and usage on the completion. An unknown model answers 404 with the OpenAI error object, and the Authorization header is accepted whatever its value: the provider’s apiKey field can hold anything.

What’s missing, from where we stand

The full picture of command-line coding agents, strengths and weaknesses against Claude Code, Codex, Gemini CLI and the rest, is covered separately in our dedicated comparison. The list of skills and the resolved configuration are only reachable through diagnostic commands, debug skill and debug config. Both are missing from the main menu: useful once you know about them, but not exactly discoverable. Local storage is very real: sessions, messages and tool calls all live in a SQLite database on your disk, so “no data sent to Anomaly’s servers” doesn’t mean “nothing gets written.” And free access stops at the house model: with the first name-brand model, you’re back to the account and the credit card you’d find anywhere else.

What to remember

  • OpenCode (Anomaly, MIT licence) installs without touching the system via npm install opencode-ai --prefix and an isolated HOME, version tested: 1.18.29.
  • Configuration is read at three merged levels: project (walking up to the Git root), global (~/.config/opencode, never ~/.opencode), and external skills auto-loaded from ~/.claude/skills and ~/.agents/skills.
  • Isolating HOME works, as long as you export it in every command. Forgetting it reconnects OpenCode to your real home directory, other tools’ skills included: two decoy skills confirm it, and a listing without HOME pulls in 120.
  • A project skill and a local MCP tool work together from the very first try, with no credentials, using OpenCode Zen’s free big-pickle model.
  • ask permissions are automatically refused in non-interactive mode, but the exit code stays at 0, worth watching if you script checks around opencode run.
  • Local models (Ollama, llama.cpp, LM Studio) go through a generic OpenAI-compatible provider you declare by hand, not a dedicated Ollama integration.

Common errors

Isolating HOME only half protects you If a single command forgets to export the variable, OpenCode falls back to your real home directory and loads the skills already installed for other tools.
~/.claude/skills and ~/.agents/skills are read automatically This isn't an isolation flaw: the bundled documentation confirms it (external skills, auto-loaded). On a machine already set up for Claude Code, its skills end up there with no explicit declaration.
No visible skill command The list of skills doesn't show up in opencode --help: it's hidden under opencode debug skill, a diagnostic tool that can change without notice.
~/.config/opencode, not ~/.opencode The bundled documentation explicitly flags this naming trap, the second path is silently ignored.
Exit code 0 despite a permission refusal In non-interactive mode, a refused bash command makes the tool fail, but opencode run still exits with code 0.

MCPOllamaOpenCodeSkills

Damien Flandrin Web developer since 2010, creator of Gekkode and Email Impact. Every article is tested on a real project before publication. Contact
Newsletter

New tests, tutorials and projects, by e-mail.

Reproducible tests, versioned code, dated results. Never any spam.