Large Language ModelsGenerate imagesGenerate videos

OpenCode MCP Config: Add Servers, OAuth and Timeout Fixes

Wire MCP servers into OpenCode with the exact opencode.json fields for local and remote setups, see how OAuth sign-in works and why it stalls, and fix the timeout errors that kill tools at startup or after 60 seconds of work.

OpenCode MCP Config: Add Servers, OAuth and Timeout Fixes
Cristian Da Conceicao
Founder of Picasso IA

You add an MCP server to opencode.json, restart the terminal, and the tools never show up. Or they show up, the browser login opens, and the callback never lands. Or everything works until the first slow call dies with a timeout. Those three failures account for most OpenCode MCP config trouble, and each one has a short, boring fix.

This article walks through the exact fields OpenCode reads, the commands that tell you what is wrong, and the settings that end the guessing. The snippets follow the official OpenCode MCP docs as checked in October 2026, so paste them as written and change only the names, paths and URLs.

Technician pressing an ethernet cable into a patch panel port

Where OpenCode Reads Its Config

OpenCode does not pick one config file and ignore the rest. It merges every source it finds, and when two sources set the same field, the later one in the order below wins. That is why a server you "removed" from the project file keeps coming back: it is still defined in your global file.

Config Locations and Merge Order

OrderSourceBest used for
1Remote config from .well-known/opencodeOrganization defaults
2Global ~/.config/opencode/opencode.jsonServers you want everywhere
3Path in the OPENCODE_CONFIG variableA one-off or CI file
4opencode.json in the project rootRepo-specific servers
5.opencode directoriesAgents, commands, plugins
6OPENCODE_CONFIG_CONTENT variableInline overrides
7Managed system settingsRules enforced by an admin

Both JSON and JSONC (JSON with comments) work. Adding "$schema": "https://opencode.ai/config.json" at the top gives your editor autocomplete and red underlines on typos. Typos are the most common reason a server is silently ignored, so the schema line pays for itself within minutes.

💡 Tip: When a server behaves strangely, check the global file first. A stale entry there can override a perfectly good project entry.

Use Variables, Not Pasted Secrets

OpenCode substitutes two placeholders anywhere in the config. {env:NAME} reads an environment variable, and {file:path} inlines the contents of a file. Use them for every token, so the config stays safe to commit.

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "tracker": {
      "type": "remote",
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer {env:TRACKER_TOKEN}"
      }
    }
  }
}

Relative file paths resolve from the config directory, while paths starting with / or ~ are absolute. If the variable is missing from the shell that launched OpenCode, the server never sees a valid token and answers 401, which looks exactly like a wrong token. Export the variable in the same shell, then start OpenCode from it.

Overhead view of an oak desk with a laptop, notebook and coffee

Add Local and Remote Servers

Everything lives under one top-level field called mcp. Each child is a server with a name you choose, and that name becomes the prefix of its tools, so pick short, lowercase names without spaces.

A Minimal Local Server

A local server is a process OpenCode starts for you and talks to over standard input and output.

{
  "mcp": {
    "files": {
      "type": "local",
      "command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/home/dev/projects"],
      "enabled": true,
      "timeout": 15000
    }
  }
}

Two fields are required: "type": "local" and command. The part that trips people up is that command is an array of strings, one entry per argument, not a single shell string. Writing "command": "npx -y some-server" is the quickest way to get a server that never starts.

Environment Variables and Working Directory

Local servers often need credentials or a specific folder. environment passes variables to the child process, and cwd sets its working directory. Relative paths in cwd resolve from the workspace.

{
  "mcp": {
    "reports": {
      "type": "local",
      "command": ["node", "./tools/reports-server.js"],
      "cwd": "./mcp",
      "environment": {
        "API_TOKEN": "{env:REPORTS_API_TOKEN}",
        "LOG_LEVEL": "info"
      }
    }
  }
}

Developer typing on a laptop in a sunlit co-working space

A Remote Server With Headers

A remote server is already running somewhere else, so OpenCode only needs an address and, usually, a credential.

{
  "mcp": {
    "docs": {
      "type": "remote",
      "url": "https://mcp.example.com/mcp",
      "headers": {
        "Authorization": "Bearer {env:DOCS_MCP_TOKEN}"
      },
      "oauth": false,
      "timeout": 20000
    }
  }
}

Setting "oauth": false is the right move for any server that authenticates with a static token. Without it, OpenCode treats a 401 as a signal to start an OAuth login, which is confusing when the real problem is a bad token.

Long aisle of black server racks seen from a low angle

Check the Result With mcp list

