OpenCode MCP Setup: opencode.json for v1 and v2
Ask OpenCode for a confirm dialog and it writes one from memory: a fixed overlay, a hand-rolled focus trap if you are lucky, no layout for a phone. An OpenCode MCP entry fixes that by giving the agent a component registry it can search, read props from and install from. The catch is the config. The OpenCode 1 docs show mcp.<name> with enabled, the OpenCode 2 docs show mcp.servers.<name> with disabled, and each is right for its own major version. This tutorial walks through OpenCode MCP server setup for both: the config shape, local versus remote entries, API key headers versus OAuth, and the prompts that turn into real components.
What we checked, on November 6, 2026: the OpenCode 1 MCP docs (opens in a new tab), the OpenCode 2 MCP docs (opens in a new tab) and its migration guide (opens in a new tab), the JSON schema at https://opencode.ai/config.json, the npm registry entries of both versions, shadcn's source and our own server through curl. We did not install OpenCode for this post, so where the docs and the schema disagree we say so instead of guessing. Why an agent needs a component server at all is in our component library MCP guide.
Which OpenCode version do I have, and why does it matter?
Run opencode --version. The two majors ship as different npm packages:
The v1 CLI docs (opens in a new tab) say opencode upgrade --major moves you to OpenCode 2 and removes OpenCode 1. The migration guide is the part that saves you work: OpenCode 2 reads the same system, global and project files as OpenCode 1 and normalizes the v1 fields in memory, without rewriting them. So a v1 file keeps working after the upgrade. The rule it adds is to keep each server entry entirely in one shape. A v1 entry that has enabled but no type is ignored with a warning.
The schema at opencode.ai/config.json reflects both: mcp.servers is the current shape, and server names directly under mcp are still accepted and marked deprecated.
Where does the OpenCode MCP config live?
In opencode.json or opencode.jsonc, in any of these places:
- Global:
~/.config/opencode/opencode.json, for servers you want in every project. - Project:
opencode.jsonin the project root, or.opencode/opencode.json. This wins over the global file, and it is where you put a server the whole team should get. - Custom: a path in the
OPENCODE_CONFIGenvironment variable.
A component registry belongs in the project file: everyone who clones the repository gets the same server, and the agent only sees it where it is useful. Add "$schema": "https://opencode.ai/config.json" at the top and your editor will flag a wrong key before OpenCode does.
How do I add a local OpenCode MCP server?
A local entry has "type": "local" and a command array: the program and its arguments as separate strings. Here is the opencode.json MCP block for our server in the OpenCode 2 shape:
The same server for OpenCode 1:
The -y matters. Without it npx can stop to ask before it downloads a package, and nobody answers a server that OpenCode started in the background. We hit exactly that in our Gemini CLI shadcn setup.
npx wingo-ui@latest mcp is a small stdio proxy. It forwards to https://wingo-ui.com/mcp and sends WINGO_UI_API_KEY when that is set, otherwise the key that npx wingo-ui@latest login saved on your machine. With neither, it connects as an anonymous client. Local entries also accept environment (an object of variables), cwd and timeout.
Can the CLI write the entry for me?
Yes. In OpenCode 1, opencode mcp add walks you through a local or remote server. The OpenCode 2 docs show flags, with everything after -- as the command:
The second line writes the global file instead of the project one.
How do I add a remote server with an API key?
A remote entry has "type": "remote" and a url. Our server speaks Streamable HTTP at https://wingo-ui.com/mcp and takes the key as a bearer header:
For OpenCode 1, drop the servers level and add "enabled": true; the other keys are the same.
{env:WINGO_UI_API_KEY} is OpenCode's variable substitution, documented in both versions. It keeps the key out of the file, so the file can be committed. One trap: an unset variable becomes an empty string. We sent Authorization: Bearer (empty) to our server with curl and it answered 200 as an anonymous client, so a missing variable shows up as "connected, but no source" instead of an error. Ask the agent to call get_account_status once after setup to see which account it is using.
Why set oauth to false?
Because OpenCode tries OAuth on its own. Per the OpenCode 1 docs, a remote server that answers 401 starts an OAuth flow, with dynamic client registration when the server supports it, and the OpenCode 2 docs reserve oauth: false for servers that only take an API key or another header credential. Our server has no OAuth: /.well-known/oauth-protected-resource returns 404, and a wrong key gets a 401 with invalid_token. With "oauth": false, a bad key fails as a bad key instead of opening a browser flow that cannot finish. That is the general rule: API key servers get oauth: false and a header, OAuth servers get neither.
When should I use OAuth instead?
When the server supports it, OAuth is the nicer option, since no secret sits in your shell profile. Configure only type and url (for OpenCode 1, "oauth": {} is the docs' explicit form), then authenticate:
The OpenCode 2 docs list auth and logout but not auth list or debug; there, opencode mcp auth without a name lets you pick an OAuth-capable server. OpenCode 1 keeps the tokens in ~/.local/share/opencode/mcp-auth.json. Servers that need a pre-registered client take client_id, client_secret and scope under oauth (camelCase in OpenCode 1).
How do I check that the server is connected?
It lists each configured server with its connection status (and auth status for OAuth servers). Then start a session and ask: "Call get_account_status on the wingo-ui server." Without a key it reports an anonymous client. Anonymous clients still get 10 read-only tools: search_components, list_categories, get_component, get_block, get_design_rules, get_theme_tokens, get_install_instructions, check_updates, get_changelog and get_account_status. A free account adds the source of every free item. All tools and options are on the MCP server docs.
Three settings are worth knowing once it works:
- Timeouts. The OpenCode 1 docs give 30 seconds as the default, while the v1 field description in the JSON schema says 5 seconds. The OpenCode 2 docs give 30 seconds for
startupandcatalogand 12 hours forexecution. If a slow firstnpxdownload times out, settimeoutexplicitly (in OpenCode 2,timeout.startup). - Tool names. Tools are prefixed with the server name, so
wingo-ui_*matches all of ours. OpenCode 1 can switch them off with"tools": { "wingo-ui_*": false }and back on per agent underagent.<name>.tools. OpenCode 2 uses permission rules on the same<server>_<tool>names. - Code Mode. OpenCode 2 runs MCP servers in Code Mode by default;
"codemode": falseon a server exposes its tools directly.
How do I add an OpenCode shadcn MCP entry?
shadcn writes it for you:
In shadcn's source (4.23.0, November 6, 2026) that writes a project opencode.json with this entry:
The same command installs shadcn as a dev dependency, so npx finds it locally and the missing -y does no harm in that project. Add -y when you copy the entry into a project without that dependency. It is the OpenCode 1 shape, which OpenCode 2 reads as well. If you already moved your other servers under mcp.servers, rewrite this one to match, with "disabled": false instead of enabled. The shadcn server searches the registries in your components.json and returns npx shadcn@latest add commands. With both servers in one project, both libraries install a button.tsx with different props, so say in your rules which registry owns which folder.
What should AGENTS.md say?
It should tell the agent when to search, how to install and which tokens to use. Per the OpenCode rules docs (opens in a new tab), OpenCode reads AGENTS.md from the current directory upward and falls back to CLAUDE.md, plus a global ~/.config/opencode/AGENTS.md. Our CLI's agents install has no OpenCode target, so fetch the block it writes for Codex:
The block sits between <!-- wingo-ui:start v=3 --> and <!-- wingo-ui:end --> markers. Its core line:
Our AGENTS.md vs CLAUDE.md comparison has a fuller template.
Which prompts get OpenCode to install real components?
Name the screen, the data and what you want reported back. The rules already send the agent to search_components, then get_component, then npx wingo-ui@latest add. The prompt supplies what a rules file cannot.
Prompt 1: a delete confirmation
The Dialog and the Button are both free, and the core of a good result is short:
Below 768px the same component opens as a bottom sheet with full-width 48px actions, which is the part a model writing from memory skips.
$ npx wingo-ui@latest add dialogPrompt 2: a sign-in screen
The Login Page ships sample handlers so its demo runs, and its default labels name Northwind CRM. That is why the prompt asks for a report: without onVerifyCode, the code view accepts 123456 for anyone. A good result passes layout="card", methods={["password", "otp"]}, showProviders={false}, labels with your app name, and real onSignIn, onSendCode, onVerifyCode and onSuccess handlers.
$ npx wingo-ui@latest add login-pagePrompt 3: replace hand-written spinners
The Button's loading and loadingText props replace the spinner markup and the disabled juggling, and onClick is not called while it is loading.
What goes wrong, and how do I fix it?
- The server never shows up. A v1 entry without
type, or an entry that mixesenabledanddisabled. Pick one shape per entry and runopencode mcp listagain. - A local server fails to start. Usually npx waiting for an install answer: add
-ytocommand. A slow first download can also hit the timeout. - A browser window opens for an API key server.
oauthis missing. Set"oauth": falseand send the key inheaders. - Connected, but no source. The key variable is unset, so the header went out empty. Export it in the shell that starts OpenCode, or use the local entry after
npx wingo-ui@latest login(a free account needs only an email). - Pro blocks with a free key.
get_blockstill returns props and usage forlogin-page, andnpx wingo-ui@latest add login-pagestops because the block is part of Pro. The Button and Dialog are free; the Login Page is Pro ($8 a month, $80 a year or $150 once, on the pricing page).
Where should I start?
Create a free account, run npx wingo-ui@latest login, add the local entry to your project's opencode.json, append the AGENTS.md block and try prompt 1 on the free Dialog and Button. The same setup for other agents is in our Antigravity MCP setup, Codex MCP setup and Claude Code shadcn MCP setup, and the rest of the series is under Building UI with AI agents.
FAQ
Where does OpenCode look for MCP servers?
In the mcp key of opencode.json or opencode.jsonc: globally in ~/.config/opencode/, per project in the project root or in .opencode/. Project config wins over the global file, and both OpenCode 1 and 2 read the same locations.
What changed in the OpenCode 2 MCP config?
Servers moved from mcp. to mcp.servers., enabled became the inverse disabled, timeout became an object with startup, catalog and execution, and OAuth fields became snake_case (client_id instead of clientId). OpenCode 2 normalizes the old shape in memory, so a v1 file keeps working.
Should a remote MCP server in OpenCode use OAuth or an API key header?
Use OAuth when the server supports it, because OpenCode stores the token for you after opencode mcp auth. When the server takes an API key, put it in headers with {env:VAR} and set oauth to false, so a rejected key does not start an OAuth flow the server cannot finish.
Does the shadcn MCP server work with OpenCode?
Yes. npx shadcn@latest mcp init --client opencode writes a project opencode.json with a local shadcn entry in the OpenCode 1 shape (enabled: true), which OpenCode 2 also reads. The command array in shadcn 4.23.0 has no -y, which is fine because mcp init also installs shadcn as a dev dependency; add -y if you copy the entry into another project.
How do I check that an OpenCode MCP server is connected?
Run opencode mcp list, which lists each configured server with its connection status; in OpenCode 1, opencode mcp debug also tests OAuth. Then ask a question only the server can answer, such as calling get_account_status on the wingo-ui server.
- OpenCode
- MCP
- shadcn/ui
- AGENTS.md
- AI agents
