17.3 实验三:构建一个 MCP Server

模型: claude-opus-4-6 (anthropic/claude-opus-4-6) 生成日期: 2026-02-18


上两个实验中我们构建了一个具备对话和工具调用能力的 CLI 工具。但那些工具都是硬编码在程序内部的——如果我们想让工具可以独立开发、独立部署、被多个 AI 应用共享呢?这就是 MCP(Model Context Protocol)要解决的问题。

在第 8 章我们分析了 OpenCode 的 MCP Client 实现(mcp/index.ts),它使用 @modelcontextprotocol/sdkClient 类来连接 MCP Server。本节我们换一个角度——自己构建一个 MCP Server,让 OpenCode 能够连接并使用它。

17.3.1 目标

使用 @modelcontextprotocol/sdk 创建一个自定义的 MCP Server,实现以下功能:

  1. 通过 Stdio(标准输入输出)传输协议与 Client 通信

  2. 提供一个实用的工具:GitHub 仓库信息查询

  3. 将 MCP Server 配置到 OpenCode 中进行实际测试

衍生解释——为什么 MCP 用 Stdio 传输?

MCP 支持三种传输方式:Stdio(标准输入输出)、SSE(Server-Sent Events)和 StreamableHTTP。其中 Stdio 是最常见的本地 MCP Server 传输方式。它的工作原理非常简单:

  1. Client(如 OpenCode)以子进程方式启动 Server

  2. Client 通过子进程的 stdin 发送 JSON-RPC 请求

  3. Server 通过 stdout 返回 JSON-RPC 响应

这种设计的优势在于:不需要网络端口、不需要 HTTP 服务器、不需要处理认证——只要能启动进程就能通信。对于本地工具而言,这是最简单可靠的方案。

在 OpenCode 源码中,StdioClientTransport 正是这样实现的:

const transport = new StdioClientTransport({
  command: cmd,
  args,
  env: { ...process.env, ...mcp.environment },
})

17.3.2 实现步骤

步骤一:初始化项目

@modelcontextprotocol/sdk 是 MCP 官方 SDK,同时提供 Client 和 Server 两端的实现。

步骤二:创建 MCP Server

创建 server.ts

注意几个关键点:

  1. McpServer:这是 MCP SDK 提供的高级 API,比直接使用底层的 Server 类更简便。它内置了工具注册、参数校验等功能。

  2. server.tool() 方法:注册工具的签名是 (name, description, schema, handler),其中 schema 使用 Zod 定义——与 OpenCode 内部的 Tool.define() 风格一致。

  3. 返回值格式:MCP 工具的返回值必须是 { content: [{ type: "text", text: string }] } 格式——这是 MCP 协议规定的标准结构。

  4. 使用 console.error 而非 console.log:因为 stdout 被 Stdio 传输占用,任何日志都必须写到 stderr

步骤三:本地测试

先确保 Server 可以正常启动:

步骤四:配置到 OpenCode

编辑项目根目录的 opencode.json(或 ~/.config/opencode/opencode.json):

这与 OpenCode 源码中 config/config.ts 定义的 McpLocal Schema 完全对应:

配置完成后,重启 OpenCode,你就可以在对话中使用这两个工具了:

Last updated