Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

OpenCode Server 接口参考

OpenCode 对外暴露三类接口:HTTP REST API(HTTP 表述性状态转移接口)LSP(Language Server Protocol,语言服务器协议) 集成、MCP(Model Context Protocol,模型上下文协议) 集成。这三类接口覆盖了“程序化调用 OpenCode“和“OpenCode 调用外部能力“两条完整链路,是 OpenCode 从 TUI 工具升级为可编排 Agent 平台的协议底座。

角色定位必须先讲清——这是最容易混淆的一点:

  • HTTP REST API 中 OpenCode 是 server(服务端):通过 opencode serve 启动独立 Server 进程,对外暴露 18 大类 REST 端点,任何能发 HTTP 请求的语言都能成为 OpenCode 的客户端。
  • LSP 中 OpenCode 是 client(客户端):OpenCode 启动并管理外部的语言服务器(typescript-language-server、pyright、gopls 等),消费其诊断结果,并把其中一部分能力重新打包成 Tool 暴露给 Agent。
  • MCP 中 OpenCode 是 client(客户端):通过 opencode.jsonmcp 字段把外部 MCP servers 注册进来,让 Agent 像调用内置工具一样调用它们。

第三方扩展能力不在本文主线内:opencode.nvim 把 OpenCode 反向包装成 LSP server 供 Neovim 调用、opencode-mcp 把 OpenCode 暴露为 MCP server 供 Claude Desktop/Cursor 调用——两者都不属于 OpenCode 核心仓库,本文在对应章节末尾的“生态扩展“小节单独标注。

本文与 OpenCode SDK:编程式 Agent 开发 的分工:那篇文章从 SDK(软件开发工具包) 客户端视角讲如何用 TypeScript 封装层调用接口(含重试、类型安全、E2B 沙箱、CI/CD 集成等深度客户端能力);本文从 Server 端和协议视角讲接口规范本身——前者是“怎么用最舒服“,后者是“协议长什么样“。

接口二象性对照

下表展示 HTTP API 与 LSP/MCP 的二象性关系:HTTP API 不仅自身提供 18 大类端点,还包含对 LSP/MCP 的控制端点。也就是说,LSP 和 MCP 既是独立的协议层,也可以通过 HTTP API 远程操控。

接口类型OpenCode 角色核心端点/方法HTTP API 中的控制端点
HTTP REST APIServer18 大类端点(/session、/tool、/agent 等)
LSPClient10 个暴露给 AI 的方法 + 9 种 Tool operationGET /lsp(查询状态)
MCPClientopencode.json mcp 字段 + CLI 命令GET|POST /mcpPOST /mcp/:name/connect未确认POST /mcp/:name/disconnect未确认

三类接口架构总览

graph TB
    subgraph Client["客户端"]
        SDK["@opencode-ai/sdk<br/>TypeScript SDK"]
        Curl["curl / HTTP 客户端"]
        GoSDK["opencode-sdk-go"]
        IDE["IDE 扩展<br/>(VSCode/Neovim)"]
        MCPClient["MCP 客户端<br/>(Claude Desktop等)"]
    end

    subgraph OC["OpenCode 进程"]
        Server["HTTP Server<br/>opencode serve :4096"]
        LSPClient["LSP Client<br/>vscode-jsonrpc"]
        MCPClientMgr["MCP Client Manager"]
        Agent["Agent Loop"]
    end

    subgraph External["外部服务"]
        LSPSrv["语言服务器<br/>(tsserver/pyright/gopls...)"]
        MCPSrv["MCP Servers<br/>(filesystem/context7...)"]
    end

    SDK -->|"HTTP REST"| Server
    Curl -->|"HTTP REST"| Server
    GoSDK -->|"HTTP REST"| Server
    IDE -->|"HTTP / TUI API"| Server
    MCPClient -.->|"通过 opencode-mcp<br/>第三方桥接"| Server

    Server --> Agent
    Agent --> LSPClient
    Agent --> MCPClientMgr
    LSPClient -.->|"stdio JSON-RPC"| LSPSrv
    MCPClientMgr -.->|"stdio / SSE / HTTP"| MCPSrv

    classDef opencode fill:#4A90D9,color:#fff,stroke:#2E5C8A
    classDef external fill:#A66CFF,color:#fff,stroke:#6B4BCC
    class Server,LSPClient,MCPClientMgr,Agent opencode
    class LSPSrv,MCPSrv external

HTTP REST API —— 作为 Server

通过 opencode serve 启动一个无头 HTTP Server(HTTP 服务器),用任何语言、任何客户端通过 REST 接口调用 OpenCode 的全部能力。

OpenCode 不仅是 TUI 工具,它本质上是 Server-Client(服务端-客户端)架构:你在终端看到的 TUI 只是众多客户端之一,背后运行的 Server 才是核心。Server 暴露 OpenAPI 3.1 规范 的 REST 接口,任何能发 HTTP 请求的语言都能成为 OpenCode 的客户端——curl、Python、Go、Rust、Shell 脚本,甚至是浏览器里的 fetch。

本文聚焦 Server 端:怎么启动、怎么认证、有哪些接口、用 curl/Python/Go 怎么调用。如果你要的是 TypeScript SDK 客户端的封装体验(重试、类型安全、E2B 沙箱集成),请直接跳到 → OpenCode SDK:编程式 Agent 开发


启动与配置

最简启动

opencode serve
# 默认监听 127.0.0.1:4096,仅本机可访问

启动后立即可以验证:

curl http://localhost:4096/global/health
# 期望返回:{"healthy":true,"version":"1.x.x"}

💡 设计决策:默认 127.0.0.1 而非 0.0.0.0,是 OpenCode 的安全默认——Server 不会自动暴露到局域网。需要从其他机器访问时(如 Docker 容器、CI Runner)才显式指定 --hostname 0.0.0.0,此时必须配合认证使用(见下文)。

CLI 参数详解

opencode serve [options]
参数类型默认值说明
--portnumber4096监听端口。若端口被占用,Server 启动失败而非自动换端口
--hostnamestring127.0.0.1监听地址。0.0.0.0 表示监听所有网卡(暴露到局域网,需配合认证)
--mdnsboolfalse启用 mDNS(多播 DNS) 服务发现,局域网内可零配置发现 Server
--mdns-domainstringopencode.localmDNS 服务名,配合 --mdns 使用
--corsstring[][]追加允许的浏览器 Origin,可多次传入

--cors 是追加而非覆盖,默认已允许的 Origin(见下文“认证与安全“)不受影响:

opencode serve \
  --hostname 0.0.0.0 \
  --port 8192 \
  --mdns \
  --cors https://app.example.com \
  --cors https://staging.example.com

TUI 与 Server 的关系

直接运行 opencode(不带 serve)会同时启动 TUI 和一个 Server,但 Server 监听随机端口和随机 hostname。这种方式不适合程序化集成。

💡 设计决策:程序化场景下永远用 opencode serve 启动独立 Server,不要依赖 TUI 内置的 Server。独立 Server 端口固定、可重启、可被多个客户端复用;TUI 关闭后内置 Server 也会退出,会导致客户端断连。

OpenAPI 规范入口

启动 Server 后,浏览器打开 http://localhost:4096/doc 即可查看交互式 OpenAPI 3.1 文档。这个端点也是 SDK 客户端代码生成的源:

# 拉取原始 OpenAPI JSON 用于代码生成
curl http://localhost:4096/doc -o openapi.json

# 用 openapi-generator 生成任意语言客户端
npx @openapitools/openapi-generator-cli generate \
  -i openapi.json \
  -g python \
  -o ./opencode-client-py

⚠️ 注意:本文所有接口签名以运行时 /doc 为准。OpenCode 处于快速迭代期,本文列出的是 v1.17.x 附近的接口集合,新版本可能增删端点。每次集成前请用 curl /doc 校验。


认证与安全

HTTP Basic Auth

Server 通过环境变量启用 HTTP Basic Auth(HTTP 基本认证)

# 启用认证(用户名默认 opencode)
OPENCODE_SERVER_PASSWORD="s3cret-pass" opencode serve

