Skip to content

feat(client): add connectClientMcp helper for client-hosted MCP tools - #57504

Merged
jlongster merged 4 commits into
devfrom
client-mcp-helper
Oct 22, 2026
Merged

jlongster merged 4 commits into
devfrom
client-mcp-helper

Conversation

@jlongster

@jlongster jlongster commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Stack 3/5, based on client-mcp-attach. Merge in order.

Part of the client MCP stack (experimental): a client (TUI/desktop/web) opens a WebSocket to the server, and the server treats that incoming socket as an internal MCP server scoped to a location. Tool calls carry _meta["ai.opencode/sessionID"] so the client acts on the calling session. MCP has no official WebSocket transport, so this is a custom transport, which the spec allows.

This PR

Adds @opencode/client/mcp, a separate subpath that doesn't import core or server and has no runtime dependencies:

  • connectClientMcp({ url, name, directory?, headers?, tools }) opens the WebSocket, serves the tools, and reconnects with backoff (0.5s → 10s, reset once the server sends notifications/initialized)
  • Tools are a plain array: { name, description?, inputSchema? (JSON Schema), handler(args, ctx) }. ctx.sessionID holds the calling session
  • The MCP server is a small hand-written JSON-RPC handler, not @modelcontextprotocol/server. It only answers what core's attached MCP client uses: initialize (echoes the requested legacy protocol version), ping, tools/list and tools/call. Unknown tools get -32602, unknown methods get -32601, a handler that throws returns an isError: true result, and malformed frames are ignored
  • Arguments aren't validated here because core already validates tool input against inputSchema before calling

Using a hand-written server instead of the SDK and zod shrinks the minified browser bundle of @opencode/client/mcp from 251 KB (64 KB gzip) to 2.1 KB (1.1 KB gzip). It also keeps them out of every @opencode/client install. @modelcontextprotocol/client is only a dev dependency.

Tests run a Bun WebSocket server acting as the opencode server, and the real MCP SDK client talks to the hand-written server. They cover the handshake, listing, calls with and without a session ID, errors, reconnects and backoff.

Stack

  1. feat(core): add MCP transport over incoming client sockets #57502 feat(core): add MCP transport over incoming client sockets
  2. feat(core): attach client-hosted MCP servers #57503 feat(core): attach client-hosted MCP servers
    → 3. feat(client): add connectClientMcp helper for client-hosted MCP tools #57504 feat(client): add connectClientMcp helper for client-hosted MCP tools
  3. feat(server): add client MCP websocket endpoint (experimental) #57505 feat(server): add client MCP websocket endpoint (experimental)
  4. feat(tui): expose open_terminal over client MCP (experimental) #57506 feat(tui): expose open_terminal over client MCP (experimental)

@jlongster
jlongster merged commit 4535146 into dev Oct 22, 2026
10 of 12 checks passed
@jlongster
jlongster deleted the client-mcp-helper branch October 22, 2026 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant