OpenCode: Agents, Tools, MCP, Skills & Commands
AGENTS.md (Project Instructions)
The AGENTS.md file is OpenCode’s equivalent of Claude Code’s CLAUDE.md. It lives at the project root and injects context into every LLM interaction.
Creation
# Auto-generate from project analysis
opencode /init
The /init command scans important files in your repo (package.json, Makefile, pyproject.toml, etc.) and generates an AGENTS.md with project-specific guidance including build commands, architecture, conventions, and setup requirements. Running /init again updates the existing file.
Discovery Precedence
OpenCode walks up from the current working directory to the git worktree root, loading the first match in each category:
-
AGENTS.mdin project root (primary) -
CLAUDE.mdin project root (fallback for cross-compatibility) -
~/.config/opencode/AGENTS.md(global) -
~/.claude/CLAUDE.md(global fallback)
Only the first matching file in each category is used. Monorepo pattern: packages/*/AGENTS.md for per-package rules.
Manual Structure
# Project Name
## Overview
Brief project description and purpose.
## Architecture
- Framework, build system, key directories
- Entry points, module boundaries
## Conventions
- Naming conventions (files, variables, functions)
- Code style (formatting, imports, exports)
- Testing patterns
## Important Rules
- Never modify generated files in `build/`
- Always use attributes from antora.yml, never hardcode values
- Run `make` before committing
## Dependencies
- Key libraries and their purposes
- External services and APIs
## Common Tasks
- How to build: `make`
- How to test: `make test`
- How to deploy: `make deploy`
Planned AGENTS.md Sections for domus-* Repos
| Section | Content |
|---|---|
AsciiDoc Standards |
Attribute-first authoring, no inline TOC, partials for reuse, no hardcoded values |
Antora Conventions |
Component structure, xref format, partial/example includes |
Security Rules |
Never access .age files, no gopass show, no ~/.secrets reads |
Build Pipeline |
|
File Naming |
Document prefixes (WRKLOG, MTG, DOC, etc.), directory structure |
CLI Standards |
Senior-level commands, awk/sed for parsing, verification before AND after changes |
Supplemental Instructions
Additional instruction files can be loaded alongside AGENTS.md:
{
"instructions": [
".opencode/context/asciidoc-rules.md",
".opencode/context/security-policy.md",
"~/.config/opencode/global-rules.md"
]
}
Supports: file paths, glob patterns, and remote URLs (fetched with 5s timeout). All matched files are injected into the system prompt.
{
"instructions": [
".opencode/context/asciidoc-rules.md",
".opencode/context/security-policy.md",
"~/.config/opencode/global-rules.md",
".cursor/rules/*.md",
"https://raw.githubusercontent.com/my-org/shared-rules/main/style.md"
]
}
Disabling Claude Code Compatibility
If running OpenCode standalone:
export OPENCODE_DISABLE_CLAUDE_CODE=1 # Disable all .claude/ reading
export OPENCODE_DISABLE_CLAUDE_CODE_PROMPT=1 # Disable ~/.claude/CLAUDE.md only
export OPENCODE_DISABLE_CLAUDE_CODE_SKILLS=1 # Disable .claude/skills/ only
Cross-Compatibility with Claude Code
OpenCode and Claude Code can coexist in the same repo:
<repo>/
├── AGENTS.md # OpenCode reads this
├── .claude/
│ └── CLAUDE.md # Claude Code reads this
├── .opencode/
│ └── agents/ # OpenCode custom agents
For repos used with both tools, maintain both instruction files. Extract shared rules into a common .md file referenced by both via instructions (OpenCode) and CLAUDE.md includes.
Agent System
OpenCode ships with 4 built-in agents and supports unlimited custom agents defined in Markdown files.
Built-in Agents
| Agent | Mode | Purpose | Tools |
|---|---|---|---|
Build |
Primary |
Full-featured coding agent. Default agent for all tasks. Has complete tool access (read, write, edit, bash, etc.) |
All |
Plan |
Primary |
Read-only analysis mode. Disables file writes and bash execution. Use for architecture review, code analysis, and planning without risk of modifications. |
Read-only |
General |
Subagent |
Full tool access subagent invoked via |
All |
Explore |
Subagent |
Read-only fast codebase navigation. Invoked via |
Read-only |
Switching Agents
-
Tab key — Toggle between Build and Plan modes in TUI
-
@agent-name— Invoke a subagent in conversation -
Model picker —
Ctrl+Por configured keybind to switch models within an agent
Custom Agent Definition
Agents are Markdown files in .opencode/agents/ or ~/.config/opencode/agents/:
---
description: "Audits AsciiDoc files for hardcoded values, missing attributes, and convention violations"
mode: subagent
model: ollama/qwen-coder-14b
temperature: 0
steps: 15
permission:
allow:
- "read(**)"
- "grep(**)"
- "glob(**)"
- "list(**)"
deny:
- "write(**)"
- "edit(**)"
- "bash(*)"
color: cyan
---
# AsciiDoc Linter
You are a read-only linter for AsciiDoc files in the domus-* documentation ecosystem.
## Checks
1. Hardcoded IPs, hostnames, ports, MACs, VLANs (should use {attributes})
2. Missing :description: or :revdate: headers
3. :toc: attributes present (forbidden -- Antora UI handles TOC)
4. Missing subs=attributes+ on code blocks with {attribute} references
5. Heading level jumps (h2 -> h4 without h3)
...
Agent Properties
| Property | Values | Description |
|---|---|---|
|
String |
Shown in agent selection UI and used for dispatch |
|
|
Primary agents appear in Tab toggle; subagents invoked via |
|
|
Override the default model for this agent |
|
0-2 |
Model temperature (0 for deterministic) |
|
Integer |
Maximum turns before agent stops |
|
Object with |
Tool restrictions (glob patterns) |
|
String |
Additional system prompt (appended to markdown body) |
|
Color name |
Terminal output color for this agent |
|
0-1 |
Nucleus sampling parameter |
Planned Custom Agents
| Agent | Purpose | Model | Priority |
|---|---|---|---|
adoc-linter |
Read-only AsciiDoc convention checker |
Ollama local |
P1 |
build-fixer |
Parse Antora warnings, fix all, verify clean build |
Claude Sonnet |
P1 |
worklog-creator |
Create daily WRKLOG from template and git commits |
Ollama local |
P1 |
carryover-updater |
Increment day counts in tracker partials |
Ollama local |
P2 |
commit-reviewer |
Review staged changes for security and convention violations |
DeepSeek |
P2 |
cli-drill-master |
Generate awk/sed/jq challenges, grade answers |
Ollama local |
P3 |
| Agents that are read-only (linter, reviewer) should run on Ollama local for zero cost. Agents that write files (build-fixer, worklog-creator) should use stronger models to avoid introducing errors. |
Built-in Tools
OpenCode ships with 19 built-in tools. All tools are enabled by default. Control behavior through the permission system (allow/deny/ask per tool, per agent).
Tool Inventory
| Tool | Purpose | Risk Level |
|---|---|---|
|
Execute shell commands with optional |
High |
|
Exact string replacement with 9-level fuzzy matching chain: exact → line-trimmed → block-anchor → whitespace-normalized → indentation-flexible → escape-normalized → trimmed-boundary → context-aware → multi-occurrence. Reports LSP diagnostics after edit. |
Medium |
|
Batch multiple edits to the same file atomically. All edits succeed or none are applied. Prefer over |
Medium |
|
Create new files or overwrite existing. Controlled by |
Medium |
|
Apply unified patches. Supports: |
Medium |
|
Read file contents (default 2000 lines, max 2000 chars/line, 50KB cap). Reads directories as tree listings. Supports images and PDFs as base64 attachments. Suggests similar filenames on 404. |
Low |
|
Regex search via ripgrep. Max 100 matches. Results sorted by modification time. Max 2000 chars per match line. |
Low |
|
File pattern matching via ripgrep. Max 100 files. Sorted by modification time (most recent first). |
Low |
|
List files and directories as tree. Max 100 files. Auto-ignores: node_modules, pycache, .git, dist, build, target, vendor, .venv, etc. |
Low |
|
LSP operations: goToDefinition, findReferences, hover, documentSymbol, workspaceSymbol, goToImplementation, prepareCallHierarchy, incomingCalls, outgoingCalls. Requires |
Low |
|
Load SKILL.md from |
Low |
|
Todo list with |
Low |
|
Fetch web content. Formats: markdown (default), text, html. Max 5MB response, 30s default timeout (120s max). Auto-upgrades HTTP to HTTPS. Returns images as base64 attachments. |
Low |
|
Web search via Exa AI. Types: auto/fast/deep. Crawl modes: fallback/preferred. Default 8 results. No API key needed. Requires |
Low |
|
Ask user questions with header, options, descriptions. Supports multiple questions, multi-select, and custom text answers. First option is recommended by convention. |
None |
|
Launch subagents (General, Explore, or custom). Returns |
Varies |
|
Summarize conversation to free context window. Triggered automatically or via |
None |
Tool Enable/Disable
Disable specific tools globally or per agent:
{
"tools": {
"websearch": false,
"webfetch": false
},
"agent": {
"plan": {
"tools": {
"bash": false,
"edit": false,
"write": false
}
}
}
}
Ignore Patterns
Tools using ripgrep (grep, glob, list) respect .gitignore. To include normally-ignored files, create .ignore at project root:
!node_modules/
!dist/
!build/
Permission Configuration
Permissions use object syntax with glob patterns. Last matching rule wins.
Planned Global Permissions
{
"permission": {
"bash": {
"*": "ask",
"git *": "allow",
"git push --force *": "deny",
"make *": "allow",
"ls *": "allow",
"tree *": "allow",
"awk *": "allow",
"sed *": "allow",
"grep *": "allow",
"find *": "allow",
"jq *": "allow",
"yq *": "allow",
"curl *": "allow",
"shellcheck *": "allow",
"d2 *": "allow",
"python *": "allow",
"cargo *": "allow",
"npm *": "allow",
"gh *": "allow",
"rm -rf *": "deny",
"bash -c *": "deny",
"age -d *": "deny",
"gopass show *": "deny",
"docker *": "ask",
"systemctl *": "ask",
"ssh *": "ask",
"scp *": "ask"
},
"read": {
"*": "allow",
"*.env": "deny",
"*.env.*": "deny",
"*.env.example": "allow",
"~/.secrets/*": "deny",
"~/.age/*": "deny"
},
"edit": {
"*": "allow",
"*.env*": "deny",
"~/.secrets/*": "deny"
},
"external_directory": {
"~/atelier/*": "allow",
"/tmp/*": "allow"
}
}
}
Per-Agent Permission Override (Markdown Frontmatter)
---
permission:
edit: deny
bash: ask
webfetch: deny
---
Only analyze code and suggest changes.
Per-agent permissions narrow the global scope. Full deep-dive in the security section.
LSP Tool (Experimental)
9 LSP operations for code intelligence:
| Operation | Use Case |
|---|---|
|
Navigate to function/class definition |
|
Find all usages of a symbol |
|
Get type info and documentation |
|
List all symbols in a file |
|
Search symbols across the workspace |
|
Find implementations of an interface/abstract method |
|
Get call hierarchy item at a position |
|
Find all callers of a function |
|
Find all functions called by a function |
Enable:
export OPENCODE_EXPERIMENTAL_LSP_TOOL=true
Parameters: operation, filePath, line (1-based), character (1-based).
MCP Servers
Model Context Protocol (MCP) servers extend OpenCode with external tool capabilities. OpenCode supports both local (stdio) and remote (SSE/streamable-http) servers with full OAuth support.
Local MCP Servers (stdio)
Local servers run as child processes communicating via stdin/stdout:
{
"mcp": {
"filesystem": {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/home/evanusmodestus/atelier"],
"environment": {},
"timeout": 5000
}
}
}
Remote MCP Servers (SSE/HTTP)
Remote servers connect over HTTP with optional OAuth:
{
"mcp": {
"github": {
"type": "remote",
"url": "https://api.githubcopilot.com/mcp/",
"headers": {
"Authorization": "Bearer {env:GITHUB_TOKEN}"
}
}
}
}
OAuth Support
OpenCode handles OAuth flows automatically:
{
"mcp": {
"my-oauth-server": {
"type": "remote",
"url": "https://mcp.example.com",
"oauth": "auto"
}
}
}
OAuth modes:
-
"auto"— Attempt OAuth if server requests it (default) -
"preregistered"— Use pre-registered client credentials -
"disabled"— Skip OAuth entirely
Per-Agent MCP Tool Control
MCP tools can be enabled or disabled per agent via glob patterns in agent permissions:
---
permission:
allow:
- "mcp:filesystem(*)"
deny:
- "mcp:github(*)"
---
MCP CLI Management
# List configured MCP servers
opencode mcp list
# Authenticate with an MCP server
opencode mcp auth <server-name>
# Debug MCP connection
opencode mcp debug <server-name>
# Logout from MCP server
opencode mcp logout <server-name>
Planned MCP Servers
| Server | Capabilities | Type | Priority |
|---|---|---|---|
GitHub MCP |
PR management, issue tracking, code search |
Remote |
P1 |
Antora MCP |
Validate xrefs, check playbook, list components |
Local |
P1 |
netapi MCP |
Query ISE sessions, Vault PKI, DNS records |
Local |
P2 |
gopass MCP |
Query paths/metadata (NEVER secrets) |
Local |
P2 |
rmapi MCP |
List/organize reMarkable documents |
Local |
P3 |
MCP vs Built-in Tools
| Aspect | Built-in Tools | MCP Servers |
|---|---|---|
Deployment |
Bundled with OpenCode |
External processes or remote services |
Latency |
Instant (in-process) |
Process/network overhead |
Customization |
Permission overrides only |
Fully custom capabilities |
Use case |
Standard operations (read, write, grep, bash) |
Domain-specific integrations (GitHub, databases, APIs) |
Skills
Skills are reusable instruction sets loaded on demand by agents. They are SKILL.md files with YAML frontmatter discovered from multiple locations.
Discovery Paths (Precedence)
OpenCode searches these paths for skills:
| Path | Scope |
|---|---|
|
Project-scoped (highest priority) |
|
Cross-compatible with Claude Code |
|
Alternative project path |
|
Global |
OpenCode reads .claude/skills/ directories. This means existing Claude Code skills work in OpenCode without modification. This is a key migration advantage.
|
Skill File Format
---
name: deploy
description: Deploy documentation changes to Cloudflare Pages
---
## Deploy Workflow
1. Check for uncommitted changes
2. If uncommitted: commit with provided message or ask
3. Push spoke repo to GitHub
4. Trigger Cloudflare Pages rebuild via domus-docs empty commit
5. Report deployment URL
## Steps
### Step 1: Validate
- Verify repo name starts with `domus-`
- Check git status for uncommitted changes
### Step 2: Push
```bash
git push origin main
```
...
Dynamic Context Injection
Skills support shell command injection via ! syntax:command
## Current State
**Repository:** !`basename $(git rev-parse --show-toplevel)`
**Branch:** !`git branch --show-current`
**Status:**
!`git status --short`
Commands wrapped in ! execute before the skill is loaded, injecting live state into the context.…
Existing Skills (Migrated from Claude Code)
| Skill | Purpose | Status |
|---|---|---|
|
Push spoke repo + trigger Cloudflare Pages rebuild |
Ready (cross-compatible) |
|
Create daily WRKLOG with standard partials |
Ready (cross-compatible) |
|
Create collaboration session log |
Ready (cross-compatible) |
Planned Skills
| Skill | Purpose | Priority |
|---|---|---|
|
Random CLI drill from mastery curriculum |
P2 |
|
Run audit-worklogs.sh with analysis |
P2 |
|
Generate reMarkable workbook for topic |
P3 |
|
Switch active model with context about cost/capability |
P2 |
Custom Slash Commands
Slash commands are Markdown files that define reusable prompts invoked via /command-name in the TUI.
Built-in Commands
| Command | Purpose |
|---|---|
|
Analyze project and generate |
|
Undo last file change (requires snapshot: true) |
|
Redo last undone change |
|
Share current session as a link |
|
Show help and available commands |
|
Connect to a remote OpenCode server |
|
List available models across all providers |
|
Switch theme interactively |
Custom Command Format
Location: .opencode/commands/ or ~/.config/opencode/commands/
---
description: Audit AsciiDoc files in the current component for convention violations
---
Analyze all .adoc files in this Antora component for:
1. Hardcoded IP addresses, hostnames, ports (should use {attributes})
2. Missing :description: or :revdate: headers
3. Presence of :toc: attributes (forbidden)
4. Code blocks with {attributes} missing subs=attributes+
5. Heading level jumps
Report findings as a numbered list with file:line references.
Do NOT make any changes -- report only.
File name becomes the command name: audit-adoc.md → /audit-adoc
Placeholders
Commands support argument injection:
---
description: Search codebase for a pattern and explain matches
---
Search the entire codebase for `$ARGUMENTS` and explain:
1. What each match does
2. Whether it follows project conventions
3. Suggestions for improvement
Usage: /search-explain "function.*export"
Available placeholders:
-
$ARGUMENTS— All arguments as a single string -
$1,$2, … — Positional arguments -
!— Shell command output injectioncommand -
@filepath— File content injection
Implemented Custom Commands
| Command | Purpose | Status |
|---|---|---|
|
Lint AsciiDoc files for convention violations (delegates to adoc-linter agent) |
Implemented |
|
Run |
Implemented |
|
Estimate token usage and cost for current session |
Implemented |
|
Push spoke repo + trigger Cloudflare Pages rebuild (parity with Claude Code) |
Implemented |
|
Create daily WRKLOG with standard partials (parity with Claude Code) |
Implemented |
|
STD-001 project management — create, audit, add phases/features, status (parity with Claude Code) |
Implemented |
|
Capture idea → scaffold STD-001 project (parity with Claude Code) |
Implemented |
|
Save CLI commands to quick-commands partial with tags (parity with Claude Code) |
Implemented |
|
Create collaboration session log with commits, deliverables, teaching points (parity with Claude Code) |
Implemented |
Parity with Claude Code Skills
| Claude Code Skill | OpenCode Command | Status |
|---|---|---|
|
|
✅ Parity |
|
|
✅ Parity |
|
|
✅ Parity |
|
|
✅ Parity |
|
|
✅ Parity |
|
|
✅ Parity |
Planned Custom Commands
| Command | Purpose | Priority |
|---|---|---|
|
Show active provider, model, context usage |
P2 |
|
Check today’s worklog completeness |
P2 |
Custom Tools
Define custom tools that the LLM can call. Tools are TypeScript/JavaScript files in .opencode/tools/ (project) or ~/.config/opencode/tools/ (global).
Tool Definition
// .opencode/tools/query-db.ts
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Query the project database",
args: {
query: tool.schema.string().describe("SQL query to execute"),
limit: tool.schema.number().optional().describe("Max rows to return"),
},
async execute(args, context) {
// context provides: agent, sessionID, messageID, directory, worktree
const result = await runQuery(args.query, args.limit)
return JSON.stringify(result)
},
})
Naming Convention
-
Filename becomes tool name:
query-db.ts→query-db -
Multiple exports create
<filename>_<exportname>:
// .opencode/tools/database.ts
export const query = tool({ ... }) // -> database_query
export const migrate = tool({ ... }) // -> database_migrate
Argument Schema
Uses Zod schema validation via tool.schema:
args: {
path: tool.schema.string().describe("File path"),
recursive: tool.schema.boolean().optional().describe("Search recursively"),
pattern: tool.schema.string().regex(/^[a-z]+$/).describe("Search pattern"),
tags: tool.schema.array(tool.schema.string()).describe("Filter tags"),
}
Context Parameters
The execute function receives context:
| Parameter | Value |
|---|---|
|
Current agent name |
|
Current session identifier |
|
Current message identifier |
|
Project working directory |
|
Git worktree path |
Precedence
Custom tools override built-in tools with matching names. A custom read.ts replaces the built-in read tool.
Dependencies
If your tool needs npm packages, add a package.json in the tools directory:
// .opencode/package.json
{
"dependencies": {
"better-sqlite3": "^11.0.0"
}
}
OpenCode runs bun install automatically.
Multi-Language Tools
Tools can invoke scripts in any language:
import { tool } from "@opencode-ai/plugin"
import { $ } from "bun"
export default tool({
description: "Run Python analysis script",
args: {
file: tool.schema.string().describe("File to analyze"),
},
async execute(args) {
const result = await $`python3 scripts/analyze.py ${args.file}`.text()
return result
},
})
Planned Custom Tools
| Tool | Purpose | Priority |
|---|---|---|
|
Check xrefs and attributes against antora.yml |
P1 |
|
Increment day counts in tracker partials |
P2 |
|
Generate worklog from template with today’s date |
P2 |
|
Estimate token cost for a given operation before running |
P3 |
LSP Servers (30+)
OpenCode auto-detects and launches Language Server Protocol servers for the LLM. After every file edit, OpenCode reports LSP diagnostics (up to 20 errors per file, up to 5 affected files) — the LLM sees compiler/linter errors immediately and can self-correct.
Built-in Servers
| Server | Languages | Auto-Download |
|---|---|---|
|
TypeScript, JavaScript |
Yes |
|
Python |
Yes |
|
Rust (rust-analyzer) |
Yes |
|
Go |
Yes |
|
Lua |
Yes |
|
Bash, Shell |
Yes |
|
C, C++ |
Yes |
|
C# |
Yes |
|
Dart |
Yes |
|
Deno TypeScript |
Yes |
|
Elixir |
Yes |
|
JavaScript/TypeScript linting |
Yes |
|
F# |
Yes |
|
Gleam |
Yes |
|
Haskell |
Yes |
|
Java |
Yes |
|
Julia |
Yes |
|
Kotlin |
Yes |
|
Nix |
Yes |
|
OCaml |
Yes |
|
JavaScript/TypeScript linting |
Yes |
|
PHP |
Yes |
|
Prisma schema |
Yes |
|
Ruby |
Yes |
|
Swift |
Yes |
|
Svelte |
Yes |
|
Terraform |
Yes |
|
Typst |
Yes |
|
Vue |
Yes |
|
YAML |
Yes |
|
Zig |
Yes |
|
Astro |
Yes |
|
Clojure |
Yes |
Configuration
Disable All LSP
{
"lsp": false
}
Disable Specific Server
{
"lsp": {
"typescript": {
"disabled": true
}
}
}
Custom LSP Server
{
"lsp": {
"custom-lsp": {
"command": ["custom-lsp-server", "--stdio"],
"extensions": [".custom"],
"env": {
"MY_VAR": "value"
},
"initialization": {}
}
}
}
Properties: disabled, command, extensions, env, initialization.
Disable Auto-Downloads
export OPENCODE_DISABLE_LSP_DOWNLOAD=true
LSP Tool vs LSP Servers
Two different things:
| Concept | Purpose |
|---|---|
LSP Servers (this section) |
Background processes providing diagnostics, completions, and code intelligence to the LLM automatically after every edit |
LSP Tool (experimental) |
Explicit tool the LLM can call for goToDefinition, findReferences, hover, etc. Requires |
Relevant LSP Servers for Our Stack
| Server | Our Use Case |
|---|---|
|
Shell scripts across all repos |
|
Python scripts (remarkable generators, automation) |
|
Rust projects (netapi, Kora) |
|
Neovim configuration (domus-nvim) |
|
YAML configs (antora.yml, docker-compose, etc.) |
|
OpenCode plugins (.ts files) |
| LSP diagnostics after edits mean the LLM catches type errors, undefined variables, and syntax issues in real-time. This is a significant quality improvement — Claude Code has no equivalent automatic feedback loop. |