Skip to main content
Glama

opencode-mcp

An MCP server that discovers, monitors, and drives multiple OpenCode instances running across personal machines. Uses SSH reverse tunnels through a central relay for discovery and transport. Pluggable transport layer supports future backends (Tailscale, Cloudflare Tunnels, mDNS).

Quick Start (local testing)

# 1. Install
npm install && npm run build

# 2. Start opencode with HTTP side-car (in a separate terminal)
#    opencode-connected picks a random port and writes a registration file
ln -s ~/prg/opencode-mcp/scripts/opencode-connected ~/bin/opencode-connected
opencode-connected

# 3. Run with MCP inspector (in another terminal)
npx @modelcontextprotocol/inspector tsx src/index.ts

# Or run directly (stdio MCP server)
node dist/index.js

Both opencode-connected and the MCP server default to /tmp/opencode-relay for the registry directory — no configuration needed for local testing.

Related MCP server: agent-bus-mcp

Architecture

┌──────────────────────────────────────────────┐
│        Relay machine (GCE / VPS / etc.)      │
│                                              │
│  mcp-gateway ──── opencode-mcp (stdio)       │
│       │              │                       │
│       │         reads /tmp/opencode-relay/    │
│       │         or RELAY_REGISTRY_DIR         │
│       │              │                       │
│    OAuth         localhost:10001 ──┐         │
│    front         localhost:10002 ──┤ opencode│
│                  localhost:10003 ──┘ APIs    │
│                                              │
│  sshd: accepts reverse tunnels               │
└──────▲──────────▲───────────▲────────────────┘
       │          │           │
    ssh -R     ssh -R      ssh -R
       │          │           │
    laptop     desktop     laptop
    (oc:4823)  (oc:4567)   (oc:4901)

The MCP server runs on the same machine that accepts SSH reverse tunnels. It reads registration JSON files from a directory, health-checks each registered port on localhost, and creates OpenCode SDK clients for healthy instances. All OpenCode API calls go through localhost:{tunnel_port}.

OpenCode binds to 127.0.0.1 (default) — the SSH tunnel is the auth boundary. No passwords needed.

MCP Tools

Tool

Input

Description

instances

—

List all discovered instances with status (idle/busy) and recent session

send

instance, message, abort?

Send a message to the most recent session; set abort=true to stop a running task

read

instance, message_limit?

Read the last N messages from the most recent session

Instance names support fuzzy substring matching (e.g. "laptop" matches "laptop-myproject").

Environment Variables

MCP server

Variable

Default

Description

RELAY_REGISTRY_DIR

/tmp/opencode-relay

Directory containing registration JSON files

DISCOVERY_INTERVAL_MS

30000

How often to refresh instance list (ms)

HEALTH_CHECK_TIMEOUT_MS

3000

Timeout for health-checking each instance (ms)

TRANSPORT

local-relay

Transport backend (local-relay, future: tailscale)

SEND_TIMEOUT_MS

300000

Timeout for streaming send responses (ms)

opencode-connected script

Variable

Default

Description

RELAY_SSH_CMD

—

SSH command to reach relay. If unset, local only.

RELAY_REGISTRY_DIR

/tmp/opencode-relay

Registry directory (local or on relay)

INSTANCE_NAME

$(hostname)-$(basename $PWD)

Instance name for registration

Registration File Format

Each file in RELAY_REGISTRY_DIR is a JSON file named {instance-name}.json:

{
  "name": "laptop-myproject",
  "hostname": "laptop",
  "port": 10042,
  "localPort": 4823,
  "cwd": "/home/user/projects/myproject",
  "connectedAt": "2026-03-14T10:30:00Z"
}

Files are written by opencode-connected (locally or on the relay via SSH). The MCP server prunes files whose ports fail health checks.

Connecting an OpenCode Instance

Use opencode-connected instead of bare opencode to start the TUI with an HTTP side-car:

# Install (symlink)
ln -s ~/prg/opencode-mcp/scripts/opencode-connected ~/bin/opencode-connected

# Local only (no tunnel, writes registration to /tmp/opencode-relay/)
opencode-connected

# With relay (set RELAY_SSH_CMD in your shell profile)
export RELAY_SSH_CMD="gcloud compute ssh mcp-gateway --zone=us-central1-a --project=my-project --"
opencode-connected

# Or with direct SSH
export RELAY_SSH_CMD="ssh user@relay.example.com"
opencode-connected

# Pass extra args to opencode (after --)
opencode-connected -- -d

The script:

  1. Picks a random available local port (4096-5095)

  2. Starts opencode TUI with --port (enables HTTP side-car on 127.0.0.1)

  3. If RELAY_SSH_CMD is set: establishes SSH reverse tunnel with auto-retry

  4. Registers the instance (lazily creates the registry directory)

  5. Cleans up the registration file on exit

Note: opencode without --port does not start an HTTP server. The --port flag is what enables the HTTP side-car alongside the TUI.

Multiple instances in the same directory: The instance name defaults to $(hostname)-$(basename $PWD). If you run multiple opencode instances in the same directory, they'll compete for the same registration file — the last one wins and the others become invisible to the MCP server. To avoid this, set INSTANCE_NAME explicitly:

INSTANCE_NAME=my-tests opencode-connected
INSTANCE_NAME=my-refactor opencode-connected

For work machines with different MCP configs, set OPENCODE_CONFIG in your shell profile — the script does not handle config selection.

Integration with mcp-gateway (Docker)

To add opencode-mcp to an existing mcp-gateway Docker deployment:

1. Install from npm

npx -y opencode-mcp  # or add to gateway's SERVERS dict

2. Docker configuration

# docker run additions:
--network=host                    # reach SSH tunnel ports on host's localhost
-v /tmp/opencode-relay:/tmp/opencode-relay:ro  # read registration files
-e RELAY_REGISTRY_DIR=/tmp/opencode-relay

--network=host is required because SSH reverse tunnels bind on the host's localhost. The container needs to reach those ports directly.

3. MCP server config in mcp-gateway

Add to the gateway's server configuration:

{
  "mcpServers": {
    "opencode": {
      "command": "npx",
      "args": ["-y", "opencode-mcp"],
      "transport": "stdio",
      "env": {
        "RELAY_REGISTRY_DIR": "/tmp/opencode-relay"
      }
    }
  }
}

4. Verify

# On a client machine:
export RELAY_SSH_CMD="gcloud compute ssh mcp-gateway --zone=us-central1-a --project=my-project --"
opencode-connected

# From the chat interface, the LLM can now call:
# instances → sees the connected instance
# send → interacts with it
# read → sees what's been happening

Project Structure

opencode-mcp/
├── src/
│   ├── index.ts                 # MCP server entry + transport factory
│   ├── types.ts                 # RegistrationFile, OpenCodeInstance
│   ├── registry.ts              # Instance cache + OpenCode SDK client mgmt
│   ├── transport/
│   │   ├── interface.ts         # Abstract Transport interface
│   │   └── local-relay.ts       # File-based registry + localhost health checks
│   └── tools/
│       └── simplified.ts       # instances, send, read
├── scripts/
│   └── opencode-connected       # Client: random port + tunnel + exec opencode TUI
├── plans/
│   └── architecture.md          # Design doc + future work
├── package.json
├── tsconfig.json
└── .env.example

Development

npm install
npm run dev          # run with tsx (no build step)
npm run build        # compile TypeScript
npm start            # run compiled output

Security

  • SSH tunnels: the auth boundary — standard SSH key or gcloud auth

  • Tunnel ports: bound to host's localhost only, not externally accessible

  • OpenCode binding: 127.0.0.1 by default — not network-accessible

  • MCP transport: stdio (no network exposure); OAuth via mcp-gateway

  • Registration files: contain only name, hostname, port, cwd — no credentials

Available Tools

3 tools
instancesA

List all connected opencode instances with their current status (busy/idle). Call this to see what machines are available.

Inputs: no named parameters.

readA

Read the last few messages from the most recent opencode session on an instance. Use this to see what has been happening without sending a new message.

Inputs: instance (required), message_limit.

sendB

Send a message to the most recent opencode session on an instance. Streams the response back in real-time. Set abort=true to stop a running task instead of sending a message.

Inputs: instance (required), message, abort.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 3 tools

    Disambiguation5/5

    Each tool has a clearly distinct purpose: listing instances, reading session history, and sending messages. No overlap or ambiguity.

    Naming Consistency4/5

    All tool names are lowercase and concise, but 'instances' is a noun while 'read' and 'send' are verbs, creating a slight pattern inconsistency.

    Tool Count5/5

    Three tools cover the essential operations for interacting with opencode instances (list, read, send) without unnecessary bloat.

    Completeness4/5

    Core workflows are covered, but there is no tool to select a specific instance or start a new session, which are minor gaps.

    Maintenance

    ActivityInactive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers