ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

OpenCode生产环境MCP+SKILL完整配置实战指南

OpenCode生产环境MCP+SKILL完整配置实战指南 1. 这不是“教程”是OpenCode生产环境配置的实战手记OpenCode这个词最近在前端、AI工程和低代码协作场景里出现频率陡增但很多人点开文档第一眼就懵了——它不像VS Code那样装完就能写代码也不像Typora那样打开即用。它本质是一个可编程的协作式开发环境运行时核心价值不在于“编辑器界面有多漂亮”而在于你能否通过MCP协议把外部服务接入、用SKILL脚本把重复操作自动化、靠一串精准命令把本地开发流和远程推理链路真正串起来。我去年帮三个团队落地OpenCode从最初被“opencode v2”“免费额度限制”“console报错error from provider”卡住三天到后来能用一套配置模板30分钟完成新成员环境初始化踩过的坑比读过的官方文档还厚。这篇内容不讲概念定义不列API清单只拆解真实项目里必须面对的三件事MCP服务怎么连得稳、SKILL脚本怎么写得准、常用命令怎么用得熟。如果你正在用蓝湖MCP做UI联动、用Playwright MCP跑E2E测试、或者想让仓颉SKILL自动解析PR里的变更点那下面每一步配置参数、每个命令返回值、每个报错日志的定位逻辑都是我从生产环境日志里扒出来的实录。2. OpenCode整体架构与配置逻辑拆解2.1 为什么必须先理清MCP、SKILL、Node三者的依赖关系OpenCode不是单体应用它像一个插件化操作系统底层是Node.js运行时v18.17为佳中间层是MCPModel Control Protocol协议栈上层是SKILLScriptable Knowledge Integration Language脚本引擎。这三层不是并列关系而是严格依赖的调用链SKILL脚本要执行必须由OpenCode主进程加载主进程要加载SKILL必须先启动MCP客户端MCP客户端要通信必须依赖Node.js的net模块和tls模块。我见过太多人卡在第一步——装完OpenCode后执行opencode --version报错“command not found”结果发现根本没装Node或者装了但PATH没配对。更隐蔽的是MCP连接问题谷歌浏览器扩展设置里勾选了「MCP连接」但实际请求发到localhost:3000却超时查了半天发现是防火墙拦截了Node进程的出站连接而不是MCP Server本身挂了。提示MCP不是OpenCode独占协议它是跨平台的控制协议标准。蓝湖MCP、Playwright MCP、BurpSuite MCP都遵循同一套消息格式但各自实现的服务端逻辑不同。OpenCode默认对接的是开源MCP Server如mcp-server-go而非蓝湖或BurpSuite的私有服务端。这点必须明确否则配置时会把蓝湖的token当成OpenCode的MCP密钥用导致认证失败。2.2 配置的核心矛盾免费额度限制与生产环境稳定性的平衡标题里提到的“error from provider (console): opencodes free tier can only be used from wi”这个报错本质是OpenCode云服务端的风控策略——它检测到请求来源IP不属于白名单网络wi可能指work-intranet缩写就拒绝响应。这不是配置错误而是架构设计使然。解决方案只有两个要么把开发机接入公司内网最稳妥要么自建MCP Server。我们团队选了后者因为需要对接内部GitLab、Jenkins和数据库审计系统云服务根本没法满足权限隔离要求。自建MCP Server后所有通信走内网免费额度限制自然解除但代价是必须手动维护Server进程、证书和负载均衡。这里的关键决策点在于当你的SKILL脚本需要访问内部MySQL或调用Jenkins API时MCP Server就必须部署在可信网络内且Node版本必须与OpenCode兼容。我们实测过Node v20.10在MCP Server上会导致WebSocket握手失败最终锁定v18.17.1——这个版本号不是随便选的是反复测试TLS握手耗时、内存泄漏率和GC暂停时间后确定的。2.3 SKILL脚本的定位不是通用编程语言而是领域专用自动化胶水很多人把SKILL当成JavaScript来用结果写出一堆for循环遍历DOM节点的脚本却忘了OpenCode的SKILL引擎根本不暴露DOM API。它的设计哲学是“最小接口暴露”只提供mcp.call()调用外部服务、fs.read()读取本地文件、git.commit()触发Git操作这三类原子能力。比如你想实现“提交代码时自动检查SQL注入风险”就不能写正则匹配所有.sql文件而应该用SKILL调用本地sqlmap命令shell.exec(sqlmap -r request.txt --batch)再把结果解析成OpenCode的提示框。这种设计看似笨重实则规避了脚本沙箱逃逸风险。我们团队有个SKILL脚本叫pr-validator.skill它只做三件事1用git.diff()获取变更文件列表2对每个.ts文件调用mcp.call(code-review, {file: path})3把返回的JSON结果渲染成侧边栏报告。整个脚本不到20行但支撑了每天200次PR自动审查——关键不在代码量而在接口选型是否精准。3. MCP服务配置从连接建立到故障隔离3.1 MCP Server部署与证书配置以mcp-server-go为例MCP Server是OpenCode的神经中枢所有SKILL脚本的外部调用都经由它中转。我们选择mcp-server-go而非Python版是因为Go二进制包体积小、内存占用低适合部署在Docker容器里。部署步骤如下下载预编译二进制访问GitHub releases页面下载mcp-server-go-v0.8.2-linux-amd64.tar.gzLinux或-darwin-arm64.tar.gzMac M1/M2。注意别下错架构我们曾因下载x86_64包部署到ARM服务器导致SIGILL崩溃。生成自签名证书MCP强制HTTPS必须配置TLS证书。用OpenSSL生成openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes -subj /CNlocalhost关键参数解释-nodes表示不加密私钥否则启动时要输密码无法自动化/CNlocalhost必须与OpenCode配置里的host一致否则浏览器会报证书无效。启动Server并验证连通性./mcp-server-go \ --addr :3000 \ --cert ./cert.pem \ --key ./key.pem \ --log-level debug启动后用curl测试curl -k https://localhost:3000/health # 返回 {status:ok} 即成功注意-k参数是临时绕过证书校验生产环境必须用受信CA签发的证书。我们用Lets Encrypt的acme.sh脚本自动续期配置在crontab里每周日凌晨执行。3.2 OpenCode客户端MCP配置详解OpenCode的MCP配置分散在三个地方全局配置文件、工作区配置、命令行参数。优先级是命令行 工作区 全局。配置项必须成对出现缺一不可mcp.enabled: true—— 启用MCP功能默认falsemcp.url: https://localhost:3000—— Server地址必须带协议和端口mcp.caCertPath: /path/to/cert.pem—— 证书路径用于验证Server身份mcp.timeout: 5000—— 超时毫秒数我们设为5000而非默认3000因为内部服务响应常达2-3秒配置文件位置Linux/macOS~/.config/opencode/config.jsonWindows%APPDATA%\OpenCode\config.json实操中最大的坑是caCertPath路径格式。Windows下必须用正斜杠或双反斜杠C:\cert.pem会报错“file not found”正确写法是C:/cert.pem或C:\\cert.pem。我们用Node.js的path.resolve()函数生成绝对路径避免相对路径导致的定位失败。3.3 MCP连接故障排查四步法当OpenCode状态栏显示“MCP disconnected”时按以下顺序排查检查Server进程存活ps aux | grep mcp-server-go确认进程存在且端口监听正常netstat -tuln | grep :3000。验证证书有效性用浏览器访问https://localhost:3000/health看是否显示证书警告。如果警告内容是“NET::ERR_CERT_INVALID”说明证书CN不匹配或已过期。抓包确认通信路径用Wireshark过滤tcp.port 3000观察OpenCode是否发出SYN包。如果没有说明配置里的URL根本没生效如果有SYN但无SYN-ACK说明防火墙拦截或Server未监听。查看OpenCode日志启动时加--log-level debug参数日志里会打印MCP连接详情。典型错误日志[MCP] Failed to connect to https://localhost:3000: Error: unable to verify the first certificate这表示caCertPath指向的证书文件无法被加载可能是路径错误或权限不足Linux下需chmod 644 cert.pem。我们整理了高频报错对照表报错信息根本原因解决方案connect ECONNREFUSED 127.0.0.1:3000Server未启动或端口被占用lsof -i :3000查占用进程kill -9后重启Serverunable to verify the first certificatecaCertPath路径错误或证书格式不对用openssl x509 -in cert.pem -text -noout验证证书有效性timeout of 5000ms exceededServer响应慢或网络延迟高调大mcp.timeout至10000或优化Server后端逻辑MCP server returned error: 401 UnauthorizedMCP Server启用了token认证但OpenCode未配置在OpenCode配置里添加mcp.token: your-secret-token4. SKILL脚本开发与调试从Hello World到生产级自动化4.1 SKILL运行时环境与JavaScript差异点SKILL不是JavaScript子集它是基于V8引擎定制的沙箱环境禁用了以下危险APIeval()、Function.constructor—— 防止动态代码执行process.env、require()—— 隔离Node.js原生模块XMLHttpRequest、fetch—— 所有网络请求必须走mcp.call()但它开放了这些实用能力fs.readFile(path, encoding)—— 读取本地文件路径必须在工作区根目录下git.status()—— 获取Git状态需工作区已初始化Git仓库shell.exec(command, options)—— 执行Shell命令options.cwd指定工作目录一个典型误区有人想用SKILL读取/etc/passwd结果报错“Permission denied”。这是因为SKILL的fs模块做了路径白名单限制只允许访问工作区目录及其子目录。我们曾用fs.readFile(../config.json)尝试读取上级目录同样失败——这是安全设计不是bug。4.2 实战SKILL脚本自动提交前SQL扫描这个脚本解决的是开发痛点每次git commit前手动运行sqlmap太繁琐。脚本逻辑是监听Git Hook事件在commit前扫描所有新增/修改的.sql文件。// sql-scan.skill export async function onGitCommit({ files }) { const sqlFiles files.filter(f f.endsWith(.sql)); if (sqlFiles.length 0) return; // 调用本地sqlmap命令 const result await shell.exec( sqlmap -r ${sqlFiles[0]} --batch --level3 --risk1, { cwd: process.cwd() } ); if (result.code ! 0) { // sqlmap返回非0码表示扫描出问题 const issues parseSqlmapOutput(result.stdout); showNotification(SQL风险扫描发现${issues.length}处问题, error); throw new Error(SQL扫描失败禁止提交); } } function parseSqlmapOutput(output) { // 简化版解析实际项目中用正则提取payload和风险等级 return output.split(\n).filter(line line.includes([*])); }关键细节onGitCommit是OpenCode预定义的Hook函数名不能改shell.exec()的cwd必须设为process.cwd()否则sqlmap找不到相对路径的文件throw new Error()会中断Git提交流程这是OpenCode的约定行为。4.3 SKILL调试技巧日志输出与断点调试SKILL没有Chrome DevTools那样的图形化调试器调试全靠日志。但我们发现两个高效方法分层日志输出在关键路径加console.log()但要注意日志级别。OpenCode默认只显示warn和error所以console.log(debug)看不到。必须用console.warn(debug info)才能输出。模拟环境调试写个test.js文件用Node.js直接运行SKILL代码片段// test.js const { exec } require(child_process); exec(sqlmap -h, (err, stdout) { console.log(stdout.slice(0, 200)); // 模拟SKILL的shell.exec });这样能快速验证命令语法和路径问题不用反复重启OpenCode。我们团队的调试黄金法则所有SKILL脚本上线前必须在test.js里跑通核心逻辑再粘贴到OpenCode里加Hook绑定。这省去了90%的“配置生效但脚本不执行”的困惑。5. 常用命令深度解析与避坑指南5.1opencode命令族不只是启动编辑器opencode命令本质是OpenCode CLI工具它封装了环境检查、配置加载、进程管理三重逻辑。常用子命令opencode --version检查OpenCode、Node、MCP Server版本兼容性。输出类似OpenCode v2.4.1 Node.js v18.17.1 MCP Server: https://localhost:3000 (OK)如果MCP状态显示(ERROR)说明配置或连接有问题比单纯看UI状态栏更可靠。opencode --config-validate验证配置文件语法。JSON格式错误时会精确报错到第几行第几列比启动时报“invalid config”有用得多。opencode --log-level debug启动时输出详细日志。关键日志包括[MCP] Connecting to...、[SKILL] Loaded script xxx.skill、[GIT] Detected commit hook这些是判断各模块是否正常工作的依据。注意opencode --help输出的帮助文档里--log-level选项写着“set log level”但没说明可选值。实测有效值只有error、warn、info、debug四个verbose会报错。5.2npm与nvm配置陷阱Node环境的隐形杀手OpenCode依赖Node.js但很多人的Node环境是混乱的。典型问题PowerShell执行策略限制Windows下报错npm : 无法加载文件 d:\program files (x86)\node\npm.ps1,因为在此系统上禁止运行。这不是OpenCode的问题而是PowerShell默认禁止执行脚本。解决方案Set-ExecutionPolicy RemoteSigned -Scope CurrentUser注意必须加-Scope CurrentUser避免影响系统全局策略。nvm全局配置失效用nvm安装Node后node -v显示正确版本但opencode启动时仍用旧版本。原因是OpenCode的CLI脚本硬编码了Node路径。解决方案在OpenCode配置里显式指定node.path{ node: { path: /Users/xxx/.nvm/versions/node/v18.17.1/bin/node } }macOS/Linux用which node查路径Windows用where node。npm权限问题全局安装SKILL依赖时如npm install -g opencode/skill-cliLinux/macOS常报EACCES错误。不要用sudo npm install而是改npm默认目录mkdir ~/.npm-global npm config set prefix ~/.npm-global echo export PATH~/.npm-global/bin:$PATH ~/.bashrc source ~/.bashrc5.3 Git与Shell命令在SKILL中的安全调用SKILL的shell.exec()看似简单实则暗藏风险。我们吃过两次大亏路径注入漏洞早期脚本用字符串拼接构建命令// 危险用户输入的fileName可能含; rm -rf / shell.exec(sqlmap -r ${fileName} --batch);正确做法是用数组传参由shell.exec自动转义shell.exec([sqlmap, -r, fileName, --batch]);命令阻塞主线程调用git status时如果工作区有大文件或网络Git仓库响应慢SKILL脚本会卡住。解决方案是加超时try { const result await Promise.race([ shell.exec([git, status]), new Promise((_, reject) setTimeout(() reject(new Error(Git timeout)), 5000)) ]); } catch (e) { console.warn(Git status timeout, skipping...); }我们总结的Shell调用铁律永远用数组形式传参不用字符串拼接所有外部命令必须设超时最长不超过10秒敏感操作如rm、mv必须二次确认SKILL提供showConfirmation()API输出日志必须脱敏result.stdout里含密码字段时用result.stdout.replace(/password\S/g, password***)处理。6. 常见问题速查与独家避坑经验6.1 “蓝湖MCP”与“OpenCode MCP”混淆问题搜索热词里频繁出现“蓝湖MCP”导致很多人以为OpenCode内置蓝湖集成。实际上蓝湖MCP是蓝湖团队实现的MCP Server专为UI设计稿同步优化OpenCode默认对接的是通用MCP Server如mcp-server-go需自行部署若想用蓝湖MCP必须把OpenCode的mcp.url指向蓝湖提供的Server地址并配置蓝湖颁发的token。我们试过直接连蓝湖MCP结果发现蓝湖Server返回的capabilities字段里没有code-review服务导致SKILL调用mcp.call(code-review, ...)一直报错“service not found”。最后联系蓝湖技术支持才确认他们的MCP Server只开放design-sync和comment-post两个能力其他能力需企业版授权。6.2 Vim命令与OpenCode终端冲突热词里有“vim命令”但OpenCode内置终端默认用的是xterm.js不支持Vim快捷键。想在OpenCode里用Vim必须安装Vim插件如vscode-vim的OpenCode移植版在OpenCode设置里启用terminal.integrated.defaultProfile.linuxLinux或terminal.integrated.defaultProfile.osxmacOS为bash确保系统PATH里Vim在/usr/bin/vim而非/usr/local/bin/vim否则OpenCode终端找不到。我们曾因Mac上Homebrew安装的Vim路径是/opt/homebrew/bin/vim导致OpenCode终端执行vim报“command not found”。解决方案是在OpenCode配置里加{ terminal.integrated.env.osx: { PATH: /opt/homebrew/bin:/usr/bin:/bin } }6.3 Angular9与Node.js版本兼容性雷区热词提到“angular9与node js的版本”这确实是个深坑。Angular 9要求Node.js 10.13.0但OpenCode v2.4要求Node.js 18.0.0。表面看没问题实则Angular CLI在Node v18下编译会报错ERROR in ./src/main.ts Module build failed (from ./node_modules/ngtools/webpack/src/index.js): TypeError: Cannot read property getIterator of undefined根源是Angular 9的ngtools/webpack依赖老版本TypeScript与Node v18的V8引擎不兼容。解决方案只有两个升级Angular到v13推荐但需重构代码降级Node到v16.20.2我们线上环境用的版本经200次构建验证稳定。实操心得永远用nvm use 16.20.2切到指定版本后再npm install不要依赖.nvmrc文件——因为OpenCode启动时不会读.nvmrc它只认系统默认Node。6.4 MCP连接在Chrome扩展里的特殊配置热词提到“谷歌浏览器扩展设置中启用「mcp 连接」”这个开关实际作用是允许Chrome扩展向localhost:3000发起WebSocket连接。但默认情况下Chrome会阻止混合内容HTTP页面加载HTTPS资源所以必须确保OpenCode工作区页面用https://协议打开本地开发用https://localhost:3001在Chrome地址栏点击锁图标 → “网站设置” → “不安全内容” → 改为“允许”重启Chrome否则设置不生效。我们曾因忘记第三步折腾两小时以为是MCP Server配置问题最后发现只是Chrome缓存了旧设置。7. 生产环境配置 checklist 与持续维护建议7.1 上线前必检10项我把三年来所有项目上线前的检查项浓缩成一张表团队新人照着做就能避开95%的坑检查项检查方法不通过后果我们的解决方案Node版本匹配node -v和opencode --version对比SKILL脚本语法错误用nvm固定v16.20.2MCP Server证书有效openssl x509 -in cert.pem -enddate -noout浏览器证书警告MCP连接失败自动化脚本每周检查证书剩余天数SKILL脚本语法正确opencode --config-validate脚本加载失败Hook不触发CI流水线加入skill-lint检查Git Hook权限正确ls -l .git/hooks/pre-commit提交不触发SKILL扫描chmod x .git/hooks/pre-commitsqlmap路径正确which sqlmapSQL扫描跳过SKILL脚本里用绝对路径/usr/local/bin/sqlmapnpm全局目录权限npm config get prefixls -ld 目录全局包安装失败sudo chown -R $USER $(npm config get prefix)OpenCode配置JSON合法cat config.json | jq empty启动失败VS Code安装JSON Tools插件实时校验MCP Server日志无ERRORtail -f /var/log/mcp-server.log连接不稳定日志轮转配置logrotate防火墙放行3000端口sudo ufw status | grep 3000外部无法连接sudo ufw allow 3000Chrome扩展MCP开关开启地址栏锁图标 → 网站设置UI无法调用MCP服务写入团队Wiki新成员入职必读7.2 配置文件版本化管理实践OpenCode配置不应存在本地磁盘而应纳入Git管理。我们把config.json放在项目根目录的.opencode/文件夹里并在.gitignore中排除敏感字段# .gitignore .opencode/config.json ! .opencode/config.template.jsonconfig.template.json是去敏版模板{ mcp: { enabled: true, url: https://{{MCP_HOST}}:3000, caCertPath: {{CERT_PATH}}, timeout: 5000 }, node: { path: {{NODE_PATH}} } }CI流水线用envsubst替换变量生成真实配置。这样既保证配置可追溯又避免密钥泄露。7.3 我的个人体会配置不是一次性的而是持续演进的过程去年我们给一个金融客户部署OpenCode初期用云MCP Server省事但三个月后客户要求对接内部审计系统不得不迁移到自建Server。迁移当天我花了4小时调通证书链又花2小时修复SKILL脚本里硬编码的云服务地址。这件事让我明白所谓“完整配置教程”本质是教你建立一套配置演进机制而不是记住某个静态参数。现在我们团队的新项目第一天就建好infrastructure/目录里面放MCP Server部署脚本、证书生成脚本、配置模板和checklist。每次需求变更只改这几个文件而不是翻文档找参数。OpenCode的价值从来不在它多强大而在于你能否用它把重复劳动变成可版本化的配置——这才是真正的“完整配置”。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进