GitHub Docs

VibeCody is a fully-featured MCP host. The MCP panel inside VibeCoder/VibeAIChat is the unified surface for managing servers, browsing the plugin directory, viewing the lazy-loaded tool registry, and inspecting cache metrics. This page documents the user-facing surface; the underlying client lives in vibecoder/crates/vibe-ai/src/mcp.rs.


What’s in the panel

The panel has five tabs:

Tab Purpose
Servers Configure MCP servers — name, command, args, env, OAuth tokens. Test connectivity and see live tool lists.
Tools The lazy-loaded tool registry. Search across servers, load / unload individual tools to manage agent-context budget.
Directory Browse the curated plugin catalog by category, install or uninstall with one click.
Installed Just the installed plugins, with View Tools navigation back to the Tools tab.
Metrics Cache hit rate, average load time, context-savings percentage, and a per-tool load-time chart.

Server configuration

Server records live in ~/.vibecoder/mcp.json as a plain JSON array. Each entry:

{
  "name": "filesystem",
  "command": "mcp-fs",
  "args": ["--root", "/workspace"],
  "env": { "MCP_LOG_LEVEL": "info" }
}

The panel writes this file via the save_mcp_servers Tauri command. OAuth tokens for servers that require auth are not stored alongside the config: they are secrets, so each one is an encrypted row in the ProfileStore (~/.vibecli/profile_settings.db) under mcp.oauth.<server>. Before 0.5.9 they lived in a plaintext ~/.vibecoder/mcp-tokens.json; that file is migrated into the store and deleted the first time the panel reads a token.

Test before save

The Servers tab has a Test button that spins up a short-lived MCP client connection, calls list_tools, and shows what the server would expose. Run this before saving — a server that can’t be reached or doesn’t speak MCP will surface the failure inline (with aria-live="assertive") rather than silently land in the config.

OAuth flow

For servers that need OAuth (e.g. cloud APIs):

  1. Click Connect via OAuth on the server row.
  2. Fill in client_id, auth_url, token_url, scopes, and the redirect URI (which the daemon listens on).
  3. Click Initiate — the system browser opens to the authorization page.
  4. After authorizing, the IDP redirects to the daemon’s callback. Paste the resulting code back into the panel.
  5. The panel exchanges the code for a token via complete_mcp_oauth and stores it under the server’s name in the encrypted ProfileStore.

The token-status badge on each server row reflects connectivity and expiration — a stale token shows as disconnected even if the row’s config looks fine.

Delete confirmation

Like the Sessions panel, deleting a server is a two-click confirmation: first click arms, second click commits. There’s no recycle bin — the row is removed from mcp.json immediately on commit.


Protocol versions

VibeCody’s MCP client speaks two eras and picks one per server when it connects.

Era Revisions How it opens What the client does
Modern 2026-07-28 No handshake. Every request carries its protocol version, client identity and capabilities in _meta. Uses it whenever the server answers server/discover with a result that names 2026-07-28.
Legacy 2025-11-25 and earlier An initialize handshake. Used when the probe is answered with any other error, with garbage, or not at all.
  • Probe, not a guess. On connect the client sends server/discover. A modern answer selects modern. Any other outcome selects legacy and the client sends initialize as before, because legacy servers answer an unknown method however they like (-32601, -32602, silence). The probe waits up to 20 s; a legacy server that ignores unknown methods makes the connect wait that long, and a modern server that takes longer than that to start is mistaken for a legacy one.
  • A modern server we cannot talk to is not a legacy server. If it answers with a version list that does not include 2026-07-28 (or the UnsupportedProtocolVersionError), the connection fails and the error names the versions it supports. The client does not fall back to initialize.
  • Input requests. A modern server may answer a tool call with input_required. With no user to ask, the client declines elicitation and reports no roots, then retries with the server’s requestState echoed exactly. A server that asks the client to run a model (sampling), which this client does not declare, makes the call fail with that reason. A server that keeps asking is abandoned after 8 rounds; the spec sets no limit, so the bound is the client’s own.
  • List caching. A modern tools/list carries ttlMs; while it is fresh the list is reused. Absent, negative or zero means ask every time, which is also how legacy servers behave. Change notifications are not subscribed to, so a list can be up to ttlMs out of date.

What has been verified: the rules are unit-tested, and the whole flow is tested over a real stdio pipe against a scripted modern server and scripted legacy servers. The legacy path was also run against the real @modelcontextprotocol/server-everything. No real modern server was available, so interoperability with one is untested. Not implemented: the Tasks extension, subscriptions/listen, pagination cursors, and the HTTP transport.

Tool registry (lazy loading)

The Tools tab reflects the lazy-loading runtime — tools aren’t held in agent context until you load them. The columns:

  • Status — loaded, loading, or unloaded. Loading is animated; the actual load happens in the background.
  • Size — KB cost when loaded.
  • Last used — timestamp from the daemon’s tool-call history.
  • Load time — first-load wall-clock from when load was issued.

Click the row toggle to load or unload. The Tools tab shows live tools per configured server when reachable; servers that don’t respond to test_mcp_server within the timeout simply collapse to “no tools listed” rather than blocking the whole tab.

The search box filters the registry across all servers in real time (200ms debounce). Matches against tool name and description. Use this when you can’t remember which server a tool came from.


Plugin directory

The Directory tab is a curated catalog of MCP plugins. Each entry has:

  • Author / Author rating — surfaced for user trust signals.
  • Category — filterable from the sidebar.
  • Install / Uninstall — two-click confirmation. Install adds the plugin id to ~/.vibecoder/mcp-installed.json; uninstall removes it.