# 同时指定用户名
OPENCODE_SERVER_USERNAME="myteam" \
OPENCODE_SERVER_PASSWORD="s3cret-pass" \
opencode serve --hostname 0.0.0.0

启用后所有请求都必须带认证头:

curl -u opencode:s3cret-pass http://localhost:4096/global/health

🔴 危险动作:把 Server 暴露到公网(--hostname 0.0.0.0 且端口开放到 0.0.0.0:4096)却用默认密码或弱密码,等同于把整个代码仓库和 LLM API Key 拱手送人。任何能访问该端口的人都能创建会话、读写文件、执行 Shell。生产环境必须满足三条:

  1. 设置高强度 OPENCODE_SERVER_PASSWORD(≥ 32 字节随机串)
  2. 在反向代理(Nginx/Caddy)层再加 TLS 和速率限制
  3. 配合防火墙白名单限制来源 IP

CORS 默认策略

不传 --cors 时,Server 默认允许以下浏览器 Origin 跨域访问:

默认允许的 Origin用途
http://localhost:*本地开发服务器(Vite/Webpack dev server 等)
http://127.0.0.1:*本地回环地址变体
tauri://localhostTauri(桌面应用框架) WebView 环境
https://*.opencode.aiOpenCode 官方 Web 客户端

需要从其他域名访问时,用 --cors 追加。CORS 仅影响浏览器,curl/Python/Go 等非浏览器客户端不受限。

💡 设计决策:CORS 是浏览器同源策略的豁免机制,不是认证。即使 CORS 允许某 Origin,没带正确的 Basic Auth 凭据仍然会被 401 拒绝。不要把 CORS 当作安全边界。

不启用的安全默认

  • Server 自动启用 TLS,需在反向代理层终止 TLS
  • Server 做速率限制,需在反向代理层做
  • Server 审计日志,需在反向代理层或 Plugin Hook 层补齐
  • Server 做 IP 白名单,需在防火墙层做

这些“不做“是设计选择:Server 聚焦核心能力,基础设施层的责任交给基础设施。把 Server 放在内网或反向代理后面是生产部署的标准姿势。


接口分类导航

下表是 18 大类接口的导航索引。本表不是穷举参考手册——每类的完整字段、查询参数、响应结构请查 /doc。本表的目的是:你拿到一个需求后,能快速定位“该用哪类接口、对应哪个场景“。

