Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
  1. Home
  2. Blog
  3. Building UI with AI agents
  4. OpenCode MCP Setup: opencode.json for v1 and v2
Auf Deutsch lesen
  1. Blog
  2. Building UI with AI agents
  3. OpenCode MCP Setup: opencode.json for v1 and v2
Building UI with AI agents
Building UI with AI agents

OpenCode MCP Setup: opencode.json for v1 and v2

SR
Serban Rusu · Founder of Wingo UI
Nov 6, 2026 · 11 min read

On this page

0%
  1. Which OpenCode version do I have, and why does it matter?
  2. Where does the OpenCode MCP config live?
  3. How do I add a local OpenCode MCP server?
    1. Can the CLI write the entry for me?
  4. How do I add a remote server with an API key?
    1. Why set oauth to false?
  5. When should I use OAuth instead?
  6. How do I check that the server is connected?
  7. How do I add an OpenCode shadcn MCP entry?
  8. What should AGENTS.md say?
  9. Which prompts get OpenCode to install real components?
    1. Prompt 1: a delete confirmation
    2. Prompt 2: a sign-in screen
    3. Prompt 3: replace hand-written spinners
  10. What goes wrong, and how do I fix it?
  11. Where should I start?
  12. FAQ
    1. Where does OpenCode look for MCP servers?
    2. What changed in the OpenCode 2 MCP config?
    3. Should a remote MCP server in OpenCode use OAuth or an API key header?
    4. Does the shadcn MCP server work with OpenCode?
    5. How do I check that an OpenCode MCP server is connected?

TL;DR

OpenCode reads MCP servers from the mcp key of opencode.json: in OpenCode 1 each server sits directly under mcp with enabled, in OpenCode 2 it sits under mcp.servers with disabled, and OpenCode 2 still reads the old shape. A local entry runs a command array such as npx -y wingo-ui@latest mcp; a remote entry takes a url plus headers, and with an API key you set oauth to false. Run opencode mcp list to check the connection, then ask for components by name.

Published Nov 6, 2026

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:

OpenCode 1OpenCode 2
npm packageopencode-ai@opencode/cli
Latest on npm (November 6, 2026)1.19.5, published October 28, 20262.2.5; 2.0.0 was published September 11, 2026
Commands it installsopencodeopencode and opencode2
Where servers gomcp.<name>mcp.servers.<name>
On or offenabled: truedisabled: false
Timeoutone number in ms{ startup, catalog, execution } in ms
OAuth fieldsclientId, clientSecret, callbackPort, redirectUriclient_id, client_secret, callback_port, redirect_uri, auth_server_metadata_url

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.json in 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_CONFIG environment 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:

jsonc
// opencode.json (OpenCode 2)
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"wingo-ui": {
"type": "local",
"command": ["npx", "-y", "wingo-ui@latest", "mcp"]
}
}
}
}

The same server for OpenCode 1:

jsonc
// opencode.json (OpenCode 1)
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"wingo-ui": {
"type": "local",
"command": ["npx", "-y", "wingo-ui@latest", "mcp"],
"enabled": true
}
}
}

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:

bash
opencode mcp add wingo-ui -- npx -y wingo-ui@latest mcp
opencode mcp add wingo-ui --global -- npx -y wingo-ui@latest mcp

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:

jsonc
// opencode.json (OpenCode 2)
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"servers": {
"wingo-ui": {
"type": "remote",
"url": "https://wingo-ui.com/mcp",
"oauth": false,
"headers": {
"Authorization": "Bearer {env:WINGO_UI_API_KEY}"
}
}
}
}
}

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:

bash
opencode mcp auth sentry # opens the browser, stores the token
opencode mcp auth list # OpenCode 1: auth status of every OAuth-capable server
opencode mcp debug sentry # OpenCode 1: connectivity and OAuth discovery
opencode mcp logout sentry # removes the stored token

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).

Local (npx -y wingo-ui@latest mcp)Remote with headerRemote with OAuth
Needs NodeYesNoNo
Where the secret livesThe CLI's login store or WINGO_UI_API_KEYAn environment variable via {env:...}OpenCode's token store
oauth keynot usedfalseomitted, or client settings
Good forMost setupsCI and machines without NodeServers that support OAuth

How do I check that the server is connected?

bash
opencode mcp list

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 startup and catalog and 12 hours for execution. If a slow first npx download times out, set timeout explicitly (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 under agent.<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": false on a server exposes its tools directly.

How do I add an OpenCode shadcn MCP entry?

shadcn writes it for you:

bash
npx shadcn@latest mcp init --client opencode

In shadcn's source (4.23.0, November 6, 2026) that writes a project opencode.json with this entry:

json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"shadcn": {
"type": "local",
"command": ["npx", "shadcn@latest", "mcp"],
"enabled": true
}
}
}

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:

bash
curl -s https://wingo-ui.com/agents/AGENTS.md >> AGENTS.md

The block sits between <!-- wingo-ui:start v=3 --> and <!-- wingo-ui:end --> markers. Its core line:

md
- Before writing UI, use the `wingo-ui` MCP tools: `search_components` to find what exists (blocks for whole screens), `get_component` / `get_block` for props and usage, `get_design_rules` for tokens, sizes, motion and mobile rules. Never recreate a component the library has.

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

text
Add a "Delete project" confirmation to app/settings with the Wingo UI dialog. Read the dialog and
button props with get_component first, install both with the CLI, use the danger tone, show a
spinner while deleting, and check the dialog at 390px.

