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.json的mcp字段把外部 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 API | Server | 18 大类端点(/session、/tool、/agent 等) | — |
| LSP | Client | 10 个暴露给 AI 的方法 + 9 种 Tool operation | GET /lsp(查询状态) |
| MCP | Client | opencode.json mcp 字段 + CLI 命令 | GET|POST /mcp、POST /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]
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--port | number | 4096 | 监听端口。若端口被占用,Server 启动失败而非自动换端口 |
--hostname | string | 127.0.0.1 | 监听地址。0.0.0.0 表示监听所有网卡(暴露到局域网,需配合认证) |
--mdns | bool | false | 启用 mDNS(多播 DNS) 服务发现,局域网内可零配置发现 Server |
--mdns-domain | string | opencode.local | mDNS 服务名,配合 --mdns 使用 |
--cors | string[] | [] | 追加允许的浏览器 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。生产环境必须满足三条:
- 设置高强度
OPENCODE_SERVER_PASSWORD(≥ 32 字节随机串)- 在反向代理(Nginx/Caddy)层再加 TLS 和速率限制
- 配合防火墙白名单限制来源 IP
CORS 默认策略
不传 --cors 时,Server 默认允许以下浏览器 Origin 跨域访问:
| 默认允许的 Origin | 用途 |
|---|---|
http://localhost:* | 本地开发服务器(Vite/Webpack dev server 等) |
http://127.0.0.1:* | 本地回环地址变体 |
tauri://localhost | Tauri(桌面应用框架) WebView 环境 |
https://*.opencode.ai | OpenCode 官方 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。本表的目的是:你拿到一个需求后,能快速定位“该用哪类接口、对应哪个场景“。
| # | 分类 | 典型端点 | 对应场景 | 深入参考 |
|---|---|---|---|---|
| 1 | Global | GET /global/health、GET /global/event | 健康检查、全局事件流 | 场景一、场景三 |
| 2 | Project | GET /project、GET /project/current、POST /project/init未确认 | 多项目管理、初始化新项目 | — |
| 3 | Path & VCS | GET /path、GET /vcs、GET /vcs/diff未确认 | 工作目录、Git 状态查询 | — |
| 4 | Instance | POST /instance/dispose | 释放当前 Server 实例 | — |
| 5 | Config | GET|PATCH /config、GET /config/providers | 运行时配置读写、可用 Provider 列表 | — |
| 6 | Provider | GET /provider、GET /provider/auth、POST /provider/{id}/oauth/* | 模型供应商查询、OAuth(开放授权) 流程 | — |
| 7 | Session | GET|POST /session、GET|DELETE|PATCH /session/:id、POST /session/:id/{init,fork,abort,share,summarize,revert} 等(18 端点) | 会话生命周期管理 | 场景一、场景二 |
| 8 | Messages | GET|POST /session/:id/message、GET /session/:id/message/:messageID、POST /session/:id/{prompt_async,command,shell} | 发送消息、获取单条消息、异步提示、执行命令 | 场景一、场景二 |
| 9 | Commands | GET /command | 列出可用 slash 命令 | — |
| 10 | Find & File | GET /find、GET /find/file、GET /find/symbol、GET /file、GET /file/content、GET /file/status | 内容搜索、文件查找、符号查找、文件读写 | — |
| 11 | Tools(实验性) | GET /experimental/tool/ids、GET /experimental/tool | 工具枚举与 JSON Schema 查询 | — |
| 12 | LSP / Formatters / MCP | GET /lsp、GET /formatter、GET|POST /mcp | LSP(语言服务器协议) 状态、格式化器、MCP 服务器管理 | — |
| 13 | TUI | POST /tui/{append-prompt,submit-prompt,open-*,execute-command,show-toast}、GET|POST /tui/control/*(11 端点) | 远程驱动 TUI(IDE 插件用) | — |
| 14 | Auth | PUT /auth/:id、DELETE /auth/:id未确认 | 设置/删除 Provider 凭据 | — |
| 15 | Event | GET /event | 会话级 SSE 事件流 | 场景三 |
| 16 | Doc | GET /doc | OpenAPI 3.1 规范(HTML) | — |
| 17 | Log | POST /log | 写入服务端日志 | — |
| 18 | Agents | GET /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% 集成场景:
- Global + Event —— 健康检查 + 实时事件流,是任何客户端启动后的第一件事
- Session + Messages —— 创建会话、发送消息、获取响应,是核心交互闭环
- 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 类参数 |
| 500 | Server 内部错误 | 查 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 版本间调整。集成时建议:
- 锁定版本——Server 与客户端使用同一版本,CI 中固定 OpenCode 二进制版本
- 运行时校验——客户端启动时调用
/global/health拿到version,与预期版本对比 - 依赖
/doc而非记忆——任何端点签名疑问都查/doc,不要依赖本文或任何外部文档的记忆 - 实验性端点谨慎用——
/experimental/*端点随时可能变更或移除,生产环境避免依赖
→ SDK 客户端深度使用见 agent-sdk.md
本文聚焦“Server 端配置 + 原生 HTTP 规范 + 多语言集成“。如果你在写 TypeScript/JavaScript 应用,需要类型安全、自动重试、E2B 沙箱、CI/CD 集成、Docker 部署、成本监控、安全加固等更深度的客户端能力,请继续阅读:
那篇覆盖了 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 | ~559 | LSP 接口定义、客户端匹配、诊断收集 |
client.ts | ~253 | 单个语言服务器的客户端封装(基于 vscode-jsonrpc) |
server.ts | ~1968 | LSP 服务编排、生命周期管理 |
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
图里要特别注意三件事:
- Agent 不直接调语言服务器。AI 只能通过
tool/lsp.ts这个 Tool 间接访问 LSP 能力,Tool 内部再走lsp.ts暴露的接口。这是 Generator-Evaluator 模式的体现——执行层和验证层分离,AI 不能自己改自己看到的诊断。 - 每个语言服务器是独立子进程。OpenCode 用 stdio 上的 JSON-RPC 和它们通信,崩溃不影响主进程,但也要为启动开销买单。
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 / JavaScript | typescript | .ts .tsx .js .jsx .mjs .cjs .mts .cts | 项目有 typescript 依赖 |
| TypeScript(替代) | deno | 同上 | 有 deno 命令 |
| Lint | eslint | .ts .tsx .js .jsx .mjs .cjs .mts .cts .vue | 项目有 eslint 依赖 |
| Lint(替代) | oxlint | 同 eslint + .vue .astro .svelte | 项目有 oxlint 依赖 |
| Python | pyright | .py .pyi | 已安装 pyright |
| Go | gopls | .go | 有 go 命令 |
| Rust | rust | .rs | 配置键为 rust,命令为 rust-analyzer |
| Java | jdtls | .java | 装了 Java SDK 21+ |
| C / C++ | clangd | .c .cpp .cc .cxx .c++ .h .hpp .hh .hxx .h++ | C/C++ 项目自动安装 |
| Ruby | ruby-lsp | .rb .rake .gemspec .ru | 有 ruby 和 gem |
| Lua | lua-ls | .lua | 自动安装 |
| Bash | bash | .sh .bash .zsh .ksh | 自动安装 |
完整列表见 官方文档。LSP 文件被打开时,OpenCode 会按扩展名匹配服务器并启动——前提是项目满足“启动条件“那一列里的依赖。
语言服务器配置
LSP 通过 opencode.json 的 lsp 字段配置。三种取值:
| 取值 | 含义 |
|---|---|
| 省略 | 所有 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"]
}
}
}
每个服务器条目支持的字段:
| 属性 | 类型 | 说明 |
|---|---|---|
disabled | boolean | 设为 true 禁用该服务器 |
command | string[] | 启动命令(除非只用于禁用,否则必填) |
extensions | string[] | 该服务器处理的文件扩展名 |
env | object | 启动时设置的环境变量 |
initialization | object | initialize 请求里的初始化选项 |
⚠️ 避免自动下载:默认情况下 OpenCode 会自动下载缺失的语言服务器。在离线或受限环境里,设环境变量
OPENCODE_DISABLE_LSP_DOWNLOAD=true关掉这个行为,所有服务器必须由你预先装好。
暴露给 AI 的 10 个 LSP 接口
OpenCode 在 packages/opencode/src/lsp/lsp.ts 的 Interface 中定义了 10 个对外暴露的 LSP 方法。这是 AI 能看到的全部 LSP 能力——任何不在这张表里的 LSP 功能(rename、formatting、codeLens 等)AI 都用不到。
| # | 方法 | 用途 | 输入 |
|---|---|---|---|
| 1 | diagnostics() | 获取所有文件诊断信息(错误/警告/hint) | 无 |
| 2 | hover(input) | 符号悬停信息(类型签名、文档) | LocInput |
| 3 | definition(input) | 跳转到符号定义 | LocInput |
| 4 | references(input) | 查找符号的所有引用 | LocInput |
| 5 | implementation(input) | 查找接口的实现 | LocInput |
| 6 | documentSymbol(uri) | 文档符号树(函数/类/变量列表) | uri: string |
| 7 | workspaceSymbol(query) | 工作区符号搜索 | query: string |
| 8 | prepareCallHierarchy(input) | 准备调用层次 | LocInput |
| 9 | incomingCalls(input) | 谁调用了这个函数 | LocInput |
| 10 | outgoingCalls(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 方法 |
|---|---|
goToDefinition | definition(input) |
findReferences | references(input) |
hover | hover(input) |
documentSymbol | documentSymbol(uri) |
workspaceSymbol | workspaceSymbol(query) |
goToImplementation | implementation(input) |
prepareCallHierarchy | prepareCallHierarchy(input) |
incomingCalls | incomingCalls(input) |
outgoingCalls | outgoingCalls(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 编译错误“任务的内部循环大致是:
diagnostics()拿到错误清单- 对每个错误位置调
hover()看期望类型 - 调
goToDefinition()跳到相关 API 的定义 - 调
findReferences()评估修改影响面 - 改完代码后再调
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 --noEmit、ruff check、go vet 这类 CLI 检查工具,让 Agent 直接跑这些命令往往更可靠——错误信息明确、可重现、不依赖服务器状态。把这些命令写进 AGENTS.md 或 Skill,让 Agent 知道该跑什么。
LSP 适合的场景
| 场景 | LSP 是否推荐 | 理由 |
|---|---|---|
| 大型 TypeScript 项目,类型复杂 | ✅ 推荐 | tsserver 的类型推导比 tsc 报错信息更精准 |
| 单文件 Python 脚本 | ❌ 不推荐 | 启动 pyright 的开销大于收益 |
| Rust 项目 | ✅ 推荐 | rust-analyzer 的借用检查反馈无替代品 |
| 多语言混合仓库 | ⚠️ 谨慎 | 多个语言服务器同时启动,内存压力大 |
| CI/CD 自动化循环 | ❌ 不推荐 | 服务器启动时间不可控,用 CLI 工具更稳定 |
性能调优
如果决定开 LSP,按下面几点调优:
-
只开必要的语言服务器。别一股脑
"lsp": true,按需开:{ "$schema": "https://opencode.ai/config.json", "lsp": { "typescript": {}, "rust": {} } }只显式列你需要的几个,其他保持禁用。
-
离线环境关掉自动下载:
export OPENCODE_DISABLE_LSP_DOWNLOAD=true避免每次启动都尝试联网下载缺失的服务器。
-
监控诊断超时。如果某个语言服务器经常 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.json 的 mcp 字段把外部 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.json 的 mcp 字段下声明,每个 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 子进程):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | 是 | 固定为 "local" |
command | string[] | 是 | 启动 MCP server 的命令及参数,例如 ["npx","-y","@modelcontextprotocol/server-filesystem","."] |
cwd | string | 否 | 子进程工作目录,相对路径基于工作区 |
environment | object | 否 | 注入子进程的环境变量 |
enabled | boolean | 否 | 是否启用,设为 false 可临时禁用而不删除配置 |
timeout | number | 否 | 拉取工具列表的超时(毫秒),默认 5000 |
Remote 类型(HTTP/SSE):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | string | 是 | 固定为 "remote" |
url | string | 是 | MCP server 端点 URL |
headers | object | 否 | 随请求发送的 HTTP 头,支持 {env:VAR} 占位符读取环境变量 |
oauth | object | false | 否 | OAuth 配置对象,或 false 显式关闭自动 OAuth 探测 |
enabled | boolean | 否 | 同上 |
timeout | number | 否 | 同上 |
💡 设计决策:
{env:VAR_NAME}占位符是 OpenCode 推荐的密钥管理方式——配置文件可以提交到 Git,密钥留在 shell 环境。永远不要在headers或environment里硬编码 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_search、my-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
}
}
}
验证步骤:
- 启动 OpenCode 后运行
opencode mcp list,应看到filesystem行ENABLED=true。 - 在 prompt 里测试:
use filesystem to list files in /Users/me/notes。 - 如果工具列表为空,运行
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 显示 expired | opencode mcp auth <server> 重新认证 | refresh token 过期 |
| 工具调用一直 401 | opencode mcp debug <server> | clientId/scope 不匹配,或 server 不支持 Dynamic Client Registration |
| 浏览器没自动打开 | 检查 $BROWSER 环境变量 | 无默认浏览器或 SSH 环境,需手动复制授权 URL |
生态扩展:opencode-mcp 将 OpenCode 暴露为 MCP server
⚠️ 重要声明:
opencode-mcp是社区驱动的第三方项目,不是 OpenCode 官方能力。主仓库为AlaeddineMessadi/opencode-mcp,社区另有 hardened forkMekaretEriker/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 个)
不需要逐一列举,按分类理解能力边界即可:
| 分类 | 数量 | 代表工具 | 用途 |
|---|---|---|---|
| 工作流工具 | 13 | opencode_setup / opencode_ask / opencode_run | 启动会话、提问、运行任务 |
| 会话工具 | 20 | create / list / fork / share | 会话生命周期管理 |
| 消息工具 | 6 | send / execute / shell | 发送消息、执行 shell |
| 文件与搜索 | 6 | read / write / search / symbol_search | 文件操作与符号检索 |
| 配置工具 | 3 | get / set / list | 读写 opencode.json |
| Provider 与认证 | 6 | models / set_key / oauth / status | 模型与凭据管理 |
| TUI 控制 | 9 | focus / input / resize / screenshot | 远程操控 TUI 界面 |
| 系统与监控 | 13 | health / vcs_info / instance_dispose | 健康检查与进程管理 |
| 事件工具 | 1 | events_poll | 长轮询事件流 |
| 项目工具 | 3 | list / get / init | 多项目管理 |
| 全局工具 | 1 | global_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 传输协议,按部署场景选择:
| 传输方式 | 适用场景 | 启动方式 |
|---|---|---|
| stdio | Claude Desktop / Cursor 本地集成 | 默认,command+args 启动 |
| SSE | 需要长连接的 Web 客户端 | 启动时加 --transport sse --port 3001 |
| StreamableHTTP | HTTP 友好的环境、负载均衡 | 启动时加 --transport streamable-http --port 3001 |
主仓库 vs hardened fork
| 维度 | AlaeddineMessadi/opencode-mcp | MekaretEriker/opencode-mcp |
|---|---|---|
| 定位 | 主仓库,功能最新 | Hardened fork,稳定性优先 |
| 适用场景 | 试用新功能、参与贡献 | 生产环境、企业部署 |
| 安全审计 | 社区维护 | 额外的输入校验和速率限制 |
| 推荐度 | 学习/原型 | ★ 生产首选 |
最佳实践
MCP server 选择
- 按需启用,不要全装:每启用一个 MCP server 都会向上下文注入工具描述,GitHub MCP 这种工具数多的 server 单独就能占掉数千 token。建议当前工作流用到哪个装哪个,不用的设为
enabled: false。 - 优先选官方维护的 server:
@modelcontextprotocol/server-*系列由 MCP 协议团队维护,质量有保障。社区 server 使用前查看最近提交时间和 issue 响应速度。 - Local 优先于 Remote:能本地跑的 server 不要走远程 HTTP——少一次网络往返、少一份认证负担。PostgreSQL、SQLite、filesystem 这类工具天然适合 local。
超时配置
- 默认
timeout: 5000(5 秒)适合大多数轻量 server。 - 工具调用本身耗时长的 server(如 Puppeteer 浏览器自动化)建议提到
15000或更高。 - 不要无脑调到 60000+——如果 server 5 秒拉不到工具列表,多半是配置错了或网络断了,等更久只会拖慢 OpenCode 启动。
安全考虑
{env:VAR}占位符强制使用:API key、OAuth secret 一律走环境变量,配置文件可以放心提交 Git。在 CI 中通过 secret 注入环境变量。- Local server 的
command要固定版本:npx -y foo@latest每次拉最新版可能引入供应链风险,建议写成npx -y foo@1.2.3。 - OAuth token 存储路径:
~/.local/share/opencode/mcp-auth.json是明文 JSON,文件权限应为600。共享机器上注意隔离用户目录。 - Per-agent 隔离敏感工具:财务/数据库类 MCP server 全局禁用,只在专门的
adminagent 上启用,避免普通对话误触。
性能优化
- 预热连接: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(模型上下文协议) 服务器
相关章节
- → OpenCode 内置能力 — Server 接口在 OpenCode 整体能力版图中的位置
- → OpenCode SDK:编程式 Agent 开发 — 从 SDK 客户端视角深度使用这些接口
- → OpenCode 生态参考 — MCP 配置速查、第三方生态项目
- → MCP(模型上下文协议) 服务器 — 如何开发自定义 MCP server