#分类典型端点对应场景深入参考
1GlobalGET /global/healthGET /global/event健康检查、全局事件流场景一、场景三
2ProjectGET /projectGET /project/currentPOST /project/init未确认多项目管理、初始化新项目
3Path & VCSGET /pathGET /vcsGET /vcs/diff未确认工作目录、Git 状态查询
4InstancePOST /instance/dispose释放当前 Server 实例
5ConfigGET|PATCH /configGET /config/providers运行时配置读写、可用 Provider 列表
6ProviderGET /providerGET /provider/authPOST /provider/{id}/oauth/*模型供应商查询、OAuth(开放授权) 流程
7SessionGET|POST /sessionGET|DELETE|PATCH /session/:idPOST /session/:id/{init,fork,abort,share,summarize,revert} 等(18 端点)会话生命周期管理场景一、场景二
8MessagesGET|POST /session/:id/messageGET /session/:id/message/:messageIDPOST /session/:id/{prompt_async,command,shell}发送消息、获取单条消息、异步提示、执行命令场景一、场景二
9CommandsGET /command列出可用 slash 命令
10Find & FileGET /findGET /find/fileGET /find/symbolGET /fileGET /file/contentGET /file/status内容搜索、文件查找、符号查找、文件读写
11Tools(实验性)GET /experimental/tool/idsGET /experimental/tool工具枚举与 JSON Schema 查询
12LSP / Formatters / MCPGET /lspGET /formatterGET|POST /mcpLSP(语言服务器协议) 状态、格式化器、MCP 服务器管理
13TUIPOST /tui/{append-prompt,submit-prompt,open-*,execute-command,show-toast}GET|POST /tui/control/*(11 端点)远程驱动 TUI(IDE 插件用)
14AuthPUT /auth/:idDELETE /auth/:id未确认设置/删除 Provider 凭据
15EventGET /event会话级 SSE 事件流场景三
16DocGET /docOpenAPI 3.1 规范(HTML)
17LogPOST /log写入服务端日志
18AgentsGET /agent列出所有可用 Agent 定义

📌 关于“未确认“标记:标注的端点未在 https://opencode.ai/docs/server/ 公开文档中明确列出,可能来自源码或预发布版本。集成前请用 curl /doc 校验当前 Server 实例是否支持。OpenCode 团队保留在版本迭代中调整实验性端点的权利。

📌 关于实验性端点POST /experimental/workspace未确认POST /experimental/workspace/warp未确认POST /experimental/control-plane/move-session未确认 等实验性端点未列入上表分类,可在 /doc 中查证当前 Server 实例是否支持。

三类高频接口

如果你只关心最常用的接口,记住这三类即可覆盖 80% 集成场景:

  1. Global + Event —— 健康检查 + 实时事件流,是任何客户端启动后的第一件事
  2. Session + Messages —— 创建会话、发送消息、获取响应,是核心交互闭环
  3. Find & File —— 文件操作,用于读取 Agent 修改的代码或预处理上下文

场景一:curl 快速验证

目标:用 curl 走完“健康检查 → 列会话 → 发消息 → 收响应“全流程,建立对 API 的直观感受。

Step 1:启动 Server(带认证)

OPENCODE_SERVER_PASSWORD="dev-only-pass" opencode serve --port 4096
# 等待输出 "Server listening on http://127.0.0.1:4096"

Step 2:健康检查(Hello World)

curl -s -u opencode:dev-only-pass http://localhost:4096/global/health | jq

期望响应:

{
  "healthy": true,
  "version": "1.17.0"
}

healthy: true 说明 Server 进程正常、配置加载完成、Provider 链路就绪。如果返回 401,检查 Basic Auth 凭据;如果连接被拒,检查端口和 hostname。

Step 3:列出已有会话

curl -s -u opencode:dev-only-pass http://localhost:4096/session | jq '.data | length'
# 0 表示还没有会话

Step 4:创建新会话

SESSION=$(curl -s -u opencode:dev-only-pass \
  -X POST http://localhost:4096/session \
  -H "Content-Type: application/json" \
  -d '{"title":"curl-test"}' | jq -r '.data.id')

echo "Session ID: $SESSION"

Step 5:发送消息并等待响应

POST /session/:id/message 是同步接口——会等 Agent 完整生成响应后才返回。简单查询用这个;长任务用 prompt_async(见下文场景三)。

curl -s -u opencode:dev-only-pass \
  -X POST "http://localhost:4096/session/$SESSION/message" \
  -H "Content-Type: application/json" \
  -d '{
    "parts": [{ "type": "text", "text": "用一句话回答:1+1 等于几?" }]
  }' | jq '.data.parts'

响应的 parts 数组包含 Agent 输出的所有片段(文本、工具调用、工具结果等)。文本片段取 text 字段:

[
  { "type": "text", "text": "1 + 1 = 2。" }
]

Step 6:清理

curl -s -u opencode:dev-only-pass -X DELETE "http://localhost:4096/session/$SESSION"

💡 设计决策:curl 验证流之所以按“健康 → 列会话 → 建会话 → 发消息 → 删会话“这个顺序,是因为每一步都依赖前一步的成功。集成调试时把这个顺序固化为脚本,能快速定位“到底是 Server 没起来、认证失败、还是会话创建失败“。


场景二:非 JS 语言集成(Python/Go)

目标:用 Python 和 Go 各写一个最小可运行的客户端,覆盖“创建会话 + 发消息 + 取响应“闭环。两种实现都仅依赖标准库 + HTTP 客户端,不需要任何 OpenCode SDK。

Python 版(requests 库)

"""
OpenCode HTTP API 最小集成示例(Python)
依赖:pip install requests
"""
import os
import requests
from requests.auth import HTTPBasicAuth

BASE = os.environ.get("OPENCODE_URL", "http://localhost:4096")
AUTH = HTTPBasicAuth(
    os.environ.get("OPENCODE_SERVER_USERNAME", "opencode"),
    os.environ.get("OPENCODE_SERVER_PASSWORD", ""),
)
TIMEOUT = 120  # 同步消息接口可能较慢,超时设宽


def health() -> dict:
    r = requests.get(f"{BASE}/global/health", auth=AUTH, timeout=10)
    r.raise_for_status()
    return r.json()


def create_session(title: str = "py-client") -> str:
    r = requests.post(
        f"{BASE}/session",
        auth=AUTH,
        json={"title": title},
        timeout=TIMEOUT,
    )
    r.raise_for_status()
    return r.json()["data"]["id"]


def send_message(session_id: str, text: str) -> str:
    """同步发送消息并取回第一个文本片段。"""
    r = requests.post(
        f"{BASE}/session/{session_id}/message",
        auth=AUTH,
        json={"parts": [{"type": "text", "text": text}]},
        timeout=TIMEOUT,
    )
    r.raise_for_status()
    parts = r.json()["data"]["parts"]
    # 拼接所有文本片段
    return "".join(p.get("text", "") for p in parts if p.get("type") == "text")


def main():
    print("health:", health())
    sid = create_session()
    print(f"session: {sid}")
    reply = send_message(sid, "用一句话回答:什么是 OpenCode?")
    print(f"reply: {reply}")


if __name__ == "__main__":
    main()

运行:

OPENCODE_SERVER_PASSWORD="dev-only-pass" python client.py

Go 版(net/http)

Go 标准库即可,无需任何第三方依赖:

// OpenCode HTTP API 最小集成示例(Go),仅依赖标准库:go run main.go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"time"
)

var client = &http.Client{Timeout: 120 * time.Second}

// do 发送带认证的请求,解析 JSON 响应到 out。
func do(method, base, path string, body any, out any) error {
	var reqBody *bytes.Reader
	if body != nil {
		b, _ := json.Marshal(body)
		reqBody = bytes.NewReader(b)
	} else {
		reqBody = bytes.NewReader(nil)
	}
	req, _ := http.NewRequest(method, base+path, reqBody)
	user := os.Getenv("OPENCODE_SERVER_USERNAME")
	if user == "" {
		user = "opencode"
	}
	req.SetBasicAuth(user, os.Getenv("OPENCODE_SERVER_PASSWORD"))
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if out != nil {
		return json.NewDecoder(resp.Body).Decode(out)
	}
	return nil
}

func main() {
	base := os.Getenv("OPENCODE_URL")
	if base == "" {
		base = "http://localhost:4096"
	}

	// 1. 健康检查
	var health map[string]any
	if err := do("GET", base, "/global/health", nil, &health); err != nil {
		fmt.Println("health error:", err)
		os.Exit(1)
	}
	fmt.Printf("health: %+v\n", health)

	// 2. 创建会话
	var sess map[string]any
	if err := do("POST", base, "/session", map[string]string{"title": "go-client"}, &sess); err != nil {
		fmt.Println("create session error:", err)
		os.Exit(1)
	}
	sid := sess["data"].(map[string]any)["id"].(string)
	fmt.Println("session:", sid)

	// 3. 发送消息
	var msg map[string]any
	body := map[string]any{
		"parts": []map[string]string{{"type": "text", "text": "用一句话回答:什么是 OpenCode?"}},
	}
	if err := do("POST", base, "/session/"+sid+"/message", body, &msg); err != nil {
		fmt.Println("send message error:", err)
		os.Exit(1)
	}
	parts := msg["data"].(map[string]any)["parts"].([]any)
	for _, p := range parts {
		part := p.(map[string]any)
		if part["type"] == "text" {
			fmt.Println("reply:", part["text"])
		}
	}
}

运行:

OPENCODE_SERVER_PASSWORD="dev-only-pass" go run main.go

💡 设计决策:上面两个示例都把超时设到 120 秒。OpenCode 的同步消息接口在 Agent 调用工具、多轮思考时可能耗时数十秒,过短的超时会中断生成。生产环境建议优先用 prompt_async + SSE 监听(场景三),同步接口只用于短问答。

Go SDK 替代方案

如果不想自己拼请求体,OpenCode 官方提供 Go SDK:github.com/sst/opencode-sdk-go。它由 OpenAPI 自动生成,覆盖全部端点,适合需要长期维护的 Go 集成项目。安装与使用参考仓库 README——本文聚焦原生 HTTP 规范,不展开 SDK 用法。


场景三:SSE 事件流消费

目标:消费 Server 的事件流,实时拿到 Agent 的生成进度、工具调用、消息完成等事件。这是长任务场景下的标准做法——避免 HTTP 同步接口长时间阻塞,又能拿到细粒度进度。

两条事件流端点

端点范围首个事件
GET /event当前会话级事件会话连接就绪事件
GET /global/event全局事件(跨会话)server.connected

两个端点都是 SSE(Server-Sent Events,服务器推送事件) 协议,响应头 Content-Type: text/event-stream,每条事件形如 event: <type>\ndata: <json>\n\n

curl 消费 SSE

# 监听全局事件流(Ctrl+C 退出)
curl -N -u opencode:dev-only-pass http://localhost:4096/global/event

-N 禁用 curl 的输出缓冲,让事件实时打到终端。典型输出:

event: server.connected
data: {"version":"1.17.0"}

event: session.updated
data: {"sessionID":"sess_xxx","title":"curl-test"}

event: message.updated
data: {"sessionID":"sess_xxx","messageID":"msg_yyy","role":"assistant"}

event: tool.called
data: {"sessionID":"sess_xxx","tool":"write","path":"src/foo.ts"}

Python 消费 SSE

Python 标准库不直接支持 SSE,但可以手工解析 text/event-stream。下面是不依赖第三方库的实现:

# OpenCode SSE 事件流消费(Python,仅标准库)
# 用法:python sse_consumer.py        → 监听全局 /global/event
#       python sse_consumer.py <sid>  → 监听会话 /event
import base64, json, os, sys, urllib.request

BASE = os.environ.get("OPENCODE_URL", "http://localhost:4096")
USER = os.environ.get("OPENCODE_SERVER_USERNAME", "opencode")
PASS = os.environ.get("OPENCODE_SERVER_PASSWORD", "")


def stream(endpoint: str):
    auth = base64.b64encode(f"{USER}:{PASS}".encode()).decode()
    req = urllib.request.Request(
        f"{BASE}{endpoint}",
        headers={"Accept": "text/event-stream", "Authorization": f"Basic {auth}"},
    )
    with urllib.request.urlopen(req, timeout=None) as resp:
        event_type, data_lines = "", []
        for raw in resp:
            line = raw.decode("utf-8", errors="replace").rstrip("\n")
            if line == "" and data_lines:  # 空行 = 事件结束
                data = "\n".join(data_lines)
                try:
                    payload = json.loads(data)
                except json.JSONDecodeError:
                    payload = data
                print(f"[{event_type or 'message'}] {payload}")
                event_type, data_lines = "", []
            elif line.startswith("event:"):
                event_type = line[6:].strip()
            elif line.startswith("data:"):
                data_lines.append(line[5:].lstrip())


if __name__ == "__main__":
    endpoint = "/event" if len(sys.argv) > 1 else "/global/event"
    print(f"Listening on {endpoint}")
    try:
        stream(endpoint)
    except KeyboardInterrupt:
        print("\nStopped.")

异步消息 + SSE 的标准组合

长任务的标准模式是:用 prompt_async 触发生成(立即返回 204),再用 SSE 监听进度。这避免了 HTTP 长连接超时风险:

# 1. 异步发送消息(立即返回)
curl -s -u opencode:dev-only-pass \
  -X POST "http://localhost:4096/session/$SESSION/prompt_async" \
  -H "Content-Type: application/json" \
  -d '{"parts":[{"type":"text","text":"重构 src/ 下所有 Go 文件的导入顺序"}]}' \
  -o /dev/null -w "async sent: %{http_code}\n"
# 期望输出:async sent: 204

# 2. 另开一个终端监听事件流,看到 message.completed 事件即代表完成
curl -N -u opencode:dev-only-pass http://localhost:4096/global/event | grep "message.completed"

💡 设计决策:SSE 是单向流(Server → Client),不能用来发送消息。所以“异步发消息 + SSE 监听“是双通道设计:HTTP POST 发指令,SSE 收事件。这比 WebSocket 轻量、比长轮询高效,也是 OpenCode 的选择。客户端实现时建议两条连接独立管理,避免互相阻塞。


最佳实践

端口管理

  • 开发环境:固定用 4096,方便文档和示例统一
  • 测试环境:用 --port 0 让 OS 分配空闲端口,再从 Server 启动日志解析实际端口(CI 场景避免端口冲突)
  • 生产环境:固定端口 + 反向代理,对外只暴露 80/443

认证安全

场景推荐做法
本机开发不设密码即可(默认 127.0.0.1 不暴露)
局域网共享OPENCODE_SERVER_PASSWORD + 防火墙白名单
公网暴露强烈不推荐。如必须,加反向代理 + TLS + IP 白名单 + 速率限制 + 强密码

不要在生产环境用默认密码或示例里的 dev-only-pass。生成强密码:

openssl rand -base64 32

CORS 配置

  • 仅浏览器集成需要关心,curl/Python/Go 不受 CORS 影响
  • 生产 Web 应用通过反向代理同源访问,可避免 CORS 复杂性
  • 多域名场景用 --cors 显式追加,不要图省事用 --cors '*'(OpenCode 默认不允许任意 Origin)

错误处理

OpenCode Server 返回标准 HTTP 状态码:

状态码含义客户端应对
200成功解析响应体
204成功无内容(如 prompt_async不解析响应体
400请求参数错误检查 body schema,对照 /doc
401认证失败检查 Basic Auth 凭据
404路径不存在检查 URL,特别是 :id 类参数
500Server 内部错误查 Server 日志,可能需要重启

客户端实现建议:

  • 指数退避重试——仅对 5xx 和网络错误重试,3-5 次上限
  • 不重试 4xx——客户端错误重试无意义
  • 超时分层——健康检查 5s、文件操作 30s、消息同步接口 120s+
  • 会话幂等——同一个 session_id 重复发送同一条消息会生成多条响应,需要客户端用 messageID 做幂等键

性能考虑

  • 同步消息接口阻塞——POST /session/:id/message 会阻塞到 Agent 完成响应。长任务用 prompt_async + SSE
  • 会话上下文累积——同一会话多次发送消息,上下文会累积,Token 成本线性增长。定期 POST /session/:id/summarize 压缩历史
  • 文件读取代价——GET /file/content 对大文件无分页,整文件返回。读取大文件应改用 GET /find 按需搜索
  • 并发限制——Server 默认不限制并发会话,但底层 LLM Provider 有速率限制。生产环境在客户端层做令牌桶限流

版本兼容

OpenCode 处于快速迭代期,HTTP 接口可能在 minor 版本间调整。集成时建议:

  1. 锁定版本——Server 与客户端使用同一版本,CI 中固定 OpenCode 二进制版本
  2. 运行时校验——客户端启动时调用 /global/health 拿到 version,与预期版本对比
  3. 依赖 /doc 而非记忆——任何端点签名疑问都查 /doc,不要依赖本文或任何外部文档的记忆
  4. 实验性端点谨慎用——/experimental/* 端点随时可能变更或移除,生产环境避免依赖

→ SDK 客户端深度使用见 agent-sdk.md

本文聚焦“Server 端配置 + 原生 HTTP 规范 + 多语言集成“。如果你在写 TypeScript/JavaScript 应用,需要类型安全、自动重试、E2B 沙箱、CI/CD 集成、Docker 部署、成本监控、安全加固等更深度的客户端能力,请继续阅读:

OpenCode SDK:编程式 Agent 开发

那篇覆盖了 TypeScript SDK 的 createOpencode / createOpencodeClient 初始化、命名空间方法速查、一体化启动 vs 仅 Client 连接的取舍、E2B/Docker/CI/CD 集成模式、以及错误重试/超时/成本/安全等生产实践。如果你只需要 curl 或非 JS 语言的轻量集成,本文已覆盖完毕。


LSP 集成 —— 作为 Client

OpenCode 通过 LSP(Language Server Protocol,语言服务器协议) 与外部语言服务器集成,把类型检查、跳转定义、查找引用等 IDE 级能力作为反馈信号喂给 Agent(智能体)。先讲清楚一个最容易混淆的角色定位:OpenCode 在这套协议里扮演的是 client(客户端),不是 server(服务端)——它消费语言服务器的诊断结果,并把其中一部分能力重新打包成 Tool 暴露给 AI。

💡 角色澄清:很多读者第一次看到“LSP 集成“会以为 OpenCode 自己实现了一个语言服务器。事实正好相反——OpenCode 是 LSP client,它启动并管理外部的语言服务器(typescript-language-server、pyright、gopls 等),然后把诊断信息和导航能力转发给上层 Agent。


OpenCode 的 LSP 架构

OpenCode 的 LSP 子系统位于 packages/opencode/src/lsp/ 目录,核心由四个文件组成:

文件行数职责
lsp.ts~559LSP 接口定义、客户端匹配、诊断收集
client.ts~253单个语言服务器的客户端封装(基于 vscode-jsonrpc
server.ts~1968LSP 服务编排、生命周期管理
index.ts~100对外导出

底层通信使用 vscode-jsonrpc——这正是 VS Code 自己用来和语言服务器通信的同一个 JSON-RPC 实现,保证了协议兼容性。上层用 Effect(函数式效应框架) 管理所有 LSP 服务的生命周期、错误传播和资源回收。

三层架构

graph TB
    subgraph OC["OpenCode 进程"]
        Agent["Agent Loop<br/>AI 决策层"]
        Tool["LSP Tool<br/>tool/lsp.ts"]
        LSPServer["LSP 服务编排<br/>server.ts (Effect)"]
        ClientMgr["客户端匹配<br/>getClients(file)"]
    end

    subgraph Clients["LSP Client 层 (vscode-jsonrpc)"]
        TSC["TypeScript Client"]
        PyC["Pyright Client"]
        GoC["Gopls Client"]
        RaC["rust-analyzer Client"]
    end

    subgraph Ext["语言服务器进程 (独立子进程)"]
        TSSrv["typescript-language-server"]
        PySrv["pyright"]
        GoSrv["gopls"]
        RaSrv["rust-analyzer"]
    end

    Agent -->|"调用 diagnostics / definition / ..."| Tool
    Tool --> LSPServer
    LSPServer --> ClientMgr
    ClientMgr --> TSC
    ClientMgr --> PyC
    ClientMgr --> GoC
    ClientMgr --> RaC
    TSC -.->|"stdio JSON-RPC"| TSSrv
    PyC -.->|"stdio JSON-RPC"| PySrv
    GoC -.->|"stdio JSON-RPC"| GoSrv
    RaC -.->|"stdio JSON-RPC"| RaSrv

    classDef opencode fill:#4A90D9,color:#fff,stroke:#2E5C8A
    classDef external fill:#A66CFF,color:#fff,stroke:#6B4BCC
    class Agent,Tool,LSPServer,ClientMgr,TSC,PyC,GoC,RaC opencode
    class TSSrv,PySrv,GoSrv,RaSrv external

图里要特别注意三件事:

  1. Agent 不直接调语言服务器。AI 只能通过 tool/lsp.ts 这个 Tool 间接访问 LSP 能力,Tool 内部再走 lsp.ts 暴露的接口。这是 Generator-Evaluator 模式的体现——执行层和验证层分离,AI 不能自己改自己看到的诊断。
  2. 每个语言服务器是独立子进程。OpenCode 用 stdio 上的 JSON-RPC 和它们通信,崩溃不影响主进程,但也要为启动开销买单。
  3. getClients(file) 是匹配中枢。它根据文件扩展名(.ts → typescript、.py → pyright、.go → gopls……)返回 0 到 N 个相关客户端——一个文件可能同时匹配多个服务器,比如 .ts 文件会同时触发 typescript 和 eslint。

诊断收集策略

诊断(diagnostics)是 LSP 给 Agent 最直接的反馈。OpenCode 的收集策略经过精心调优:

  • 150ms 防抖:文件变更后等待 150ms 才向语言服务器请求诊断,避免每次按键都触发一轮请求
  • 3 秒超时:单次诊断请求超过 3 秒直接放弃,防止慢服务器拖垮 Agent 循环
  • push + pull 双模式:既监听 textDocument/publishDiagnostics 推送,也支持主动拉取,兼容不同语言服务器的实现风格

💡 为什么是 150ms:这是人类连续输入的典型停顿间隔。比这个短会浪费请求,比这个长会让 Agent 等太久才看到错误。

内置支持的语言服务器

OpenCode 内置 34 种语言服务器,覆盖主流编程语言。LSP 默认禁用,需要显式开启。下表列出常见的几类:

语言服务器文件扩展名启动条件
TypeScript / JavaScripttypescript.ts .tsx .js .jsx .mjs .cjs .mts .cts项目有 typescript 依赖
TypeScript(替代)deno同上deno 命令
Linteslint.ts .tsx .js .jsx .mjs .cjs .mts .cts .vue项目有 eslint 依赖
Lint(替代)oxlint同 eslint + .vue .astro .svelte项目有 oxlint 依赖
Pythonpyright.py .pyi已安装 pyright
Gogopls.gogo 命令
Rustrust.rs配置键为 rust,命令为 rust-analyzer
Javajdtls.java装了 Java SDK 21+
C / C++clangd.c .cpp .cc .cxx .c++ .h .hpp .hh .hxx .h++C/C++ 项目自动安装
Rubyruby-lsp.rb .rake .gemspec .rurubygem
Lualua-ls.lua自动安装
Bashbash.sh .bash .zsh .ksh自动安装

完整列表见 官方文档。LSP 文件被打开时,OpenCode 会按扩展名匹配服务器并启动——前提是项目满足“启动条件“那一列里的依赖。


语言服务器配置

LSP 通过 opencode.jsonlsp 字段配置。三种取值:

取值含义
省略所有 LSP 服务器禁用(默认)
true启用所有内置 LSP 服务器
对象 {}启用内置服务器,并允许覆盖或添加自定义

启用所有内置服务器

{
  "$schema": "https://opencode.ai/config.json",
  "lsp": true
}

禁用特定服务器

只关掉 typescript,保留其他:

{
  "$schema": "https://opencode.ai/config.json",
  "lsp": {
    "typescript": {
      "disabled": true
    }
  }
}

给服务器传环境变量

rust-analyzer 想看 debug 日志:

{
  "$schema": "https://opencode.ai/config.json",
  "lsp": {
    "rust": {
      "command": ["rust-analyzer"],
      "env": {
        "RUST_LOG": "debug"
      }
    }
  }
}

传初始化选项

某些语言服务器接受 initialize 请求里的初始化选项(每个服务器自己定义的 schema):

{
  "$schema": "https://opencode.ai/config.json",
  "lsp": {
    "custom-lsp": {
      "command": ["custom-lsp-server", "--stdio"],
      "extensions": [".custom"],
      "initialization": {
        "preferences": {
          "importModuleSpecifierPreference": "relative"
        }
      }
    }
  }
}

添加自定义语言服务器

只要服务器说 LSP 协议,就能挂进来:

{
  "$schema": "https://opencode.ai/config.json",
  "lsp": {
    "custom-lsp": {
      "command": ["custom-lsp-server", "--stdio"],
      "extensions": [".custom"]
    }
  }
}

每个服务器条目支持的字段:

属性类型说明
disabledboolean设为 true 禁用该服务器
commandstring[]启动命令(除非只用于禁用,否则必填)
extensionsstring[]该服务器处理的文件扩展名
envobject启动时设置的环境变量
initializationobjectinitialize 请求里的初始化选项

⚠️ 避免自动下载:默认情况下 OpenCode 会自动下载缺失的语言服务器。在离线或受限环境里,设环境变量 OPENCODE_DISABLE_LSP_DOWNLOAD=true 关掉这个行为,所有服务器必须由你预先装好。


暴露给 AI 的 10 个 LSP 接口

OpenCode 在 packages/opencode/src/lsp/lsp.tsInterface 中定义了 10 个对外暴露的 LSP 方法。这是 AI 能看到的全部 LSP 能力——任何不在这张表里的 LSP 功能(rename、formatting、codeLens 等)AI 都用不到。

#方法用途输入
1diagnostics()获取所有文件诊断信息(错误/警告/hint)
2hover(input)符号悬停信息(类型签名、文档)LocInput
3definition(input)跳转到符号定义LocInput
4references(input)查找符号的所有引用LocInput
5implementation(input)查找接口的实现LocInput
6documentSymbol(uri)文档符号树(函数/类/变量列表)uri: string
7workspaceSymbol(query)工作区符号搜索query: string
8prepareCallHierarchy(input)准备调用层次LocInput
9incomingCalls(input)谁调用了这个函数LocInput
10outgoingCalls(input)这个函数调用了谁LocInput

LocInput 是位置参数的统一格式:

interface LocInput {
  file: string       // 文件绝对路径
  line: number       // 行号(1-based,不是 0-based)
  character: number  // 列号(1-based,不是 0-based)
}

⚠️ 1-based vs 0-based 陷阱:标准 LSP 规范的行列号是 0-based,但 OpenCode 这层 LocInput 是 1-based——更贴近编辑器显示的行列号。如果你从 LSP 原始响应里抠位置再回传,记得做转换。

Tool 接口

packages/opencode/src/tool/lsp.ts(~150 行)把上面的接口包装成 AI 可调用的 Tool,支持 9 种 operation:

operation对应的 Interface 方法
goToDefinitiondefinition(input)
findReferencesreferences(input)
hoverhover(input)
documentSymboldocumentSymbol(uri)
workspaceSymbolworkspaceSymbol(query)
goToImplementationimplementation(input)
prepareCallHierarchyprepareCallHierarchy(input)
incomingCallsincomingCalls(input)
outgoingCallsoutgoingCalls(input)

注意 diagnostics() 没有出现在 Tool 的 operation 列表里——它由 Agent Loop 自动消费(每次工具调用后内部拉取),AI 不需要显式调用。这是有意的设计:诊断是反馈信号,不应该让 AI 决定要不要看。


场景一:在 AI 工具中使用 LSP 能力

下面演示 AI 在编辑代码时如何通过 LSP Tool 获取结构化反馈。这是 LSP 集成最有价值的场景——AI 不再靠“通读文件猜错误“,而是直接拿到编译器级别的诊断。

子场景 A:用 diagnostics 定位类型错误

Agent 改完一个 TypeScript 文件后,内部循环自动调用 diagnostics()。返回的诊断结构类似:

{
  "file": "/project/src/utils.ts",
  "diagnostics": [
    {
      "range": {
        "start": { "line": 12, "character": 5 },
        "end":   { "line": 12, "character": 14 }
      },
      "severity": "error",
      "source": "typescript",
      "message": "Property 'fetchUser' does not exist on type 'UserApi'."
    }
  ]
}

Agent 拿到这个就明白:第 13 行(1-based)调用 userApi.fetchUser 是错的,方法名可能拼错或类型定义缺失,需要去查 UserApi 的定义。

子场景 B:用 goToDefinition 追溯方法来源

想知道 userApi.fetchUser 究竟定义在哪里,Agent 调用 lsp Tool 的 goToDefinition

{
  "tool": "lsp",
  "input": {
    "operation": "goToDefinition",
    "file": "/project/src/utils.ts",
    "line": 13,
    "character": 11
  }
}

返回的 Location 数组告诉 Agent 定义所在文件和范围,Agent 接着读那个文件就能确认签名。

子场景 C:用 findReferences 评估重构影响

准备重命名一个公共方法前,先看它被多少地方调用:

{
  "tool": "lsp",
  "input": {
    "operation": "findReferences",
    "file": "/project/src/api/user.ts",
    "line": 8,
    "character": 10
  }
}

返回所有引用位置。如果命中 50 处,Agent 会谨慎——可能分批改或者先和用户确认。

子场景 D:用 workspaceSymbol 跨文件找符号

只记得有个 validateEmail 函数但不知道在哪个文件:

{
  "tool": "lsp",
  "input": {
    "operation": "workspaceSymbol",
    "query": "validateEmail"
  }
}

返回所有匹配符号的文件和位置。

💡 效率提示workspaceSymbol 比让 Agent 用 grep 全仓搜文本快得多,而且结果经过语义匹配——不会把注释里出现的 “validateEmail” 也算进来。

一个完整的 Agent 决策循环

把上面几个能力串起来,Agent 处理“修复 utils.ts 编译错误“任务的内部循环大致是:

  1. diagnostics() 拿到错误清单
  2. 对每个错误位置调 hover() 看期望类型
  3. goToDefinition() 跳到相关 API 的定义
  4. findReferences() 评估修改影响面
  5. 改完代码后再调 diagnostics() 验证

这是 LSP 集成真正改变 Agent 工作流的地方——AI 从“猜代码“升级到“看懂代码“。


生态扩展:opencode.nvim 作为 LSP server

⚠️ 本节是第三方扩展能力,不是 OpenCode 核心能力。OpenCode 本身是 LSP client,下面讲的“作为 LSP server“完全是 Neovim 扩展 nickjvandyke/opencode.nvim 的实现,不属于 OpenCode 核心仓库。

这是什么

nickjvandyke/opencode.nvim 是社区维护的 Neovim 扩展,其中的 lsp/opencode.lua 模块实现了一个进程内 LSP server。它把 OpenCode 反过来包装成语言服务器,让 Neovim 可以像调用 pyright、tsserver 一样调用 OpenCode 的能力。

实现的 LSP 方法

LSP 方法行为
textDocument/hover收到悬停请求时,调用 OpenCode 解释光标处的符号
textDocument/codeAction针对当前诊断生成修复命令
workspace/executeCommand执行 opencode.fix 命令,触发 OpenCode 修复指定诊断

–attach 模式

-- 连接已运行的 OpenCode server,不重新启动一个
require('opencode').setup({
  attach = true
})

--attach 让 Neovim 不启动新的 OpenCode 实例,而是连到已经在 TUI 里运行的那个——避免双实例状态分裂。

为什么这个能力重要

它打通了“编辑器内诊断 → 一键 AI 修复“的链路:Neovim 显示的红色波浪线(来自 pyright、tsserver 等真正的语言服务器),右键 code action 就能让 OpenCode 生成补丁,不需要复制粘贴错误信息到 TUI。

但请记住——这是 Neovim 生态的玩法,VS Code 扩展(sst/opencode/sdks/vscode/)走的是 HTTP 通信路线,没有实现 LSP server。OpenCode 核心也始终是 LSP client 角色。


最佳实践

该不该开 LSP

OpenCode 官方文档的明确建议是:LSP 不一定净增益。语言服务器会带来四个代价:

代价具体表现
内存占用rust-analyzer 单实例常驻 500MB+
版本漂移同一项目不同机器的语言服务器版本可能不一致,诊断结果不同
启动开销大型项目首次启动 gopls 要 10-30 秒
同步延迟改完代码到诊断刷新有 150ms+ 延迟,期间 Agent 看到的是旧错误

如果你的项目本来就有 tsc --noEmitruff checkgo vet 这类 CLI 检查工具,让 Agent 直接跑这些命令往往更可靠——错误信息明确、可重现、不依赖服务器状态。把这些命令写进 AGENTS.md 或 Skill,让 Agent 知道该跑什么。

LSP 适合的场景

场景LSP 是否推荐理由
大型 TypeScript 项目,类型复杂✅ 推荐tsserver 的类型推导比 tsc 报错信息更精准
单文件 Python 脚本❌ 不推荐启动 pyright 的开销大于收益
Rust 项目✅ 推荐rust-analyzer 的借用检查反馈无替代品
多语言混合仓库⚠️ 谨慎多个语言服务器同时启动,内存压力大
CI/CD 自动化循环❌ 不推荐服务器启动时间不可控,用 CLI 工具更稳定

性能调优

如果决定开 LSP,按下面几点调优:

  1. 只开必要的语言服务器。别一股脑 "lsp": true,按需开:

    {
      "$schema": "https://opencode.ai/config.json",
      "lsp": {
        "typescript": {},
        "rust": {}
      }
    }
    

    只显式列你需要的几个,其他保持禁用。

  2. 离线环境关掉自动下载

    export OPENCODE_DISABLE_LSP_DOWNLOAD=true
    

    避免每次启动都尝试联网下载缺失的服务器。

  3. 监控诊断超时。如果某个语言服务器经常 3 秒超时,说明它已经跟不上 Agent 改文件的速度——这种情况下不如关掉它,改用 CLI。

常见问题

Q1:Agent 报告“诊断不变“但代码明明改对了

A:很可能是 LSP 服务器还没同步文件变更。OpenCode 的 150ms 防抖是为了等输入停顿,但大型项目里 rust-analyzer / jdtls 的索引时间远超这个值。解决:在 Skill 里加一步“等待 2 秒后再拉诊断“,或者干脆用 CLI 工具替代。

Q2:多个语言服务器同时匹配一个文件

A:.ts 文件会同时触发 typescript、eslint、oxlint 三个服务器,诊断结果可能冲突。建议只保留一个 lint 来源——要么 eslint 要么 oxlint,不要都开。

Q3:LSP 服务器启动失败

A:检查“启动条件“那一列——typescript 需要项目里有 typescript 依赖,gopls 需要 go 命令在 PATH 里。CI 环境里尤其要注意 PATH 是否完整。


MCP 集成 —— 作为 Client

OpenCode 内置 MCP(Model Context Protocol,模型上下文协议) client 能力,可以把外部工具/数据源接入 Agent 工作流;社区另有第三方项目 opencode-mcp 反向把 OpenCode 暴露为 MCP server 供其他客户端调用。本章按场景讲清楚两条路径。

关键事实先说清:OpenCode 官方角色是 MCP client,不是 server。官方仓库(anomalyco/opencode)只实现了 client 侧——通过 opencode.jsonmcp 字段把外部 MCP servers 注册进来,让 Agent 像调用内置工具一样调用它们。如果你看到“OpenCode 作为 MCP server“的能力,那一定是来自社区第三方项目(见后文“生态扩展“一节),不要混淆。

flowchart TB
    subgraph OC[OpenCode 进程]
        Agent[Agent Loop]
        Client[MCP Client]
        Agent -->|调用| Client
    end
    subgraph Ext[外部]
        S1["Local MCP Server<br/>stdio"]
        S2["Remote MCP Server<br/>HTTP/SSE"]
        S3["OAuth MCP Server<br/>如 Sentry/GitHub"]
    end
    Client -. "local stdio" .-> S1
    Client -. "remote HTTP" .-> S2
    Client -. "OAuth 2.1" .-> S3
    style S1 fill:#A66CFF
    style S2 fill:#A66CFF
    style S3 fill:#A66CFF
    style Client fill:#4A90D9

OpenCode 作为 MCP client 的配置

所有 MCP server 都在 opencode.jsonmcp 字段下声明,每个 server 用一个唯一名称作为 key,后续在 prompt 里通过该名称引用(例如 use context7 to ...)。

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "my-local-server": {
      "type": "local",
      "command": ["npx", "-y", "my-mcp-command"],
      "enabled": true,
      "environment": { "MY_ENV_VAR": "value" },
      "cwd": "./workspace",
      "timeout": 5000
    },
    "my-remote-server": {
      "type": "remote",
      "url": "https://mcp.example.com/mcp",
      "enabled": true,
      "headers": { "Authorization": "Bearer {env:MY_API_KEY}" },
      "timeout": 5000
    }
  }
}

字段速查

Local 类型(stdio 子进程):

字段类型必填说明
typestring固定为 "local"
commandstring[]启动 MCP server 的命令及参数,例如 ["npx","-y","@modelcontextprotocol/server-filesystem","."]
cwdstring子进程工作目录,相对路径基于工作区
environmentobject注入子进程的环境变量
enabledboolean是否启用,设为 false 可临时禁用而不删除配置
timeoutnumber拉取工具列表的超时(毫秒),默认 5000

Remote 类型(HTTP/SSE):

字段类型必填说明
typestring固定为 "remote"
urlstringMCP server 端点 URL
headersobject随请求发送的 HTTP 头,支持 {env:VAR} 占位符读取环境变量
oauthobject | falseOAuth 配置对象,或 false 显式关闭自动 OAuth 探测
enabledboolean同上
timeoutnumber同上

💡 设计决策{env:VAR_NAME} 占位符是 OpenCode 推荐的密钥管理方式——配置文件可以提交到 Git,密钥留在 shell 环境。永远不要headersenvironment 里硬编码 token。

MCP server 的工具如何被 Agent 使用

注册完成后,MCP server 暴露的所有工具会自动以 <server-name>_<tool-name> 的命名加入到 Agent 的可用工具集,和内置工具并列。在 prompt 里加一句 use <server-name> 即可触发,也可以在 AGENTS.md 里写规则让 Agent 默认使用:

当你需要查询最新库文档时,使用 `context7` 工具。
当你不确定某个 API 用法时,用 `gh_grep` 搜索 GitHub 代码示例。

全局禁用与按 Agent 启用

如果 MCP server 较多但只希望特定 Agent 使用,可以在 tools 字段用 glob 模式先全局禁用、再在某个 agent 上单独启用:

{
  "mcp": { "my-mcp": { "type": "local", "command": ["bun","x","my-mcp"], "enabled": true } },
  "tools": { "my-mcp*": false },
  "agent": {
    "reviewer": { "tools": { "my-mcp*": true } }
  }
}

glob 规则:* 匹配零个或多个任意字符,? 匹配单个字符。MCP 工具名以 server 名为前缀,所以 "my-mcp*" 会匹配 my-mcp_searchmy-mcp_list 等全部工具。


CLI 命令管理

OpenCode 提供 4 个 opencode mcp 子命令用于运行时管理。所有命令在终端直接执行,不依赖 TUI 会话。

opencode mcp list —— 列出所有已配置的 server

opencode mcp list

示例输出:

NAME                TYPE      ENABLED  AUTH     URL/COMMAND
context7            remote    true     none      https://mcp.context7.com/mcp
sentry              remote    true     ok        https://mcp.sentry.dev/mcp
filesystem          local     true     n/a       npx -y @modelcontextprotocol/server-filesystem .
my-oauth-server     remote    false    expired   https://mcp.example.com/mcp

AUTH 列反映 OAuth 令牌状态:none(无需认证)、ok(令牌有效)、expired(需重新认证)、n/a(local server 不适用)。

opencode mcp auth <server> —— 触发 OAuth 认证

opencode mcp auth sentry

执行后会在默认浏览器打开授权页面,用户完成授权后 OpenCode 把 token 写入 ~/.local/share/opencode/mcp-auth.json。命令本身是阻塞的——直到拿到 token 或用户取消才返回。

也可以用 opencode mcp auth list 一次性查看所有 OAuth-capable server 的认证状态。

opencode mcp logout <server> —— 清除已存储的凭据

opencode mcp logout sentry

仅清除本地存储的 token,不会改动 opencode.json 配置。下次使用该 server 时会再次触发 OAuth 流程。

opencode mcp debug <server> —— 调试连接问题

opencode mcp debug my-oauth-server

输出包含:当前认证状态、HTTP 连通性测试结果、OAuth discovery 流程的逐步日志。当 remote server 报 401 或工具列表拉取超时时,这是首选排查命令。

通过 HTTP API 程序化管理

如果你用 SDK 把 OpenCode 嵌入到自动化流程中,可以通过 HTTP 端点管理 MCP server(与 opencode mcp CLI 等价):

方法路径作用
GET/mcp列出所有已配置的 MCP server 及状态
POST/mcp新增/更新 MCP server 配置
POST/mcp/:name/connect未确认主动连接指定 server(触发 OAuth 流程)
POST/mcp/:name/disconnect未确认断开指定 server

💡 注意connect/disconnect 端点未在官方文档中明确列出,使用前请以运行时 /doc 为准。HTTP API 适合 CI/CD 场景——在流水线开始时通过 POST /mcp/:name/connect 预热连接,避免首次工具调用的冷启动延迟。本地面交互场景直接用 CLI 更直观。


场景一:连接外部 MCP servers(含 OAuth 认证)

下面三个子场景按“复杂度递增“组织:本地 stdio → 远程 HTTP → 远程 OAuth。每个场景给出完整配置、验证步骤和典型坑。

场景 1a:连接本地 stdio MCP server

以官方 @modelcontextprotocol/server-filesystem 为例,让 Agent 读写指定目录的文件。

配置

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "filesystem": {
      "type": "local",
      "command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/Users/me/notes"],
      "enabled": true,
      "timeout": 5000
    }
  }
}

验证步骤

  1. 启动 OpenCode 后运行 opencode mcp list,应看到 filesystemENABLED=true
  2. 在 prompt 里测试:use filesystem to list files in /Users/me/notes
  3. 如果工具列表为空,运行 opencode mcp debug filesystem 检查 npx 是否成功拉起子进程。

常见坑

  • command 是数组,不是字符串。"command": "npx -y ..." 会失败,必须写成 ["npx","-y","..."]
  • cwd 默认是工作区根目录。如果 MCP server 内部用相对路径读文件,结果会指向工作区而非你期望的目录。
  • Windows 上 npx 可能需要写成 npx.cmd,具体看 Node.js 安装方式。

场景 1b:连接远程 HTTP MCP server

以 Context7(拉取最新库文档防止 API 幻觉)为例:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "context7": {
      "type": "remote",
      "url": "https://mcp.context7.com/mcp",
      "enabled": true
    }
  }
}

如果你注册了 Context7 账号拿到 API key,可以加 headers 提升速率限制:

{
  "mcp": {
    "context7": {
      "type": "remote",
      "url": "https://mcp.context7.com/mcp",
      "headers": { "CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}" }
    }
  }
}

验证:在 prompt 中输入 Configure a Cloudflare Worker to cache JSON responses for 5 minutes. use context7,Agent 应调用 Context7 工具拉取最新文档后再生成代码。

场景 1c:连接需要 OAuth 认证的 MCP server

以 Sentry MCP 为例——它要求用户先完成 OAuth 授权才能查询自己账号下的 issue。

第 1 步:声明 remote server,留空 oauth 字段表示走自动 OAuth

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "sentry": {
      "type": "remote",
      "url": "https://mcp.sentry.dev/mcp",
      "oauth": {}
    }
  }
}

OpenCode 检测到 oauth 字段为空对象时,会在首次调用工具收到 401 后自动启动 OAuth 流程,优先尝试 Dynamic Client Registration(动态客户端注册,RFC 7591),无需用户预先申请 clientId。

第 2 步:手动触发认证

虽然首次调用会自动触发,但建议提前完成认证避免阻塞:

opencode mcp auth sentry

浏览器会打开 Sentry 授权页面,登录并同意后 token 自动写入 ~/.local/share/opencode/mcp-auth.json

第 3 步:验证认证状态

opencode mcp list
# AUTH 列应显示 ok

或直接用 prompt 测试:Show me the latest unresolved issues in my project. use sentry

预注册 clientId 的写法(适合企业内自建 MCP server 已有 OAuth 应用):

{
  "mcp": {
    "internal-mcp": {
      "type": "remote",
      "url": "https://mcp.internal.corp/mcp",
      "oauth": {
        "clientId": "{env:MY_MCP_CLIENT_ID}",
        "clientSecret": "{env:MY_MCP_CLIENT_SECRET}",
        "scope": "tools:read tools:execute"
      }
    }
  }
}

关闭 OAuth 自动探测(适合用 API key 而非 OAuth 的 server):

{
  "mcp": {
    "my-api-key-server": {
      "type": "remote",
      "url": "https://mcp.example.com/mcp",
      "oauth": false,
      "headers": { "Authorization": "Bearer {env:MY_API_KEY}" }
    }
  }
}

oauth: false 显式告诉 OpenCode“不要尝试 OAuth 流程“,避免每次 401 都触发无意义的认证尝试。

OAuth 排查清单

症状排查命令可能根因
opencode mcp list 显示 expiredopencode mcp auth <server> 重新认证refresh token 过期
工具调用一直 401opencode mcp debug <server>clientId/scope 不匹配,或 server 不支持 Dynamic Client Registration
浏览器没自动打开检查 $BROWSER 环境变量无默认浏览器或 SSH 环境,需手动复制授权 URL

生态扩展:opencode-mcp 将 OpenCode 暴露为 MCP server

⚠️ 重要声明opencode-mcp社区驱动的第三方项目不是 OpenCode 官方能力。主仓库为 AlaeddineMessadi/opencode-mcp,社区另有 hardened fork MekaretEriker/opencode-mcp(推荐生产环境使用)。OpenCode 核心团队不维护该项目,issue 请提到上述仓库。

解决什么问题

OpenCode 官方只做 MCP client——它能调用外部 MCP server,但自身能力(会话管理、文件操作、Provider 切换等)无法被其他 MCP client(如 Claude Desktop、Cursor)调用。opencode-mcp 项目填补了这个空白:它把一个运行中的 OpenCode 实例包装成 MCP server,对外暴露约 80 个工具、10 个资源、6 个提示模板。

flowchart TB
    subgraph Clients[其他 MCP Client]
        CD[Claude Desktop]
        CR[Cursor]
    end
    subgraph Bridge[opencode-mcp 进程]
        Server["MCP Server<br/>stdio/SSE/StreamableHTTP"]
        OCClient[OpenCode Client]
        Server --> OCClient
    end
    subgraph Core[OpenCode Server]
        APIs["REST API<br/>会话/文件/Provider"]
    end
    CD -.MCP.-> Server
    CR -.MCP.-> Server
    OCClient -.HTTP.-> APIs
    style Server fill:#A66CFF
    style Bridge fill:#A66CFF

客户端配置示例

在 Claude Desktop 或 Cursor 的 MCP 配置中加入:

{
  "mcpServers": {
    "opencode": {
      "command": "npx",
      "args": ["-y", "opencode-mcp"]
    }
  }
}

💡 注意:这里的 mcpServers 字段是 Claude Desktop 的配置格式,不是 OpenCode 的 mcp 字段——别搞混。Claude Desktop 仍然是 MCP client,opencode-mcp 是被它调用的 server。

启动后 Claude Desktop 即可通过 opencode_* 系列工具操作一个独立的 OpenCode 实例,支持多项目并行、自动启动。

工具分类速览(按 11 大类概述,约 80 个)

不需要逐一列举,按分类理解能力边界即可:

分类数量代表工具用途
工作流工具13opencode_setup / opencode_ask / opencode_run启动会话、提问、运行任务
会话工具20create / list / fork / share会话生命周期管理
消息工具6send / execute / shell发送消息、执行 shell
文件与搜索6read / write / search / symbol_search文件操作与符号检索
配置工具3get / set / list读写 opencode.json
Provider 与认证6models / set_key / oauth / status模型与凭据管理
TUI 控制9focus / input / resize / screenshot远程操控 TUI 界面
系统与监控13health / vcs_info / instance_dispose健康检查与进程管理
事件工具1events_poll长轮询事件流
项目工具3list / get / init多项目管理
全局工具1global_status全局状态查询

注:工具数量会随 opencode-mcp 版本迭代变化,精确清单详见 opencode-mcp 仓库 README

资源清单(10 个,均为 application/json

通过 opencode:// URI scheme 暴露:

URI内容
opencode://project/current当前项目信息
opencode://config完整 opencode.json 配置
opencode://providers所有 Provider 列表
opencode://agents所有 Agent 定义
opencode://commands所有可用命令
opencode://health健康状态
opencode://vcs版本控制信息
opencode://sessions会话列表
opencode://mcp-servers已注册的 MCP servers
opencode://file-status文件变更状态

提示模板清单(6 个)

模板名用途
opencode-code-review代码审查工作流
opencode-debug调试问题
opencode-project-setup项目初始化
opencode-implement功能实现
opencode-best-practices最佳实践查询
opencode-session-summary会话摘要生成

传输方式

支持三种 MCP 传输协议,按部署场景选择:

传输方式适用场景启动方式
stdioClaude Desktop / Cursor 本地集成默认,command+args 启动
SSE需要长连接的 Web 客户端启动时加 --transport sse --port 3001
StreamableHTTPHTTP 友好的环境、负载均衡启动时加 --transport streamable-http --port 3001

主仓库 vs hardened fork

维度AlaeddineMessadi/opencode-mcpMekaretEriker/opencode-mcp
定位主仓库,功能最新Hardened fork,稳定性优先
适用场景试用新功能、参与贡献生产环境、企业部署
安全审计社区维护额外的输入校验和速率限制
推荐度学习/原型★ 生产首选

最佳实践

MCP server 选择

  1. 按需启用,不要全装:每启用一个 MCP server 都会向上下文注入工具描述,GitHub MCP 这种工具数多的 server 单独就能占掉数千 token。建议当前工作流用到哪个装哪个,不用的设为 enabled: false
  2. 优先选官方维护的 server@modelcontextprotocol/server-* 系列由 MCP 协议团队维护,质量有保障。社区 server 使用前查看最近提交时间和 issue 响应速度。
  3. Local 优先于 Remote:能本地跑的 server 不要走远程 HTTP——少一次网络往返、少一份认证负担。PostgreSQL、SQLite、filesystem 这类工具天然适合 local。

超时配置

  • 默认 timeout: 5000(5 秒)适合大多数轻量 server。
  • 工具调用本身耗时长的 server(如 Puppeteer 浏览器自动化)建议提到 15000 或更高。
  • 不要无脑调到 60000+——如果 server 5 秒拉不到工具列表,多半是配置错了或网络断了,等更久只会拖慢 OpenCode 启动。

安全考虑

  1. {env:VAR} 占位符强制使用:API key、OAuth secret 一律走环境变量,配置文件可以放心提交 Git。在 CI 中通过 secret 注入环境变量。
  2. Local server 的 command 要固定版本npx -y foo@latest 每次拉最新版可能引入供应链风险,建议写成 npx -y foo@1.2.3
  3. OAuth token 存储路径~/.local/share/opencode/mcp-auth.json 是明文 JSON,文件权限应为 600。共享机器上注意隔离用户目录。
  4. Per-agent 隔离敏感工具:财务/数据库类 MCP server 全局禁用,只在专门的 admin agent 上启用,避免普通对话误触。

性能优化

  • 预热连接:CI 流水线开始时用 POST /mcp/:name/connect未确认 主动建立连接,避免首次工具调用的冷启动。
  • Glob 批量禁用:临时不想用某类工具时,"my-mcp*": false 一次禁用整个 server,比重启 OpenCode 改配置快。
  • 监控 token 消耗:如果发现 Agent 上下文占用异常增长,先用 opencode mcp list 看启用了多少 server,再考虑用 glob 模式关掉一部分。

反模式

  • 同时启用 10+ MCP server:上下文被工具描述占满,留给实际任务的 token 不够,Agent 输出质量明显下降。
  • opencode.json 存 token:即使配置文件不提交 Git,本机被入侵后 token 直接泄露。一律走 {env:VAR}
  • 把 opencode-mcp 当成 OpenCode 官方能力向团队推广:会误导团队对 OpenCode 能力边界的认知,遇到 issue 找错仓库。
  • OAuth 失败后直接改用 long-lived API key 写死在 headers:扩大了凭据泄露面,应优先排查 OAuth 配置问题。

→ MCP 配置速查见 OpenCode 生态参考,MCP 服务器开发见 MCP(模型上下文协议) 服务器


相关章节