opencode-config-valid¶
opencode.json and opencode.jsonc must parse and use keys and MCP server shapes OpenCode reads
| Severity | error (auto) |
| Autofix | - |
| Since | v0.20.0 |
| Repo Types | opencode |
| Category | OpenCode |
Why¶
opencode.json is where an OpenCode project declares its MCP servers, its
agents and its slash commands. It ships in the repository, so a shape its
loader rejects stops OpenCode from starting for everyone on the team, and a
misspelled key fails quietly: OpenCode does not find the key it wanted and
carries on with a default. An MCP server whose command was
written as a string rather than an argv array makes OpenCode refuse to
start at all.
OpenCode configuration schemas have evolved: 1.x spellings (agent, command,
permission, flat MCP server declarations) and 2.0 spellings (agents, commands,
permissions, nested mcp.servers) are both supported.
OpenCode merges agent/agents and command/commands by entry name.
Different names may appear in both sections; only conflicting definitions of
the same name are reported. One-to-one renamed settings still accept either
spelling but report when both are present.
MCP servers may be named servers or timeout, including a bare 1.x
{"enabled": false} toggle. The value shape distinguishes these entries from
the 2.0 server map and global timeout. Nested servers named type, command
or enabled retain their own entries.
The MCP servers declared in OpenCode configuration are also evaluated by policy
rules such as mcp-prohibited.
Severity¶
Errors — OpenCode refuses to load a project configuration with any of
these, so opencode exits before it starts:
- The top-level document is not an object, or the file has syntax errors
(comments and trailing commas in
.jsoncare supported). - An agent or command section that is not an object, an entry that is not
an object, a missing or non-string
template, or an entry field of the wrong type. - An MCP server with a missing or unknown
type, acommandthat is not a non-empty array of strings, a non-string or emptyurl, anenvironment,headersoroauththat is not an object (oauth: falseis the documented way to switch OAuth off), atimeoutthat is neither a number nor an object ofstartup/catalog/execution/request, or a non-booleanenabled/disabled. - Committed credentials or secrets detected in MCP server URLs, headers, or environment mappings.
The one shape OpenCode tolerates is a server carrying a boolean enabled:
the 1.x mcp union has a bare {"enabled": …} toggle branch that ignores
other properties, so a broken server with enabled loads as a toggle and
simply never starts. Its shape findings are warnings.
Warnings — the file loads, but one setting is dead: both spellings of one
renamed key (including the 1.x and 2.0 OAuth field names), a server declared
under both layouts at once, a $schema that is not a string, and a $schema
pointing at https://opencode.ai/tui.json, which describes tui.json rather
than this file.
Information-level findings never fail a build:
- An unrecognized top-level key. OpenCode's schema changes weekly, so a key
this release has not heard of is more likely new than wrong —
extra-keysaccepts it without waiting for a skillsaw release. - An unrecognized key on an MCP server. Same reasoning, same remedy:
extra-keyscovers these too. - A
$schemathat is neither the documented URL nor the TUI one. A vendored or mirrored copy is legitimate, so this is a note rather than a defect.
Examples¶
Bad — a Claude-shaped MCP server in an OpenCode config, and a file that declares one setting twice:
{
"$schema": "https://opencode.ai/config.json",
"agent": { "reviewer": { "prompt": "Review this change." } },
"agents": { "reviewer": { "system": "Review this change." } },
"mcp": {
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}
type: "stdio" is not a transport OpenCode knows, command must be the
argv array, there is no args key, and agent/agents define reviewer
differently. OpenCode merges those sections and keeps the agent entry when
names overlap. The unknown transport is reported first and on its own: the rest
of a server's shape depends on which transport it is, so those checks resume
once type is fixed and the file is linted again.
Good, 1.x spelling — comments and a trailing comma are fine:
{
"$schema": "https://opencode.ai/config.json",
// Local servers are spawned directly, so command is argv.
"mcp": {
"playwright": {
"type": "local",
"command": ["npx", "-y", "@playwright/mcp@latest"],
"enabled": true,
}
},
"agent": {
"reviewer": {
"description": "Reviews a diff for correctness bugs",
"prompt": "Review this change.",
"disable": false
}
}
}
Good, 2.0 spelling — the same configuration after migrating:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"playwright": {
"type": "local",
"command": ["npx", "-y", "@playwright/mcp@latest"],
"disabled": false,
"timeout": { "catalog": 30000, "execution": 30000 }
}
}
},
"agents": {
"reviewer": {
"description": "Reviews a diff for correctness bugs",
"system": "Review this change.",
"disabled": false
}
}
}
How to fix¶
- Give every MCP server a
typeoflocalorremote. Alocalserver needscommandas a non-empty array of strings; aremoteserver needs aurl. The first command element must name an executable; later arguments may be empty or whitespace strings and are passed through unchanged. - A command entry must include a string
template. An empty template is valid, including commands whose prompt is supplied by a plugin. - Entries with different names may be split between
agent/agentsorcommand/commands; OpenCode merges them. Keep only one definition when the same name occurs in both sections. For one-to-one settings, keep one spelling:promptorsystem,enabledordisabled.enabledanddisabledare the same switch with the sense inverted, so a server carrying both is saying two different things. - Replace a committed credential with OpenCode's substitution syntax:
{env:VAR} and {file:./path} both work, and skillsaw recognises them as
placeholders.
- For a key newer than this skillsaw release, accept it without waiting. One list covers both places a key can be unrecognized — the top level and an MCP server entry:
rules:
opencode-config-valid:
extra-keys:
- somethingNew # a new top-level key
- elicitation # a new key on an MCP server
Configuration¶
| Parameter | Description | Default |
|---|---|---|
extra-keys |
Additional config keys to accept, at the top level or on an MCP server entry, for keys newer than this skillsaw release | [] |
Run skillsaw explain opencode-config-valid to see this documentation and the rule's effective configuration in your terminal.