Run opencode mcp list after every edit. It prints each configured server and its status, so you know in two seconds whether a server connected, needs authentication or failed. Do this before you open a session and wonder why the tools are missing.

Here is the full field reference in one place:

FieldLocalRemoteWhat it does
typerequiredrequiredlocal or remote
commandrequiredn/aArray of strings that starts the process
cwdoptionaln/aWorking directory for the process
environmentoptionaln/aVariables passed to the process
urln/arequiredServer endpoint
headersn/aoptionalCustom HTTP headers
oauthn/aoptionalAn object, or false to turn OAuth off
enabledoptionaloptionalSwitch a server on or off without deleting it
timeoutoptionaloptionalMilliseconds, default 5000

💡 Tip: Set "enabled": false on servers you only need now and then. The entry stays in the file, and nothing loads into your session until you flip it back.

Fix OAuth Sign-In Problems

Remote servers that follow the MCP authorization flow need almost no setup. When OpenCode receives a 401, it starts OAuth by itself, registers as a client through Dynamic Client Registration (RFC 7591), opens your browser, and waits for the redirect. The resulting tokens are stored in ~/.local/share/opencode/mcp-auth.json.

How Automatic OAuth Works

For a server that supports it, the whole setup is two fields and one command.

  1. Add the server with only type and url.
  2. Run opencode mcp auth tracker, replacing tracker with your server name.
  3. Approve the request in the browser tab that opens.
  4. Run opencode mcp list and confirm the server shows as connected.

Four commands handle the full lifecycle:

CommandWhat it does
opencode mcp auth <name>Starts the login flow
opencode mcp listShows servers and their auth status
opencode mcp logout <name>Deletes the stored credentials
opencode mcp debug <name>Diagnoses connection and OAuth problems

When a login that worked last week suddenly fails, the cause is usually a stale or revoked token. Run opencode mcp logout <name>, then opencode mcp auth <name> again, and you start from a clean slate.

Hands holding a small black USB security token above a laptop

Pre-Registered Clients and Scopes

Some providers refuse dynamic registration and want you to register an app by hand. In that case, give OpenCode the client details it would otherwise have created itself.

{
  "mcp": {
    "tracker": {
      "type": "remote",
      "url": "https://mcp.example.com/mcp",
      "oauth": {
        "clientId": "{env:TRACKER_CLIENT_ID}",
        "clientSecret": "{env:TRACKER_CLIENT_SECRET}",
        "scope": "tools:read tools:execute"
      }
    }
  }
}

The scope value is a single string with scopes separated by spaces, exactly as the provider documents them. Ask for the smallest set that works. A broad scope that the provider rejects produces a login page error that says nothing useful about which scope was the problem.

Login Fails on a Remote Machine

This is the one that burns an afternoon. OpenCode listens on a local callback port while you sign in, and reports from users put the default at 19876. If OpenCode runs on a remote host over SSH, your browser on the laptop redirects to 127.0.0.1:19876 on the laptop, where nothing is listening. The approval succeeds, the callback never arrives, and the command eventually times out.

The fix is a port forward from your laptop to the remote host:

ssh -o ExitOnForwardFailure=yes -L 127.0.0.1:19876:127.0.0.1:19876 user@remote-host

Run opencode mcp auth <name> inside that SSH session, open the printed URL in your local browser, and the callback now travels through the tunnel.

Recent builds also accept callbackPort and redirectUri inside the oauth object, for providers that require a fixed, pre-registered callback. The redirect URI must use http:// with localhost, 127.0.0.1 or [::1] and an explicit port. If you change the port, forward that same port. Check the schema in your editor before relying on either field, since older versions will not know them.

Developer working on a laptop inside a moving train carriage

Timeout Fixes That Work

There are two different timeouts, and mixing them up wastes hours. One decides how long OpenCode waits for a server to start and list its tools. The other decides how long a single tool call may run once the server is up.

The Startup Default

The timeout field is in milliseconds and defaults to 5000 for both local and remote servers. Five seconds is fine for a small script. It is not enough for npx -y on a cold cache, because the package has to download before the server even starts. The server then shows as failed and its tools never load.

Fix it in this order:

  1. Raise the field for that server only. Set "timeout": 15000 or 30000 on the slow entry and leave the others alone.
  2. Remove the download. Install the package globally with npm install -g, then point command at the installed binary. Startup drops to a fraction of a second.
  3. Test the endpoint for remote servers. Run a plain curl against the URL. If curl is slow too, the problem is network latency or the server, not your config.
SymptomLikely causeFix
Fails after about 5 secondsDefault timeoutRaise it to 15000 or higher
Fails only on a fresh machinenpx downloading the packageInstall it globally first
Fails only on a hotel networkSlow DNS or TLS handshakeRaise timeout, retry