Install does not auto-add a server entry to mcp.json. The directory is metadata-only — actually wiring a plugin into your active MCP host happens in the Servers tab, with the plugin’s documented command / args. This separation is deliberate so that “installed” tracks intent (the user wants this plugin available) without conflating it with active runtime state.


/health declaration

features.mcp is a runtime probe of ~/.vibecoder/mcp.json:

{
  "available": true,
  "transport": "tauri-desktop",
  "config_path": "~/.vibecoder/mcp.json",
  "server_count": 3,
  "configured": true
}

Operators can hit /health.features.mcp.server_count to confirm an environment has MCP servers wired up. The boolean configured is server_count > 0 — clients gating MCP-dependent features should read this single field.


Observability

Backend operations emit structured tracing events under vibecody::mcp:

RUST_LOG=vibecody::mcp=info vibecli serve

Events:

INFO vibecody::mcp: mcp.servers.save server_count=3
INFO vibecody::mcp: mcp.server.test.start server_name=filesystem
INFO vibecody::mcp: mcp.server.test.ok   server_name=filesystem tool_count=12 elapsed_ms=420
WARN vibecody::mcp: mcp.server.test.failed server_name=git elapsed_ms=2080
INFO vibecody::mcp: mcp.plugin.install   plugin_id=terraform installed_total=4
INFO vibecody::mcp: mcp.plugin.uninstall plugin_id=terraform installed_total=3
WARN vibecody::mcp: mcp.plugin.install.not_found plugin_id=banana

Server commands and arguments are not logged — only the server name. Tool descriptions, OAuth tokens, and plugin metadata are never logged.


Accessibility

  • The panel’s tab bar uses role="tablist" (set by the existing panel-tab-bar class) — keyboard navigation between tabs is available.
  • The shared error banner is role="alert" with aria-live="assertive", so failures from any subsystem (servers, tools, directory) are announced immediately.
  • Two-click delete on servers and plugins carries the confirmation state in aria-label so AT users hear “second click commits” rather than just “Delete”.
  • The OAuth flow is a multi-step form with clear step labels; each input has an associated <label> and the busy state disables the submit button rather than just hiding it.

Cross-client behaviour

Client MCP UI
VibeCoder / VibeAIChat Full panel (servers, tools, directory, metrics)
VibeMobile None — MCP is a desktop-host concern
VibeWatch None
IDE plugins Read-only — surface configured server names for tool-completion contexts

The panel runs in the Tauri host. Servers are subprocess-spawned by the host — they inherit the host’s user but run in fresh process trees.

Sandboxing, as of 2026-09-27: MCP server subprocesses launch through the native Tier-0 sandbox (bwrap on Linux, sandbox-exec on macOS) by default. Set VIBECLI_MCP_SANDBOX=0 to opt back out — for example while diagnosing a connector that fails to read a file it needs because the sandbox’s toolchain allow-list guessed wrong for your machine’s node/npm layout.

Two things this does not cover, regardless of the flag:

  • The network is always open. The sandbox confines the filesystem (a scratch dir plus read-only binds for your detected toolchain), not the network — every connector can still reach the internet freely. It does not stop a compromised or malicious MCP server from exfiltrating data it can already read. Use the OAuth flow rather than long-lived environment-variable tokens where possible; a sandboxed connector is not a network-isolated one.
  • Most connectors still fall back to unconfined if the sandbox can’t start. If sandbox setup fails (a bad toolchain guess, a missing bind target), the connector still runs — just without OS confinement — and a warning is logged (RUST_LOG=vibecody::mcp=info). The one exception is a connector explicitly marked requires_sandbox: true in the catalog (today: connectors in the Payments category) — for those, a sandbox failure refuses the connection outright rather than running it unconfined.

The panel and the VibeDesk connector marketplace (a separate, workspace-scoped catalog — see docs/vibedesk.md) share the same underlying client, so this behavior applies to both.


Troubleshooting

“Connect failed: Connection refused” when testing a server

Either the binary isn’t on PATH or the server isn’t speaking MCP on stdio. Check the command field — it must resolve via which from the daemon’s environment, not just the user’s shell. Absolute paths are safer.

“list_tools failed: invalid handshake”

The server connected but isn’t returning a valid MCP tools/list response. Likely an old or non-MCP binary mistakenly named like an MCP server. Cross-check against the server’s documentation — newer servers should support the 2024-11-05 protocol revision.

“Plugin already installed” when nothing seems to be installed

Clear ~/.vibecoder/mcp-installed.json to reset the install ledger. The directory tab tracks intent only — it doesn’t probe the file system.

“Token exchange failed” during OAuth

Most often caused by a redirect URI mismatch between the panel and the IDP’s registered application. The panel defaults to http://localhost:7879/oauth/callback — register that exact URL with your IDP, including the path.

“Servers tab shows no live tools after Test”

The Tools tab probes all servers in parallel, but rendering is gated by serverToolsLoading. If a server hangs (no response, no error within the timeout), the panel resolves it to an empty list. Check RUST_LOG=vibecody::mcp=info for the corresponding mcp.server.test.failed event.


  • MCP client core: vibecoder/crates/vibe-ai/src/mcp.rs — connect, list_tools, call_tool
  • Source: vibecoder/src/components/McpPanel.tsx (902 LOC) · backend in vibecoder/src-tauri/src/commands.rs
  • Tests: vibecoder/src/components/__tests__/McpPanel.test.tsx
  • Sandbox: docs/sandbox — MCP server subprocesses are sandboxed by default as of 2026-09-27 (VIBECLI_MCP_SANDBOX, default on); see the caveats above before relying on it as a network boundary.