The Dialog and the Button are both free, and the core of a good result is short:

tsx
// app/settings/delete-project-dialog.tsx
"use client";
import { Trash2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Dialog, DialogClose } from "@/components/ui/dialog";
type Props = { name: string; deleting: boolean; onDelete: () => void };
export function DeleteProjectDialog({ name, deleting, onDelete }: Props) {
return (
<Dialog
trigger={
<Button variant="soft" tone="danger" leftIcon={<Trash2 />}>
Delete project
</Button>
}
title={`Delete ${name}?`}
description="This removes the project and its deployments. You cannot undo it."
icon={<Trash2 />}
tone="danger"
footer={
<>
<DialogClose asChild>
<Button variant="soft">Cancel</Button>
</DialogClose>
<Button tone="danger" loading={deleting} loadingText="Deleting" onClick={onDelete}>
Delete
</Button>
</>
}
/>
);
}

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.

Open the dialog, then narrow the window below 768px: it reopens as a bottom sheet you can drag down
$ npx wingo-ui@latest add dialog
FreeDialog docs

Prompt 2: a sign-in screen

text
Build /login from the Wingo UI login-page block, card layout, password and 6 digit email code, no
social buttons. Post to our /api/auth routes and go to /dashboard on success. Replace every
Northwind string in the labels with Harbor and list each handler you left on its sample default.

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.

Type any email and a password shorter than 8 characters to see the error alert, then open Forgot password: the email carries over
$ npx wingo-ui@latest add login-page
ProLogin Page docs

Prompt 3: replace hand-written spinners

text
Find every button in app/account that renders its own spinner while saving and replace it with the
Wingo UI button. Read its props with get_component first and show me the diff.

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 mixes enabled and disabled. Pick one shape per entry and run opencode mcp list again.
  • A local server fails to start. Usually npx waiting for an install answer: add -y to command. A slow first download can also hit the timeout.
  • A browser window opens for an API key server. oauth is missing. Set "oauth": false and send the key in headers.
  • 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_block still returns props and usage for login-page, and npx wingo-ui@latest add login-page stops 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.

Components in this post

  • Button

    The React button component every screen starts with: five variants, six tones, all sizes, and a loading state that never jumps.

    Free
  • Dialog

    A React modal dialog for focused tasks: a spring-scaled card on desktop and a bottom sheet on phones, with nesting and scrolling.

    Free
  • Login Page

    A React login page with password, magic link or code sign-in, Google, Apple and Microsoft buttons, two-step codes and password recovery.

    Pro

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.

Try it in your project

Button

Free

The React button component every screen starts with: five variants, six tones, all sizes, and a loading state that never jumps.

75 components free, no account needed. Pro from $8 a month.

Open Button liveSee pricing
  • OpenCode
  • MCP
  • shadcn/ui
  • AGENTS.md
  • AI agents

Share

SR

About the author

Serban Rusu

Founder of Wingo UI

Serban Rusu founded Wingo UI. He builds its component library, CLI and MCP server, and writes about React interfaces for phones and AI coding agents.

More from Serban
Keep reading

Related posts

All posts
Building UI with AI agents

Windsurf MCP Setup in 2026: New Config Path, Rules, Fixes

Windsurf MCP setup after the Devin Desktop rename: the mcp_config.json paths Devin Local reads, a working JSON entry, rules files and common error fixes.

SRSerban Rusu·Nov 6, 2026·10 min read
Building UI with AI agents

Copilot shadcn Setup: MCP Server and Instructions in VS Code

Copilot shadcn setup in VS Code: the shadcn MCP server in .vscode/mcp.json, a second registry, copilot-instructions.md, and the Copilot harness .mcp.json.

SRSerban Rusu·Nov 5, 2026·10 min read
Building UI with AI agents

Claude Code shadcn Setup: MCP Server, Skill and CLAUDE.md

Claude Code shadcn setup, tested on shadcn 4.21.4: the MCP server, the skill, a second registry in its own folder, a CLAUDE.md block and prompts that work.

SRSerban Rusu·Oct 9, 2026·12 min read
Newsletter

Get new posts by email

New posts, product news and occasional offers from Wingo UI. We count clicks to keep it relevant.

No spam. Unsubscribe at any time.

WingoUI

Animated, configurable, mobile-first React components and blocks. Copy the source, make it yours, and let your coding agent build with it.

Product

  • Components
  • Templates
  • Pricing
  • AI agents
  • For teams
  • Theme generator

Resources

  • Docs
  • Contact
  • Programs
  • Blog
  • Changelog
  • Changelog RSS
  • llms.txt

Compare

  • Wingo UI vs paid kits
  • Wingo UI vs shadcn/ui
  • shadcn/ui alternatives
  • MUI alternatives
  • Tailwind Plus alternatives
  • Magic UI alternatives
  • Free components

Legal

  • Terms
  • Privacy
  • Refunds
  • License
  • Imprint

Component categories

  • Buttons & Actions
  • Inputs
  • Forms
  • Navigation
  • Overlays
  • Feedback
  • Data Display
  • Tables & Lists
  • Charts & Stats
  • Layout
  • Media
  • AI Kit
  • Text & Effects
  • Mobile
  • Commerce
  • Marketing Sections
  • Blocks
  • Hooks & Utilities

© 2026 Wingo UI

Wingo UI