ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

手把手写一个自己的 MCP 并接入 Cline:AI 助手从此“指哪打哪“

手把手写一个自己的 MCP 并接入 Cline:AI 助手从此“指哪打哪“

手把手写一个自己的 MCP 并接入 Cline:AI 助手从此"指哪打哪"

【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline

AI 编程助手在默认情况下只能使用内置的工具集:读写文件、执行终端命令、浏览网页。但一旦你遇到"读取天气接口""查询公司内部数据库""操作自研 CLI 工具"这类需求,通用模型就会暴露局限——要么不会用你的私有 API,要么只能用自然语言"猜"着编。Model Context Protocol(MCP)正是为解决这一堵点而生的开放协议:它把外部能力封装成标准化的"工具服务器",让 Cline 这类 AI 助手通过统一的 JSON-RPC 通道发现并调用这些工具,相当于给大模型装上了"指哪打哪"的精确抓手。

本文将以 Cline 仓库的真实源码为线索,带你先理解 MCP 在 Cline 中的接入方式,再从零手写一个自定义 MCP Server,最后接入 Cline 实测它完成一个原本做不到的任务。

MCP 是什么,以及 Cline 为什么需要它

MCP 全称 Model Context Protocol,核心思想非常朴素:把"工具"从"模型"中解耦。模型不需要知道某个数据源的具体 API 怎么调,它只需要知道 MCP Server 暴露了哪些工具(tools/list)、每个工具接收什么参数(input schema)、以及怎么调用(tools/call)。剩下的传输细节,全部由 MCP 协议层完成。

在 Cline 的源码中,这一点体现得淋漓尽致。sdk/packages/core/src/extensions/mcp/types.ts中定义了三种受支持的传输方式:

export type McpServerTransportConfig = | McpStdioTransportConfig // 本地进程:type: "stdio",command + args | McpSseTransportConfig // 远程:type: "sse",url + headers | McpStreamableHttpTransportConfig // 远程(推荐):type: "streamableHttp",url + headers
  • STDIO:通过标准输入输出与本机子进程通信,延迟低、配置简单,适合本地工具;
  • SSE / Streamable HTTP:通过 HTTP 端点访问远程托管服务,适合多客户端共享。

对应地,apps/vscode/src/shared/mcp.ts中定义了 Cline 视角下的 MCP 生态:一个 McpServer 可以暴露tools(工具)、resources(资源)、resourceTemplates(资源模板)和prompts(提示词模板)。其中工具是最常用的一类——Cline 会把 MCP Server 暴露的每个工具注入为它自己的一个"原生工具",模型就可以在对话中直接调用。

接入方式一:在配置文件中声明

Cline 统一使用cline_mcp_settings.json管理 MCP 服务器。IDE 扩展(VS Code 等)和 CLI 共用同一份配置,文件默认位于~/.cline/data/settings/下,也可以通过环境变量CLINE_MCP_SETTINGS_PATH覆盖路径(见apps/vscode/src/hosts/vscode/mcp-settings-legacy-migration.ts中的getSharedMcpSettingsPath)。

以本地 STDIO 服务器为例,配置长这样(来自官方文档 docs/mcp/mcp-overview.mdx):

{ "mcpServers": { "my-tools": { "command": "node", "args": ["/path/to/my-server.js"], "env": { "API_KEY": "your_api_key" }, "disabled": false, "autoApprove": [] } } }

如果是远程服务器,则改为:

{ "mcpServers": { "remote-tools": { "type": "streamableHttp", "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer your-token" }, "disabled": false, "autoApprove": [] } } }

注意type字段:对于本地服务器,没有type字段时默认按stdio处理;对于远程服务器,省略type会默认回退到旧版sse传输,建议显式写"type": "streamableHttp"。每个服务器还支持timeout字段(单位是秒),来自sdk/packages/shared/src/mcp.ts的契约:默认 60 秒,最小 1 秒、最大 3600 秒,超出范围会被钳制,而不是静默变成"几小时"——这个细节对排查超时问题很有帮助。

在 IDE 中,你可以直接点击 Cline 面板顶部的 MCP Servers 图标,在 Configure 标签页里打开这份 JSON 手动编辑;也可以走 Remote Servers 标签页填写 URL 和传输类型,由界面生成配置。

接入方式二:用 CLI 命令一行安装

如果你在用 Cline CLI(apps/cli),安装 MCP 服务器甚至不用手写 JSON。官方文档给出了两条非交互式命令:

cline mcp add files --yes -- npx -y @modelcontextprotocol/server-filesystem . cline mcp add docs https://example.com/mcp --transport http --header "Authorization: Bearer TOKEN" --yes

第一条命令通过--把后面的内容原样交给npx执行,安装一个由 npm 包提供的本地 STDIO 服务器;第二条则注册一个带认证头的远程服务器。--yes表示跳过交互向导直接写入配置。

这条命令的完整实现位于apps/cli/src/commands/mcp.ts,其核心逻辑在sdk/packages/core/src/services/mcp-install.ts中:buildMcpInstallTransport负责解析传输类型和参数,installMcpServer则调用updateMcpSettingsFileSync把新服务器合并进mcpServers字段。整个流程被封装得足够健壮——它会校验 URL 必须是 http/https、校验 header 格式、甚至会对形如<token>的未填占位符给出警告。不传--yes时,cline mcp add会打开预填好的交互式向导,引导你完成配置。

从零手写一个 MCP Server

理解了接入方式后,我们开始写自己的 MCP Server。其实 MCP 协议本身并不神秘:STDIO 传输的 MCP Server 就是一个通过 stdin 读取 JSON-RPC 请求、通过 stdout 返回 JSON-RPC 响应的普通进程。

