Files
mcpctl/docs/opencode-extension.md
Michal 0a02616c7f
Some checks failed
CI/CD / lint (pull_request) Successful in 1m7s
CI/CD / test (pull_request) Successful in 1m22s
CI/CD / typecheck (pull_request) Successful in 2m49s
CI/CD / smoke (pull_request) Failing after 1m54s
CI/CD / build (pull_request) Successful in 2m16s
CI/CD / publish (pull_request) Has been skipped
feat(opencode): add mcpctl config opencode + /mcpctl TUI switcher with bottom project indicator
2026-08-08 21:52:32 +01:00

4.7 KiB
Raw Permalink Blame History

mcpctl × opencode — native integration (TUI plugin + MCP bridge)

Motivation

With Claude Code, mcpctl config claude --project X wires a project's MCP servers into the agent two ways:

  1. a .mcp.json entry running mcpctl mcp -p X (an MCP stdio bridge), and
  2. a SessionStart hook + mcpctl skills sync that materialises server-side skills under ~/.claude/skills/.

opencode speaks MCP natively (like Claude), consumes Agent Skills SKILL.md trees (like pi), and lets plugins render into its TUI. This addon mirrors the prime-agent/pi integration so mcpctl "just works" from inside opencode:

  • mcpctl config opencode --project X registers the project's MCP stdio bridge (mcpctl mcp -p X) as an opencode local MCP server, installs a small TUI plugin, and syncs the project's skills.
  • The TUI plugin gives you a /mcpctl slash command to switch projects from the opencode prompt, and shows the current project in the bottom status bar — next to where opencode prints the model and the token counter — so you always know which project's MCP servers are live.

How it works

MCP over stdio

opencode has native MCP client support, so unlike pi there is no JSON-RPC client to vendor. mcpctl config opencode --project X writes into opencode's global config (~/.config/opencode/opencode.json):

{
  "mcp": {
    "servers": {
      "homeautomation": {
        "type": "local",
        "command": ["mcpctl", "mcp", "-p", "homeautomation"],
        "mcpctlManaged": true
      }
    }
  }
}

mcpctlManaged is a tag opencode ignores (its schema tolerates unknown keys); mcpctl uses it to recognise — and retire — the single active entry when you switch, so exactly one mcpctl project is mounted at a time and hand-configured servers are never touched.

opencode loads plugins from ~/.config/opencode/plugin/, but its auto-discovery glob is *.{ts,js} — it won't pick up a JSX .tsx file. So config opencode writes plugin/mcpctl-switch.tsx and references it from opencode.json's plugin array as a relative path:

{
  "plugin": ["./plugin/mcpctl-switch.tsx"]
}

The plugin is a TUI plugin (module default exports { id, tui }). It:

  • registers a /mcpctl slash command (surfaces in the prompt's / autocomplete) that opens a filterable project picker (opencode's DialogSelect filters as you type), and
  • renders the active project in the app_bottom slot — the bottom strip where opencode shows the model and token counter.

Switching runs the CLI:

mcpctl config opencode --project <name> --skip-plugin --skip-marker --config-dir <dir>

which rewrites opencode.json (section above). opencode's config watcher reloads MCP so the new project's tools come up — if a running session doesn't pick it up, a restart does.

Deliverables

Artifact Purpose
src/opencode-ext/mcpctl-opencode.tsx Real, reviewable TUI plugin source
scripts/generate-opencode-extension.ts Embeds that source into the CLI (config/opencode-extension.ts)
src/cli/src/config/opencode.ts opencode.json read/merge/write + state helpers
mcpctl config opencode CLI wiring: MCP + plugin + state + marker + skills
mcpctl skills sync --agent opencode Sync skills into ~/.config/opencode/skills/
docs/opencode-extension.md This document

Regenerating the embedded plugin

The plugin ships as source embedded into the CLI, so an installed binary can still provision a working addon. After editing src/opencode-ext/*.tsx, regenerate the embedded copy or the CLI will keep installing the old source:

npx tsx scripts/generate-opencode-extension.ts

A test (tests/config/opencode-embed.test.ts) fails if the embed drifts from the source.

Skills

mcpctl config opencode (and mcpctl skills sync --agent opencode) materialise the active project's skills under ~/.config/opencode/skills/<name>/, the tree opencode reads for Agent Skills. Sync state is tracked separately (~/.mcpctl/skills-state-opencode.json), so opencode, prime-agent and Claude never fight over bookkeeping.

Switching with --config-dir

The /mcpctl plugin shells out with --config-dir pointing at the config dir it was installed into, so a non-default opencode config dir keeps working too.

Project switching / auth

Like the Claude path, this uses the stdio bridge (mcpctl mcp -p X) and a locally-running mcplocal daemon, so there is no HTTP gateway / bearer-token provisioning dance (that's specific to prime-agent's remote gateway). Pointing it at an authenticated mcplocal serve needs a project mcptoken wired through mcpctl mcp — outside this addon's scope for now.