When Tool Calls Die at 60 Seconds

A different error shows up later, in the middle of a session: MCP error -32001: Request timed out. That is the request timeout of the MCP client library, and many clients built on the TypeScript SDK default to 60 seconds. Raising the startup timeout does not change it for a running tool call.

Look in your build's schema for a per-request setting first. If there is none, change the tool instead of the client. Make the first tool return a job id immediately, and add a second tool that reports the job status. The model submits, gets an id, and checks back, which is the same submit-then-poll pattern that image and video generation APIs use for the same reason.

💡 Tip: Send server logs to stderr, never stdout. A local server shares stdout with the protocol, so a single stray console.log can corrupt the handshake and look like a timeout.

Brass stopwatch resting on a dark walnut desk beside a laptop

Debug a Server That Won't Connect

When the fix is not obvious, stop editing the config and test each layer separately.

Run the Server by Hand

Copy the command array into a terminal and run it as one line. A healthy stdio server starts and waits silently for input. If it prints an error, a missing module or a bad path, you found the problem without OpenCode in the loop. Then run opencode mcp debug <name>, which diagnoses connection and OAuth problems for that one server.

For a remote server, curl -i the URL with the same headers. A 401 means credentials, a 404 means the path, and a hang means the network.

Common Errors and Fixes

SymptomLikely causeFix
Server missing from the listTypo, or the wrong file editedAdd $schema, check the global file
Fails immediatelycommand is a string, or the binary is not on PATHUse an array and an absolute path
Constant 401Missing token variable, or OAuth expectedExport the variable, or run mcp auth
Login page never finishesCallback cannot reach OpenCodeForward the callback port
Tool call ends with -3200160 second request limitUse the job id pattern
Works in your shell, fails in OpenCodeDifferent PATH or environmentSet environment, use full paths

Two colleagues around a wooden table pointing at a laptop screen

Keep Context Small Per Agent

Every connected server adds its tool descriptions to the context sent with each request. A handful of servers can eat a large share of the window before you type a word, and the model gets worse at picking the right tool when it has fifty to choose from. The OpenCode docs say as much: use MCP servers sparingly.

Disable Globally, Enable Per Agent

Tool names carry the server name as a prefix, so a glob pattern switches a whole server at once. * matches zero or more characters and ? matches exactly one.

{
  "tools": {
    "files_*": false,
    "tracker_*": false
  },
  "agent": {
    "builder": {
      "tools": { "files_*": true }
    },
    "planner": {
      "tools": { "tracker_*": true }
    }
  }
}

With this setup, the builder agent sees only the filesystem tools and the planner agent sees only the tracker. Neither pays the token cost of the other server's tool list.

Wooden pegboard with hand tools arranged neatly on hooks

How to Use Claude Sonnet 5

Config bugs are tedious pattern matching: a string where an array belongs, a variable that is not exported, a port nobody forwarded. That is where a coding model earns its time. Claude Sonnet 5 on PicassoIA reads config text, error output and even screenshots, so you can paste what you see and ask what is wrong.

  1. Open the model page. Go to Claude Sonnet 5 on PicassoIA and find the prompt box.
  2. Paste your mcp block. Replace every token and secret with a placeholder first, then add the output of opencode mcp debug <name>.
  3. Set the effort. The default low skips thinking and answers fastest. Use medium or high when several files interact, for example a global config overridden by a project config.
  4. Add a system prompt once. Something like: "You review opencode.json MCP configs. Check type, command, timeout and oauth. Reply with corrected JSON only."
  5. Attach a screenshot if you have one. The image input reads terminal errors. Raise the max image resolution when the text in the screenshot is small.
  6. Keep max_tokens at 8192. That is the default and plenty for a few config blocks.
  7. Verify before you paste. Check every field it suggests against the table above and the official docs.

💡 Tip: Never paste a real token into any chat box. Placeholders like TRACKER_TOKEN give the model everything it needs.

Make Your Own Images Today

Config is only half of what MCP can do. Once a coding agent can call tools, it can call image generators too. PicassoIA exposes its generators over MCP, and the server address lives on your account's MCP connections page. Any server that speaks HTTP follows the same remote pattern from this article: type, url, a header or OAuth, and a sensible timeout.

If you would rather skip the setup and just make pictures, open Picasso IA and try these text-to-image models:

Pick one, write a prompt about the thing you just configured, and compare how each model reads it. Ten minutes of experimenting teaches you more about prompt wording than an hour of reading, and every image you generate is a free rehearsal for the next one.

Share this article