Cline 仓库的测试代码sdk/packages/core/src/extensions/mcp/client.test.ts里就内置了一个仅 40 多行的极简 MCP Server 参考实现,堪称最好的教学模板。它逐行读取 stdin,按换行符切分 JSON 消息,只响应三类方法:initialize、tools/list、tools/call。我们据此写一个"天气查询"服务器:

#!/usr/bin/env node // weather-mcp-server.js —— 一个手写的 MCP Server const fs = require("node:fs"); let buffer = ""; // 模拟天气数据源 const WEATHER = { "beijing": "晴,25℃", "shanghai": "多云,28℃", "shenzhen": "雷阵雨,30℃", }; process.stdin.on("data", (chunk) => { buffer += chunk.toString("utf8"); let idx; while ((idx = buffer.indexOf("\n")) >= 0) { const line = buffer.slice(0, idx).trim(); buffer = buffer.slice(idx + 1); if (!line) continue; let msg; try { msg = JSON.parse(line); } catch { continue; } // 忽略通知类消息 if (msg.id === undefined || !msg.method || msg.method.startsWith("notifications/")) continue; let result; if (msg.method === "initialize") { result = { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: "weather-server", version: "1.0.0" }, }; } else if (msg.method === "tools/list") { result = { tools: [{ name: "get_weather", description: "查询指定城市的实时天气", inputSchema: { type: "object", properties: { city: { type: "string", description: "城市名,如 beijing" }, }, required: ["city"], }, }] }; } else if (msg.method === "tools/call") { const city = msg.params?.arguments?.city; result = { content: [{ type: "text", text: WEATHER[city] ?? `未收录城市:${city}` }], }; } else { result = {}; } process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id: msg.id, result }) + "\n"); } });

几个关键点值得注意:

  1. 协议版本要对齐:Cline 的 MCP 客户端固定使用协议版本"2024-11-05"(定义在sdk/packages/core/src/extensions/mcp/client.ts的MCP_PROTOCOL_VERSION)。你的initialize响应必须回传该版本,握手才能成功。
  2. tools/list返回的inputSchema就是"说明书":Cline 会把这份 JSON Schema 注入给模型。schema 写得多清楚,模型就能多准确地调用工具。
  3. tools/call的返回值采用content数组:里面可以放文本(type: "text"),也可以放图片、音频、资源链接等结构化内容,类型定义见apps/vscode/src/shared/mcp.ts的McpToolCallResponse。

如果你不想手工维护 JSON-RPC 协议,也可以使用官方 MCP SDK(@modelcontextprotocol/sdk),用McpServer+StdioServerTransport几行代码完成同样的事情。理解上面的裸协议实现,反而能让你在排错时更从容。

接入 Cline 并实测

把上面这个文件保存后,在cline_mcp_settings.json的mcpServers中加入:

{ "mcpServers": { "weather": { "command": "node", "args": ["/path/to/weather-mcp-server.js"] } } }

保存配置后,Cline 会自动尝试连接(或手动重启服务器)。此时在工具列表里会看到它暴露的get_weather工具。Cline 对 MCP 工具有一套统一的命名规则,见sdk/packages/core/src/extensions/mcp/name-transform.ts:默认格式为服务器名__工具名,例如weather__get_weather。该实现还会把非法字符替换为下划线,并为了兼容 OpenAI 系模型的函数名上限(64 字符),超长名称会用 SHA-1 哈希截断处理——所以你在日志里偶尔看到带_xxxxxxxx后缀的工具名,不是 bug,是命名保护。

接下来向 Cline 提问:"帮我查一下深圳今天的天气"。此时模型会:

  1. 从工具清单中识别出weather__get_weather及其参数要求;
  2. 调用该工具,传入{ city: "shenzhen" };
  3. Cline 通过 STDIO 通道把tools/call请求发给你的服务器进程;
  4. 服务器返回"雷阵雨,30℃",模型据此组织成自然语言回复。

如果你的数据源换成公司内部 API、自研命令行工具或者数据库查询接口,流程完全一样——这就是"指哪打哪"的本质:模型不再需要"学过"你的私有系统,只需要一份准确的工具清单。

关于调用权限,还有一个值得注意的细节:MCP 工具的autoApprove配置决定了 Cline 在调用该工具时是否需要人工确认(见apps/vscode/webview-ui/src/components/mcp/configuration/tabs/installed/server-row/McpToolRow.tsx中的开关逻辑)。对只读、低风险的工具可以开启自动批准,让 agent 全速执行;对会写数据、执行系统操作的工具,建议保留人工确认。官方文档docs/mcp/mcp-overview.mdx的 Security 章节也给出了同样的原则:只安装可信的服务器、密钥走环境变量、autoApprove只开给安全工具。

小结

从协议层面看,MCP 的设计并不复杂:一次initialize握手、一次tools/list发现、若干次tools/call调用,stdin/stdout 之间传递的是一行行 JSON-RPC 消息。但正是这套"小而标准"的协议,把 AI 助手从"只会用内置工具"解放成了"能力可无限扩展的智能体"。

通过 Cline 的三层支持——统一的cline_mcp_settings.json配置、CLI 的cline mcp add一键安装、以及运行时对tools/list/tools/call的完整封装——你不需要理解底层实现就能接入现成的 MCP 生态;而一旦你掌握了手写 Server 的能力,任何私有系统都能在一小时内变成 AI 助手的"手"和"眼"。

下次再遇到"Cline 做不到"的任务,不妨先想想:是模型不会,还是我还没给它装上对应的"手"。

【免费下载链接】clineAutonomous coding agent as an SDK, IDE extension, or CLI assistant.项目地址: https://gitcode.com/GitHub_Trending/cl/cline

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

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