Generalizes the v1.14.1 single-server Context7 PoC into a multi-server MCP client registry with per-server graceful degradation. JSON config at /data/mcp.json (bind-mounted alongside AGENTS.md) matches opencode's mcpServers schema shape. Config file missing = no MCP (opt-in by presence). Two transports: Streamable HTTP (remote servers like Context7) and stdio (local subprocess servers like codecontext). Stdio spawns a persistent child via the SDK's StdioClientTransport; shutdown hook closes all transports. Tool prefix generalized from context7_<name> to <serverName>_<toolName> with a toolToServer reverse map for dispatch routing. AGENTS.md tools: field now supports glob patterns (context7_*, !web_*) via matchToolGlob — last-match- wins with ! deny prefix. Replaces exact-match .includes() in stream-phase.ts. refreshToolNames() in agents.ts rebuilds the DEFAULT_TOOLS snapshot after appendMcpTools so agents without explicit tools: lists see MCP tools — reviewer caught that the module-load-time snapshot would permanently exclude late-registered tools. Read-only invariant: readOnlyHint === false rejected at discovery. Result size capped at 5MB. v1.14.1 env vars removed — superseded by config file. Default data/mcp.json ships with Context7 disabled. 363/363 server tests passing. No schema changes, no frontend changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.9 KiB
v1.15.0-mcp-multi — design decisions
D1. Config file path
/data/mcp.json (alongside AGENTS.md at /data/AGENTS.md). Both are bind-mounted from the host's data/ directory. Override via MCP_CONFIG_PATH env var.
File missing = no MCP (opt-in by file presence, not by env var). Simpler than the v1.14.1 approach of always-defaulting a URL.
D2. Config schema matches opencode's mcpServers shape
opencode uses ~/.opencode/config.json with a mcpServers key. BooCode uses mcp.json with the same mcpServers key so server entries are copy-pasteable. Property names match: type, url, command, args, env, headers. BooCode adds enabled (boolean toggle per server, default true) which opencode doesn't have — harmless extra key.
D3. Transport types: streamableHttp + stdio only
- streamableHttp: For remote servers (Context7, future cloud MCP services). Uses
@modelcontextprotocol/sdk'sStreamableHTTPClientTransport. - stdio: For local subprocess servers (codecontext, future local tools). Uses
@modelcontextprotocol/sdk'sStdioClientTransport(spawns child process, NDJSON framing over stdin/stdout). - SSE: Skipped. Streamable HTTP supersedes SSE per the MCP spec (May 2025 protocol update). If a legacy server requires SSE, it can be added later.
D4. Tool name prefixing: <serverName>_<toolName>
Generalizes v1.14.1's context7_<name> pattern. Server name comes from the config key (e.g. "context7", "codecontext"). Collisions between servers with the same name are impossible (config keys are unique). Collisions between an MCP tool and a native tool are possible if someone names a server entry the same as a native tool prefix — but that's a user-configuration error, not a code bug.
D5. Per-agent glob patterns: last-match-wins
AGENTS.md tools: field already supports exact-match arrays. Globs extend the same field:
tools: [view_file, grep, context7_*]
Evaluation: for each tool in ALL_TOOLS, scan the pattern list left-to-right. A ! prefix denies. Last matching pattern wins. This matches the roadmap's "wildcard rule matcher" language.
Examples:
[*]— all tools (same as omittingtools:entirely)[*, !web_*]— all tools except web[view_file, grep, context7_*]— only view_file, grep, and all Context7 tools[*]on Architect +[view_file]on Prompt Builder — each agent gets its intended scope
Globs use a simple minimatch-style check: * matches any characters. No ? or ** — tool names are flat (no path separators).
D6. No DB tables in v1.15
The roadmap listed permissions, agent_permissions, session_permissions, mcp_servers tables. All deferred to v2.0:
- Permission tables: Enterprise multi-user pattern. BooChat is single-user behind Authelia. The read-only invariant guard is the BooChat-era defense. Formal permission rulesets land when BooCoder adds write tools.
mcp_serverstable: In-memory registry is sufficient. No need to persist server state to DB when the config file is the source of truth and tools are re-discovered on every boot.
D7. Stdio child lifecycle
- Spawn on
initialize(). Persistent connection for server lifetime (not per-call). - On child exit (unexpected): mark server unavailable, log error. Do NOT auto-restart. BooCode continues with remaining servers.
- On BooCode shutdown (
app.addHook('onClose')): send SIGTERM to all stdio children. Wait up to 5s, then SIGKILL. - On ENOENT (command not found): skip server with a warning. Matches the graceful-degradation pattern from v1.14.1.
D8. v1.14.1 env vars removed
MCP_CONTEXT7_URL and MCP_CONTEXT7_API_KEY are deleted from config.ts. They're superseded by the JSON config file's context7 entry. The PoC was explicitly designed as throwaway.
Migration path for anyone who had the env vars set: add a data/mcp.json with the Context7 entry. The CHANGELOG entry will note this.