MCP 实战:从最小 Go Server 到远程服务、发布分发与 Skill 协同
本文整理自一次关于 Model Context Protocol(MCP)的完整讨论,覆盖 MCP 的核心概念、Go 实现、对外发布、自然语言注册、远程服务化,以及 MCP 与 Agent Skill 的关系与选型。
1. MCP 是什么:AI 应用的"USB-C 端口"
Model Context Protocol(MCP) 是由 Anthropic 于 2024 年 11 月推出的开放协议,用于标准化大语言模型与外部数据源、工具之间的集成方式。它常被类比为"AI 应用的 USB-C 端口":就像 USB-C 统一了设备与配件的连接方式,MCP 统一了 AI 模型与外部服务的对接标准。
MCP 基于 JSON-RPC 2.0,定义了三层架构:
- Hosts:发起连接的 LLM 应用,如 AI IDE、聊天界面。
- Clients:Host 内部的连接器,每个 Client 维护一个独立会话。
- Servers:提供上下文和能力的服务,可以是本地进程,也可以是远程服务。
MCP Server 向客户端暴露三类核心能力:
| 能力 | 说明 |
|---|---|
| Resources | 可供 AI 读取的上下文数据,如文件内容、数据库记录 |
| Prompts | 预定义的模板化消息和工作流 |
| Tools | AI 模型可调用的函数,如查询数据库、调用 API、读写文件 |
传输层方面,MCP 支持:
- stdio:标准输入输出,适合本地工具,零网络配置。
- Streamable HTTP:推荐的远程传输方式,单一 HTTP 端点,接受 POST,返回 JSON 或 SSE 流。
- HTTP + SSE:已弃用的双端点模式。
MCP 的设计灵感来自 Language Server Protocol(LSP):LSP 统一了编辑器对编程语言的支持,MCP 则以类似方式统一了 AI 应用对外部工具和数据的集成。
2. 用 Go 实现一个最简单的 MCP Server
最简洁的方式是使用 MCP 官方 Go SDK:github.com/modelcontextprotocol/go-sdk 。
2.1 初始化项目
1mkdir mcp-demo && cd mcp-demo
2go mod init mcp-demo
3go get github.com/modelcontextprotocol/go-sdk@latest
2.2 编写服务器代码
实现一个提供"获取当前时间"工具的 MCP Server:
1package main
2
3import (
4 "context"
5 "fmt"
6 "time"
7
8 "github.com/modelcontextprotocol/go-sdk/mcp"
9)
10
11func main() {
12 // 1. 创建 MCP 服务器实例
13 server := mcp.NewServer("mcp-demo", "1.0.0")
14
15 // 2. 注册 "getCurrentTime" 工具
16 server.AddTool(mcp.Tool{
17 Name: "getCurrentTime",
18 Description: "获取当前时间(支持时区参数)",
19 InputSchema: map[string]interface{}{
20 "type": "object",
21 "properties": map[string]interface{}{
22 "timezone": map[string]interface{}{
23 "type": "string",
24 "description": "时区名称,如 'Asia/Shanghai',默认为本地时区",
25 "default": "Local",
26 },
27 },
28 "required": []string{},
29 },
30 Handler: func(ctx context.Context, args map[string]interface{}) (interface{}, error) {
31 tzName, _ := args["timezone"].(string)
32 loc := time.Local
33
34 if tzName != "" && tzName != "Local" {
35 var err error
36 loc, err = time.LoadLocation(tzName)
37 if err != nil {
38 return nil, fmt.Errorf("无效时区: %v", err)
39 }
40 }
41
42 now := time.Now().In(loc).Format("2006-01-02 15:04:05 MST")
43 return map[string]string{"time": now}, nil
44 },
45 })
46
47 // 3. 通过 stdio 启动服务器
48 if err := server.ServeStdio(); err != nil {
49 fmt.Println("服务器错误:", err)
50 }
51}
核心逻辑非常直接:
mcp.NewServer创建服务器实例。AddTool注册工具,包含名称、描述、输入 JSON Schema 和处理函数。ServeStdio通过标准输入输出通信。
2.3 运行与测试
1go run main.go
服务器启动后,会通过 stdin 等待 JSON-RPC 请求。可以手动发送:
1{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"getCurrentTime","arguments":{"timezone":"Asia/Shanghai"}}}
在 VS Code 中配置 MCP Server 时,可在 .vscode/mcp.json 中添加:
1{
2 "servers": {
3 "mcp-demo": {
4 "type": "stdio",
5 "command": "go",
6 "args": ["run", "main.go"]
7 }
8 }
9}
3. 对外发布:让用户"下载即用"
如果希望用户下载对应平台的二进制文件后直接使用,核心思路是:交叉编译多平台二进制,通过 GitHub Releases 托管,再辅以包管理器或 Bundle 格式降低安装门槛。
3.1 交叉编译多平台二进制
Go 的交叉编译只需设置 GOOS 和 GOARCH。建议关闭 CGO,生成纯静态文件:
1# Linux amd64
2CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w" -o mcp-demo-linux-amd64
3
4# macOS Apple Silicon
5CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w" -o mcp-demo-darwin-arm64
6
7# Windows amd64
8CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o mcp-demo-windows-amd64.exe
建议至少覆盖:
| 平台 | GOOS | GOARCH |
|---|---|---|
| Linux x64 | linux | amd64 |
| Linux ARM64 | linux | arm64 |
| macOS Intel | darwin | amd64 |
| macOS Apple Silicon | darwin | arm64 |
| Windows x64 | windows | amd64 |
3.2 用 GoReleaser 自动化发布
手动逐个编译和上传容易出错。推荐使用 GoReleaser ,一条命令完成多平台构建、打包归档、生成校验和并上传 GitHub Releases。
初始化:
1goreleaser init
配置 GitHub Actions:.github/workflows/release.yml
1name: Release
2on:
3 push:
4 tags: ['v*']
5jobs:
6 release:
7 runs-on: ubuntu-latest
8 permissions:
9 contents: write
10 steps:
11 - uses: actions/checkout@v4
12 with:
13 fetch-depth: 0
14 - uses: actions/setup-go@v5
15 with:
16 go-version: 'stable'
17 - uses: goreleaser/goreleaser-action@v6
18 with:
19 args: release --clean
20 env:
21 GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
打 tag 推送即可自动发布:
1git tag v0.1.0 && git push origin v0.1.0
3.3 降低安装门槛:包管理器与 Bundle 格式
有了 GitHub Releases 产物,可以进一步接入分发渠道:
- Homebrew Tap:自建 tap 仓库,用户
brew install一条命令安装。 - Scoop / Winget(Windows)、APT/YUM 仓库(Linux):按目标用户群选择。
- npm 包装:即使工具是 Go 编写,也可以发一个 npm 包,安装时按平台下载对应二进制——这是很多 CLI 工具(如 esbuild)采用的模式。
- MCP Bundle(.mcpb):Anthropic 推出的打包格式,把 MCP Server 及其依赖打成一个文件,用户在 Claude Desktop 里一键安装,适合面向非开发者用户分发。
4. 自然语言注册:让 AI 自己装 MCP
随着 MCP 生态成熟,出现了"用自然语言描述需求 → AI 自动寻找并配置 MCP Server"的玩法。典型流程:
- 用户在支持 MCP 的客户端(如 Claude Code、Cursor)中用自然语言说:“帮我接入一个能查 GitHub Issue 的 MCP 工具”。
- 客户端/AI 通过 MCP Registry (官方注册表)或社区目录检索匹配的 Server。
- AI 生成配置(stdio 命令或远程 URL),写入
mcp.json,必要时引导完成 OAuth 授权。 - 重启会话后,新工具即可被模型调用。
要让你的 Server 被自然语言"搜到",关键是:
- 在 MCP Registry 提交 Server 元数据(名称、描述、传输方式、安装方式)。
Description写清楚工具做什么、参数含义——模型正是靠这些描述决定何时调用。- 保持命名语义化,避免无意义的缩写。
5. 远程服务化:Streamable HTTP
stdio 模式要求 Server 与客户端同机运行。当工具需要共享状态、访问内网数据或被多人使用时,需要把 MCP Server 部署为远程 HTTP 服务。
5.1 Go 实现 Streamable HTTP 端点
官方 Go SDK 提供 Streamable HTTP 传输,只需把 ServeStdio 换成 HTTP handler:
1func main() {
2 server := mcp.NewServer("mcp-demo", "1.0.0")
3 // ... 同前注册 Tool ...
4
5 handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
6 return server
7 }, nil)
8
9 http.Handle("/mcp", handler)
10 log.Println("MCP server listening on :8080/mcp")
11 log.Fatal(http.ListenAndServe(":8080", nil))
12}
客户端配置:
1{
2 "servers": {
3 "mcp-demo": {
4 "type": "http",
5 "url": "https://mcp.example.com/mcp"
6 }
7 }
8}
5.2 部署要点
- 认证:MCP 授权基于 OAuth 2.1;公网部署必须在网关层做鉴权,不能裸奔。
- 会话管理:Streamable HTTP 通过
Mcp-Session-Id头维持会话,水平扩容时需保证同一会话路由到同一实例或共享会话状态。 - SSE 流式响应:工具执行耗时长时可返回 SSE 流,注意反向代理(Nginx 等)要关闭缓冲。
- 部署形态:单二进制 + systemd/容器皆可;Kubernetes 部署时配合 HPA 按连接数或 CPU 扩缩容。
6. MCP 与 Agent Skill:关系与选型
MCP 之外,Anthropic 还推出了 Agent Skills(SKILL.md 格式)。两者常被拿来比较,实际定位不同:
| 维度 | MCP | Agent Skill |
|---|---|---|
| 本质 | 工具/数据接入协议(JSON-RPC) | 指令 + 脚本的知识包(Markdown) |
| 运行形态 | 独立进程或远程服务 | 客户端内按需加载的文件 |
| 适合提供 | 动态能力:查数据库、调 API、实时数据 | 静态知识:工作流程、领域规范、操作手册 |
| 集成成本 | 需实现协议、部署服务 | 复制一个目录即可 |
| 生态 | 跨厂商开放标准,客户端支持广 | 目前主要在 Claude 系客户端 |
协同模式:二者不是竞争关系。典型组合是——Skill 教会 Agent “怎么做”(流程、规范),MCP 提供"用什么做"(工具、数据)。例如一个运维排障 Skill 定义排查步骤,其中"查日志"这一步调用 MCP 的日志查询工具。
选型建议:
- 要暴露动态能力/私有数据给任何支持 MCP 的客户端 → 写 MCP Server。
- 要沉淀团队流程和领域知识,且主要在 Claude 生态使用 → 写 Skill。
- 复杂 Agent 产品 → 两者并用,Skill 编排流程,MCP 供给能力。
7. 总结
MCP 把"AI 应用 ↔ 外部工具"的集成从 N×M 的私有对接变成了 N+M 的标准对接。对个人和团队而言,现实的路径是:
- 用官方 Go SDK 几十行代码写出最小 Server,stdio 跑通本地工具。
- GoReleaser + GitHub Releases 完成多平台分发,按需接入包管理器或 .mcpb。
- 提交 MCP Registry 元数据,让 Server 可被自然语言检索和安装。
- 有共享/内网需求时,切换 Streamable HTTP 远程化,做好 OAuth 与会话管理。
- 与 Agent Skill 按需组合:Skill 管知识,MCP 管能力。
从本地 stdio 到远程服务,从手动安装到自然语言注册,MCP 生态正在快速补齐"发布—发现—使用"的完整链路,现在投入的集成成本,未来会随客户端支持的普及而持续摊薄。
