OpenCode MCP Setup: Configuration, Permissions and Troubleshooting
An OpenCode MCP setup connects the coding agent to external tools, data and services, but the configuration is not a direct copy of Claude Code or Cursor. OpenCode uses an mcp object in opencode.json or opencode.jsonc, with its own server types, command structure, environment handling and permission rules.
This guide covers project and global configuration, local and remote MCP servers, secret handling, tool restrictions, independent server testing, migration and the failures most likely to produce missing tools or launch timeouts. The practical aim is a setup that is easy to diagnose and narrow enough that one faulty or overpowered integration cannot silently widen the agent’s access.
OpenCode MCP setup: the quick answer
Use a project-level opencode.jsonc when the server definition belongs to one repository and can be shared without credentials. Use ~/.config/opencode/opencode.json for personal servers, machine-specific executable paths and user-wide permission defaults.
Start with one server. Test its command or endpoint outside OpenCode, add it under the top-level mcp key, run opencode mcp list, then allow or approve only the tools required for the task. Do not paste a Claude Code or Cursor block unchanged. Their common mcpServers, args and env fields need to be converted.
| Decision | Safer default | Reason |
|---|---|---|
| Configuration scope | Project for shared structure, global for personal or machine-specific values | Keeps repository configuration portable without committing credentials or local paths |
| Server type | Local only when direct machine access is required | A local MCP server runs as a process with the user’s operating-system permissions |
| Tool approval | Ask by default | Prevents a newly added server from performing write actions without review |
| Initial test | One read-only tool call | Separates connection success from permission and model-selection problems |
| Number of servers | Enable only those needed for the current workflow | Every exposed tool consumes context and gives the model another possible action |
Choose project or global configuration before adding a server
OpenCode merges configuration from several locations. The two most relevant for an individual developer are:
- Global:
~/.config/opencode/opencode.jsonfor user-wide providers, models, MCP servers and permissions. - Project:
opencode.jsonoropencode.jsoncin the repository root for project-specific settings.
Project settings take precedence over global settings among normal user-controlled files. The files are merged rather than treated as completely separate profiles, so an unexpected project entry can change a global default without the developer noticing. When troubleshooting, inspect both locations and look for duplicate server names, permission patterns or a project-level enabled value overriding what you expected.
A project file is useful for a shared documentation server, a repository-specific database schema tool, or an internal issue tracker. It should contain the connection structure, not the secret. A global file is the better home for personal utilities, absolute executable paths and servers that should follow you across repositories.
OpenCode also supports JSONC, which permits comments and is easier to maintain when the file includes several servers or explanatory permission rules. Use the .jsonc extension if you intend to add comments. Keeping strict JSON in a file named opencode.json avoids parser ambiguity and makes validation simpler.
Build the OpenCode MCP object in the format it expects
The safest starting point is a small configuration that proves discovery, start-up and permissions separately. The OpenCode MCP server documentation defines local servers with a command array and remote servers with a URL.
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"repo-docs": {
"type": "local",
"command": [
"npx",
"-y",
"@modelcontextprotocol/server-filesystem",
"./docs"
],
"cwd": ".",
"enabled": true,
"timeout": 15000
}
},
"permission": {
"*": "ask",
"read": "allow",
"glob": "allow",
"grep": "allow",
"edit": "ask",
"bash": {
"*": "ask",
"git status*": "allow",
"git diff*": "allow",
"rm *": "deny"
},
"repo-docs_*": "ask",
"external_directory": "deny"
}
}This example exposes a filesystem server only to the repository’s docs directory. The command arguments perform the most important access-control work. The repo-docs_* permission then requires approval when OpenCode calls a tool exposed by that server.
Do not assume OpenCode’s external_directory permission can contain every local MCP process. It protects OpenCode tool calls that access paths outside the workspace, but a separately spawned server still runs with the operating system rights of the user who launched it. Restrict file roots in the server command, use read-only credentials where available and consider a container or dedicated account for a server that can execute commands or query sensitive systems.
Local and remote MCP servers fail in different ways
| Server type | OpenCode structure | Best suited to | Common failure |
|---|---|---|---|
| Local | type: "local" plus a command array | Filesystem tools, local databases, custom scripts and developer utilities | Executable not found, wrong working directory, missing package runtime or insufficient file permission |
| Remote | type: "remote" plus url | Hosted issue trackers, observability services, team systems and OAuth connections | Wrong endpoint, failed authentication, blocked network access or slow tool discovery |
A local server is not automatically safer because it stays on the machine. It may have direct access to source code, SSH keys, environment variables and local databases. A remote server exposes data over a network but may offer narrower scopes, audit logs and revocable OAuth access. Judge the actual permissions, not the transport label.
Remote server with an environment-backed token
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"issue-tracker": {
"type": "remote",
"url": "https://mcp.example.com/mcp",
"oauth": false,
"headers": {
"Authorization": "Bearer {env:ISSUE_TRACKER_MCP_TOKEN}"
},
"enabled": true,
"timeout": 15000
}
},
"permission": {
"issue-tracker_*": "ask"
}
}Leave oauth unset when the remote server supports the normal OAuth discovery flow. Use oauth: false only when the provider expects a fixed API key or another header-based method. OpenCode can start the browser flow with opencode mcp auth <server-name> and diagnose OAuth discovery or connectivity with opencode mcp debug <server-name>.
Keep secrets out of project configuration without creating empty values
OpenCode supports {env:VARIABLE_NAME} substitution and {file:path} substitution. Both are preferable to storing a token directly in a project file, but they fail in different ways.
- Environment substitution: if the variable is not set, OpenCode replaces it with an empty string. The configuration can therefore look valid while authentication fails.
- File substitution: useful for a token stored under a restricted home-directory path. Keep the file limited to the expected value and protect it with operating-system permissions.
- OAuth: preferable for hosted servers that support revocable scopes and user consent. Avoid adding a bearer header as well, because competing authentication methods make failures harder to interpret.
Before blaming the MCP server, verify the secret in the same environment where OpenCode launches. A shell variable exported in one terminal does not exist in a desktop launcher, a system service, another shell profile, or a remote development session unless it is provided there as well.
# Confirm the variable exists without printing the secret
if [ -n "$ISSUE_TRACKER_MCP_TOKEN" ]; then
echo "token is set"
else
echo "token is missing"
fiPermissions should restrict tools and the system behind them
OpenCode permission rules resolve to allow, ask or deny. Most operations are permissive unless you configure them otherwise, so adding a server without reviewing its tools is a broader change than it first appears.
Tool names on an MCP server are prefixed with the server name. A server called github can therefore be covered by a rule such as github_*. Start with ask, invoke each required tool deliberately, then allow only genuinely routine read actions. Keep repository writes, issue changes, pull-request actions, deployments, database mutations and message sending behind approval.
Permission prompts are only one layer. A broad GitHub token still allows broad GitHub access once a tool call is approved. A database server using an administrator account can still perform administrator actions. Least privilege needs to exist in three places:
- OpenCode: deny or ask for tool calls by default.
- MCP server: expose only the required tools and filesystem roots.
- External service: use a read-only token, project-specific account or narrow OAuth scope.
Auto-approval should not be the first test. OpenCode’s auto mode still enforces explicit deny rules, but it turns actions that would normally require a request into automatic approvals. Establish the deny list before using it, not after the agent has already discovered a destructive tool.
Test the MCP server independently before adding it to OpenCode
Independent testing prevents a four-layer debugging problem involving the server, transport, OpenCode and the selected model. Prove the server can start and return tools/list before asking OpenCode to use it.
Test a local server
npx -y @modelcontextprotocol/inspector \
npx -y @modelcontextprotocol/server-filesystem ./docsCheck that the process starts, the tool list appears, and one contained read operation succeeds. If the Inspector cannot connect, OpenCode will not repair the server command on your behalf.
Test a remote server
npx -y @modelcontextprotocol/inspector --cli \
https://mcp.example.com/mcp \
--transport http \
--method tools/listFor an authenticated endpoint, add the expected header through the Inspector rather than pasting it into a reusable shell history. Once the endpoint is proven, add it to OpenCode and run:
opencode mcp list
opencode mcp debug issue-trackerA healthy status is not the end of the test. Ask OpenCode to list the available tools on that server, then request a single read-only call with an easily verified result. This catches the case where the connection succeeds, but tool registration, permission matching or model tool use still fails.
Troubleshoot OpenCode MCP failures in the right order
The recurring real-world failure pattern is configuration translation, not a broken protocol. Users copy a working Claude Code or Cursor entry, change the file path and assume the same keys will load. The server may never start because OpenCode expects a different object shape.
| Symptom | Likely cause | Check | Fix |
|---|---|---|---|
| Configuration error or server absent | Copied mcpServers, args or env structure | Compare the entry with OpenCode’s mcp schema | Convert to mcp, command array and environment |
ENOENT or command not found | Executable is not on OpenCode’s PATH | Run the exact command from the same launch environment | Use an absolute path, /usr/bin/env or an explicit PATH in environment |
| Server starts in a terminal but not OpenCode | Different working directory, shell profile or environment | Inspect cwd, relative paths and exported variables | Set cwd, use project-relative arguments and supply required variables explicitly |
| Authentication fails with an apparently correct config | Missing environment variable became an empty string | Confirm the variable exists without printing it | Export it in the OpenCode launch environment or use secure file substitution |
| Remote server needs sign-in | OAuth has not completed or stored credentials are stale | Run opencode mcp list and opencode mcp debug name | Run opencode mcp auth name, or log out and authenticate again |
| Tools are missing although the server is listed | Permission wildcard, disabled server, empty tool list or stale session | Check the server prefix, enabled value and tool discovery independently | Correct the permission pattern, restart the session and retest tools/list |
| Start-up or tool discovery times out | Slow package install, cold start, network latency or overloaded server | Time the server independently and inspect logs | Pre-install dependencies, increase timeout cautiously and remove unnecessary start-up work |
| Agent ignores an available tool | Too many overlapping tools or weak model tool selection | Disable unrelated servers and name the required server in the prompt | Reduce the active tool set and keep server names descriptive |
Timeouts need diagnosis, not an arbitrarily huge number
OpenCode’s current MCP configuration includes a per-server timeout, with a short default for fetching tools. Raising it can help a slow process complete its initial handshake or tool discovery, but it does not prove the server is healthy and should not be used to conceal a repeated package download, a blocked DNS lookup, or a command waiting for interactive input.
Measure three stages separately: process launch, MCP initialisation and the individual tool call. A server that takes 20 seconds to install dependencies every session needs packaging work, not a one-hour timeout. A tool that legitimately runs for several minutes needs a client and server path designed for progress and long-running tasks. Treat those as separate engineering problems.
Missing tools can be a context problem rather than a connection problem
Every MCP tool adds its name, description and schema to the agent’s available context. Large servers can expose dozens of similar tools. The model then has more choices, consumes more context and may select a generic built-in action instead of the specialist tool you expected.
Disable servers that are unrelated to the current job. Prefer one focused server over a marketplace-sized bundle. If two servers offer similar repository, browser or search functions, keep one active while testing. The same principle appears in DIY AI’s Codex CLI MCP setup guide: more integrations can reduce predictability even when every connection works.
Migrate Claude Code or Cursor MCP configuration without carrying over the wrong assumptions
| Claude Code or Cursor field | OpenCode equivalent | Migration note |
|---|---|---|
mcpServers | mcp | Move the named server entries under OpenCode’s top-level key |
command: "npx" plus args: [...] | command: ["npx", ...] | Combine the executable and arguments into one array |
env | environment | Rename the object and replace embedded secrets with environment or file substitution |
type: "stdio" | type: "local" | Keep the local launch command, but re-check PATH and working directory |
type: "http", streamable-http or a remote URL entry | type: "remote" | Use the provider’s current MCP endpoint and configure OAuth or headers separately |
| Claude local, project or user scope | OpenCode global or project config | The scope labels do not map one-to-one, so choose based on sharing and credential needs |
Cursor .cursor/mcp.json | Project-root opencode.jsonc | Move only the server structure and keep Cursor-specific settings out |
Cursor ~/.cursor/mcp.json | ~/.config/opencode/opencode.json | Check every absolute path because the clients may launch from different environments |
Claude Code local server converted to OpenCode
A common Claude Code or Cursor entry looks like this:
{
"mcpServers": {
"docs": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"./docs"
],
"env": {
"LOG_LEVEL": "warn"
}
}
}
}The OpenCode version is:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"docs": {
"type": "local",
"command": [
"npx",
"-y",
"@modelcontextprotocol/server-filesystem",
"./docs"
],
"environment": {
"LOG_LEVEL": "warn"
},
"enabled": true,
"timeout": 15000
}
},
"permission": {
"docs_*": "ask"
}
}Do not migrate permission decisions automatically. Claude Code, Cursor and OpenCode have different approval models and different defaults. Inventory the tools after conversion, then decide which actions should be allowed, asked or denied in OpenCode.
A least-privilege OpenCode MCP configuration checklist
- Place shared, secret-free server structure in the project file and personal credentials or machine paths in global configuration.
- Use
opencode.jsoncfor commented configuration and keep ordinaryopencode.jsonvalid JSON. - Add one MCP server at a time and leave unused servers disabled.
- Test the server independently with the MCP Inspector before adding OpenCode to the path.
- Restrict local filesystem roots in the server command rather than relying only on OpenCode prompts.
- Use read-only tokens, project-specific service accounts and narrow OAuth scopes.
- Set MCP tool prefixes to
askuntil each action has been observed and reviewed. - Deny destructive shell patterns and external-directory access unless a task has a documented need.
- Verify missing environment variables because OpenCode can substitute an empty string.
- Keep server names descriptive so the model and permission patterns can distinguish them.
- Test a denied action, not only a successful read. A permission setup is unproven until the blocked path behaves as expected.
- Review the active tool count. Remove overlapping servers that add context without adding a unique capability.
- Pin or pre-install local server packages for repeatable startup, rather than downloading the latest unknown package on every session.
- Re-run
opencode mcp listafter configuration changes and restart stale sessions before investigating deeper failures.
OpenCode MCP setup FAQs
Where is the OpenCode MCP configuration file?
The global file is ~/.config/opencode/opencode.json. A project can use opencode.json or opencode.jsonc in its root. Project settings override conflicting global settings among the standard configuration files.
Can OpenCode use the same MCP config as Claude Code?
Not unchanged. Convert mcpServers to mcp, combine command and args into a command array, rename env to environment, and use OpenCode’s local or remote type. Rebuild permissions rather than copying assumptions from Claude Code.
Why does OpenCode show an MCP server but not its tools?
The server may have connected without returning a usable tool list; its tools may be covered by a deny pattern; the session may be stale; or the model may be ignoring the tools in a crowded set. Verify tools/list independently; check the server-name prefix in permissions and restart with unrelated servers disabled.
Should MCP servers be configured globally or per project?
Use project configuration for a server that belongs to one repository and can be described without secrets. Use global configuration for personal utilities, private credentials and machine-specific executable paths. Avoid defining the same server name in both places unless the override is deliberate.
Does OpenCode sandbox local MCP servers?
Do not treat the OpenCode permission file as a complete process sandbox. A local MCP server is launched on the machine and can assume the current user’s rights. Restrict its arguments, credentials and operating-system access, and isolate high-risk servers where practical.
The best OpenCode MCP setup is intentionally small
A reliable configuration begins with scope, not a copied block of JSON. Put the server in the right file, translate its fields to OpenCode’s schema, prove it outside the agent, and then expose a narrow toolset behind explicit permissions.
Developers comparing OpenCode with other agents can use DIY AI’s AI coding tools comparison for the wider workflow trade-offs. For OpenCode itself, the practical rule is simpler: one server, one contained test, and one reviewed permission expansion at a time.


