MCP详解
本文详细介绍了如何使用Go语言开发MCP(Model Context Protocol)服务器和客户端,并集成到CodeBuddy等AI客户端中。MCP协议作为AI领域的标准化接口,采用客户端-服务器架构,通过JSON-RPC 2.0协议通信,提供Tools(工具)、Resources(资源)和Prompts(提示词)三种核心能力。文章从协议架构、核心概念入手,逐步讲解Go开发环境的准备、MCP服
MCP详解
本文介绍如何使用 Go 语言开发 MCP (Model Context Protocol) Server 和 Client,并集成到 CodeBuddy 等 AI 客户端中。
MCP详解

1. MCP 协议介绍
1.1 什么是 MCP
MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 于 2024 年 11 月发布的开放标准协议。说白了,MCP 就是 AI 领域的"USB-C 标准"——就像 USB 创建了一个通用接口,让任何 USB 设备都能连接到任何 USB 端口一样,MCP 让 LLM 应用能够以标准化的方式连接到各种外部数据源和工具。
打个比方,以前每个 AI 应用想要调用外部工具,都得自己写一套对接代码,就像早期每个手机厂商都有自己的充电接口一样。现在有了 MCP,大家都用同一套协议,开发一次就能到处使用。
1.2 MCP 协议架构
MCP 采用客户端-服务器架构,通过 JSON-RPC 2.0 协议进行通信。下面这张图展示了 MCP 的整体架构:
从图中可以看到,MCP 架构主要包含三个角色:
| 角色 | 说明 |
|---|---|
| Host | 宿主应用,如 CodeBuddy、Claude Desktop 等 AI 客户端 |
| MCP Client | 内置在 Host 中,负责与 MCP Server 通信 |
| MCP Server | 提供具体能力的服务端,可以用 Go、Python、TypeScript 等语言实现 |
1.3 MCP 核心概念
MCP Server 可以提供三种类型的能力:
| 能力类型 | 说明 | 类比 |
|---|---|---|
| Tools(工具) | 可执行的操作,如调用 API、执行计算、操作数据库等 | REST API 的 POST 端点 |
| Resources(资源) | 可读取的数据,如文件内容、数据库记录、配置信息等 | REST API 的 GET 端点 |
| Prompts(提示词) | 可复用的交互模板,定义 LLM 与用户的交互模式 | 预设的对话模板 |
1.4 MCP 数据流
下面这张时序图展示了一次完整的 MCP 调用流程:
整个流程可以分为三个阶段:
- 初始化阶段:Client 发送
initialize请求,Server 返回自己支持的能力(capabilities) - 发现阶段:Client 通过
tools/list、resources/list等请求获取可用的工具和资源 - 调用阶段:Client 根据用户意图调用具体的工具或读取资源
2. Go 开发环境准备
2.1 环境要求
| 依赖 | 版本要求 | 说明 |
|---|---|---|
| Go | 1.24+ | 本文使用 Go 1.24 |
| mcp-go | v0.44.0+ | Go 语言 MCP SDK |
2.2 创建项目
# 创建项目目录
mkdir mcp-demo && cd mcp-demo
# 初始化 Go 模块
go mod init mcp-demo
# 安装 mcp-go SDK
go get github.com/mark3labs/mcp-go
执行完成后,go.mod 文件内容如下:
module mcp-demo
go 1.24
require github.com/mark3labs/mcp-go v0.44.0
3. MCP Server 开发
这一节我们来实现一个完整的 MCP Server,包含 Tools、Resources、Prompts 三种能力。
3.1 项目结构
mcp-demo/
├── go.mod
├── go.sum
└── main.go # MCP Server 完整实现
3.2 完整代码实现
下面是一个功能完整的 MCP Server 实现,包含详尽的中文注释:
package main
import (
"context"
"fmt"
"os"
"strings"
"time"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
// 第一步:创建 MCP Server 实例
// NewMCPServer 参数说明:
// - name: 服务器名称,会在客户端显示
// - version: 服务器版本号
// - options: 可选配置项
s := server.NewMCPServer(
"Go MCP Demo Server", // 服务器名称
"1.0.0", // 版本号
server.WithToolCapabilities(true), // 启用 Tools 能力
server.WithResourceCapabilities(true, true), // 启用 Resources 能力
server.WithPromptCapabilities(true), // 启用 Prompts 能力
server.WithRecovery(), // 启用 panic 恢复,防止服务器崩溃
)
// 第二步:注册 Tools(工具)
registerTools(s)
// 第三步:注册 Resources(资源)
registerResources(s)
// 第四步:注册 Prompts(提示词模板)
registerPrompts(s)
// 第五步:启动服务器
// ServeStdio 通过标准输入输出与客户端通信
// 这是最常用的传输方式,适合与 CodeBuddy 等客户端集成
_, _ = fmt.Fprintln(os.Stderr, "MCP Server 启动中...")
if err := server.ServeStdio(s); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "服务器错误: %v\n", err)
os.Exit(1)
}
}
// Tools 注册函数
// registerTools 注册所有工具
// Tools 是 MCP 最核心的能力,用于执行具体操作
func registerTools(s *server.MCPServer) {
// 工具1:hello_world - 打招呼工具
// 这是最简单的工具示例,演示基本的参数处理
helloTool := mcp.NewTool("hello_world",
// 工具描述,会显示在客户端的工具列表中
mcp.WithDescription("向指定的人打招呼,返回问候语"),
// 定义字符串类型参数
mcp.WithString("name",
mcp.Required(), // 标记为必填参数
mcp.Description("要打招呼的人的名字"), // 参数描述
),
)
// 添加工具和对应的处理函数
s.AddTool(helloTool, helloHandler)
// 工具2:calculator - 计算器工具
// 演示枚举类型参数和数值计算
calculatorTool := mcp.NewTool("calculator",
mcp.WithDescription("执行基本的四则运算"),
// 枚举类型参数,限定可选值
mcp.WithString("operation",
mcp.Required(),
mcp.Description("运算类型"),
mcp.Enum("add", "subtract", "multiply", "divide"), // 限定可选值
),
// 数值类型参数
mcp.WithNumber("x",
mcp.Required(),
mcp.Description("第一个操作数"),
),
mcp.WithNumber("y",
mcp.Required(),
mcp.Description("第二个操作数"),
),
)
s.AddTool(calculatorTool, calculatorHandler)
// 工具3:get_current_time - 获取当前时间
// 演示无参数工具和时间处理
timeTool := mcp.NewTool("get_current_time",
mcp.WithDescription("获取当前系统时间"),
// 可选参数:时区
mcp.WithString("timezone",
mcp.Description("时区,如 Asia/Shanghai,默认为本地时区"),
),
)
s.AddTool(timeTool, timeHandler)
// 工具4:string_utils - 字符串处理工具
// 演示复杂的字符串操作
stringTool := mcp.NewTool("string_utils",
mcp.WithDescription("字符串处理工具,支持多种操作"),
mcp.WithString("action",
mcp.Required(),
mcp.Description("操作类型"),
mcp.Enum("uppercase", "lowercase", "reverse", "length", "trim"),
),
mcp.WithString("text",
mcp.Required(),
mcp.Description("要处理的文本"),
),
)
s.AddTool(stringTool, stringUtilsHandler)
}
// helloHandler 处理 hello_world 工具的调用
func helloHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 获取必填的 name 参数
// RequireString 会自动验证参数是否存在且为字符串类型
name, err := request.RequireString("name")
if err != nil {
// 返回错误结果,客户端会收到错误信息
return mcp.NewToolResultError(fmt.Sprintf("参数错误: %v", err)), nil
}
// 构造问候语
greeting := fmt.Sprintf("你好,%s!欢迎使用 Go MCP Server!", name)
// 返回文本结果
return mcp.NewToolResultText(greeting), nil
}
// calculatorHandler 处理 calculator 工具的调用
func calculatorHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 获取操作类型
operation, err := request.RequireString("operation")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数错误: %v", err)), nil
}
// 获取操作数
x, err := request.RequireFloat("x")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数 x 错误: %v", err)), nil
}
y, err := request.RequireFloat("y")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数 y 错误: %v", err)), nil
}
// 执行计算
var result float64
var symbol string
switch operation {
case "add":
result = x + y
symbol = "+"
case "subtract":
result = x - y
symbol = "-"
case "multiply":
result = x * y
symbol = "×"
case "divide":
// 除法需要检查除数是否为零
if y == 0 {
return mcp.NewToolResultError("错误:除数不能为零"), nil
}
result = x / y
symbol = "÷"
default:
return mcp.NewToolResultError(fmt.Sprintf("不支持的操作: %s", operation)), nil
}
// 返回计算结果
resultText := fmt.Sprintf("%.2f %s %.2f = %.2f", x, symbol, y, result)
return mcp.NewToolResultText(resultText), nil
}
// timeHandler 处理 get_current_time 工具的调用
func timeHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 获取可选的时区参数
timezone := request.GetString("timezone", "")
var t time.Time
if timezone != "" {
// 尝试加载指定时区
loc, err := time.LoadLocation(timezone)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("无效的时区: %s", timezone)), nil
}
t = time.Now().In(loc)
} else {
// 使用本地时区
t = time.Now()
}
// 格式化时间输出
result := fmt.Sprintf("当前时间: %s\n时区: %s",
t.Format(time.DateTime),
t.Location().String(),
)
return mcp.NewToolResultText(result), nil
}
// stringUtilsHandler 处理 string_utils 工具的调用
func stringUtilsHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 获取操作类型和文本
action, err := request.RequireString("action")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数错误: %v", err)), nil
}
text, err := request.RequireString("text")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数错误: %v", err)), nil
}
// 执行字符串操作
var result string
switch action {
case "uppercase":
result = strings.ToUpper(text)
case "lowercase":
result = strings.ToLower(text)
case "reverse":
// 反转字符串(支持 Unicode)
runes := []rune(text)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
result = string(runes)
case "length":
// 返回字符数(不是字节数)
result = fmt.Sprintf("字符串长度: %d 个字符", len([]rune(text)))
case "trim":
result = strings.TrimSpace(text)
default:
return mcp.NewToolResultError(fmt.Sprintf("不支持的操作: %s", action)), nil
}
return mcp.NewToolResultText(result), nil
}
// Resources 注册函数
// registerResources 注册所有资源
// Resources 用于向 LLM 暴露数据,类似于 REST API 的 GET 端点
func registerResources(s *server.MCPServer) {
// 资源1:server_info - 服务器信息(静态资源)
// 静态资源使用固定的 URI
serverInfoResource := mcp.NewResource(
"server://info", // 资源 URI
"服务器信息", // 资源名称
mcp.WithResourceDescription("获取 MCP Server 的基本信息"), // 资源描述
mcp.WithMIMEType("application/json"), // MIME 类型
)
s.AddResource(serverInfoResource, serverInfoHandler)
// 资源2:config - 配置信息(静态资源)
configResource := mcp.NewResource(
"config://app",
"应用配置",
mcp.WithResourceDescription("获取应用的配置信息"),
mcp.WithMIMEType("application/json"),
)
s.AddResource(configResource, configHandler)
// 资源3:file - 文件内容(动态资源模板)
// 动态资源使用 URI 模板,可以根据参数返回不同内容
fileTemplate := mcp.NewResourceTemplate(
"file://{path}", // URI 模板,{path} 是变量
"文件内容",
mcp.WithTemplateDescription("读取指定路径的文件内容"),
mcp.WithTemplateMIMEType("text/plain"),
)
s.AddResourceTemplate(fileTemplate, fileHandler)
}
// serverInfoHandler 处理 server_info 资源的读取
func serverInfoHandler(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
// 构造服务器信息 JSON
info := `{
"name": "Go MCP Demo Server",
"version": "1.0.0",
"go_version": "1.24",
"sdk": "mcp-go v0.44.0",
"capabilities": ["tools", "resources", "prompts"],
"author": "jiayq"
}`
// 返回资源内容
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: request.Params.URI,
Text: info,
},
}, nil
}
// configHandler 处理 config 资源的读取
func configHandler(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
// 模拟配置信息
config := `{
"debug": false,
"log_level": "info",
"max_connections": 100,
"timeout_seconds": 30,
"features": {
"tools_enabled": true,
"resources_enabled": true,
"prompts_enabled": true
}
}`
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: request.Params.URI,
Text: config,
},
}, nil
}
// fileHandler 处理文件资源的读取
func fileHandler(ctx context.Context, request mcp.ReadResourceRequest) ([]mcp.ResourceContents, error) {
// 从 URI 中提取文件路径
// 注意:实际应用中需要做安全检查,防止路径遍历攻击
uri := request.Params.URI
path := strings.TrimPrefix(uri, "file://")
// 这里只是演示,返回模拟内容
// 实际应用中应该读取真实文件
content := fmt.Sprintf("这是文件 %s 的模拟内容\n\n实际应用中,这里会返回真实的文件内容。", path)
return []mcp.ResourceContents{
mcp.TextResourceContents{
URI: uri,
Text: content,
},
}, nil
}
// Prompts 注册函数
// registerPrompts 注册所有提示词模板
// Prompts 是可复用的交互模板,帮助 LLM 更好地理解用户意图
func registerPrompts(s *server.MCPServer) {
// 提示词1:code_review - 代码审查模板
codeReviewPrompt := mcp.NewPrompt("code_review",
mcp.WithPromptDescription("代码审查提示词模板,帮助进行代码评审"),
mcp.WithArgument("code",
mcp.ArgumentDescription("要审查的代码"),
mcp.RequiredArgument(),
),
mcp.WithArgument("language",
mcp.ArgumentDescription("编程语言,如 go、python、java"),
mcp.RequiredArgument(),
),
)
s.AddPrompt(codeReviewPrompt, codeReviewHandler)
// 提示词2:explain_error - 错误解释模板
explainErrorPrompt := mcp.NewPrompt("explain_error",
mcp.WithPromptDescription("解释错误信息,提供解决方案"),
mcp.WithArgument("error_message",
mcp.ArgumentDescription("错误信息"),
mcp.RequiredArgument(),
),
mcp.WithArgument("context",
mcp.ArgumentDescription("错误发生的上下文(可选)"),
),
)
s.AddPrompt(explainErrorPrompt, explainErrorHandler)
// 提示词3:generate_test - 生成测试用例模板
generateTestPrompt := mcp.NewPrompt("generate_test",
mcp.WithPromptDescription("为指定函数生成单元测试"),
mcp.WithArgument("function_code",
mcp.ArgumentDescription("要测试的函数代码"),
mcp.RequiredArgument(),
),
mcp.WithArgument("test_framework",
mcp.ArgumentDescription("测试框架,如 testing、testify"),
),
)
s.AddPrompt(generateTestPrompt, generateTestHandler)
}
// codeReviewHandler 处理代码审查提示词
func codeReviewHandler(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
// 获取参数
code := request.Params.Arguments["code"]
language := request.Params.Arguments["language"]
// 构造提示词消息
promptText := fmt.Sprintf(`请对以下 %s 代码进行审查,从以下几个方面给出建议:
1. **代码质量**:是否符合最佳实践,是否有代码异味
2. **性能**:是否存在性能问题,是否有优化空间
3. **安全性**:是否存在安全漏洞
4. **可读性**:命名是否清晰,注释是否充分
5. **可维护性**:代码结构是否合理,是否易于扩展
代码如下:
%s`, language, code)
return &mcp.GetPromptResult{
Description: "代码审查提示词",
Messages: []mcp.PromptMessage{
{
Role: mcp.RoleUser,
Content: mcp.TextContent{
Type: "text",
Text: promptText,
},
},
},
}, nil
}
// explainErrorHandler 处理错误解释提示词
func explainErrorHandler(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
errorMessage := request.Params.Arguments["error_message"]
context := request.Params.Arguments["context"]
var promptText string
if context != "" {
promptText = fmt.Sprintf(`请解释以下错误信息,并提供可能的解决方案:
错误信息:
%s
发生上下文:
%s
请从以下几个方面回答:
1. 这个错误是什么意思?
2. 可能的原因有哪些?
3. 如何解决这个问题?
4. 如何避免再次发生?`, errorMessage, context)
} else {
promptText = fmt.Sprintf(`请解释以下错误信息,并提供可能的解决方案:
错误信息:
%s
请从以下几个方面回答:
1. 这个错误是什么意思?
2. 可能的原因有哪些?
3. 如何解决这个问题?`, errorMessage)
}
return &mcp.GetPromptResult{
Description: "错误解释提示词",
Messages: []mcp.PromptMessage{
{
Role: mcp.RoleUser,
Content: mcp.TextContent{
Type: "text",
Text: promptText,
},
},
},
}, nil
}
// generateTestHandler 处理生成测试提示词
func generateTestHandler(ctx context.Context, request mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
functionCode := request.Params.Arguments["function_code"]
testFramework := request.Params.Arguments["test_framework"]
if testFramework == "" {
testFramework = "testing" // 默认使用标准库
}
promptText := fmt.Sprintf(`请为以下 Go 函数生成完整的单元测试代码:
函数代码:
%s
要求:
1. 使用 %s 框架
2. 覆盖正常情况和边界情况
3. 包含表驱动测试(Table-Driven Tests)
4. 添加清晰的测试用例注释
5. 测试函数命名遵循 Go 规范`, functionCode, testFramework)
return &mcp.GetPromptResult{
Description: "生成测试用例提示词",
Messages: []mcp.PromptMessage{
{
Role: mcp.RoleUser,
Content: mcp.TextContent{
Type: "text",
Text: promptText,
},
},
},
}, nil
}
3.3 代码说明
上面的代码实现了一个功能完整的 MCP Server,包含以下能力:
Tools(工具):
| 工具名 | 功能 | 参数 |
|---|---|---|
hello_world |
向指定的人打招呼 | name(必填) |
calculator |
四则运算计算器 | operation、x、y(必填) |
get_current_time |
获取当前时间 | timezone(可选) |
string_utils |
字符串处理 | action、text(必填) |
Resources(资源):
| 资源 URI | 功能 |
|---|---|
server://info |
获取服务器信息 |
config://app |
获取应用配置 |
file://{path} |
读取指定文件(动态资源) |
Prompts(提示词):
| 提示词名 | 功能 |
|---|---|
code_review |
代码审查模板 |
explain_error |
错误解释模板 |
generate_test |
生成测试用例模板 |
3.4 编译运行
# 编译
go build -o mcp-server main.go
# 测试运行(会等待 stdin 输入)
./mcp-server

4. 传输方式详解
MCP 协议支持三种传输方式:stdio、SSE(Server-Sent Events)、Streamable HTTP。前面的示例使用的是 stdio 方式,这一节详细介绍另外两种传输方式。
4.1 三种传输方式对比
先来看一张对比图,了解三种传输方式的特点:
| 特性 | stdio | SSE | Streamable HTTP |
|---|---|---|---|
| 通信方式 | 标准输入输出 | 服务端推送 + POST 请求 | HTTP 双向流 |
| 适用场景 | 本地 CLI 工具、IDE 插件 | Web 应用、需要实时推送 | 通用 HTTP 服务、微服务 |
| 部署复杂度 | 低(无需网络) | 中(需要 HTTP 服务器) | 中(需要 HTTP 服务器) |
| 网络穿透 | 不支持 | 支持 | 支持 |
| 多客户端 | 不支持 | 支持 | 支持 |
| 会话管理 | 自动(进程级别) | 需要配置 | 可配置有状态/无状态 |
4.2 SSE 传输方式
SSE(Server-Sent Events)是一种基于 HTTP 的单向推送技术。在 MCP 中,SSE 用于服务端向客户端推送消息,而客户端通过 POST 请求向服务端发送消息。
4.2.1 SSE Server 配置选项
mcp-go SDK 提供了丰富的 SSE 配置选项:
| 配置选项 | 说明 | 默认值 |
|---|---|---|
WithSSEEndpoint(endpoint) |
设置 SSE 端点路径 | /sse |
WithMessageEndpoint(endpoint) |
设置消息端点路径 | /message |
WithBaseURL(url) |
设置服务器基础 URL | - |
WithStaticBasePath(path) |
设置静态基础路径 | - |
WithKeepAlive(bool) |
启用/禁用 Keep-Alive | false |
WithKeepAliveInterval(duration) |
Keep-Alive 间隔时间 | - |
WithHTTPServer(srv) |
使用自定义 HTTP 服务器 | - |
4.2.2 SSE Server 完整示例
下面是一个完整的 SSE Server 实现:
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
func main() {
// 第一步:创建 MCP Server 实例(与 stdio 方式相同)
mcpServer := server.NewMCPServer(
"Go MCP SSE Server", // 服务器名称
"1.0.0", // 版本号
server.WithToolCapabilities(true), // 启用 Tools 能力
server.WithResourceCapabilities(true, true), // 启用 Resources 能力
server.WithPromptCapabilities(true), // 启用 Prompts 能力
server.WithRecovery(), // 启用 panic 恢复
)
// 第二步:注册工具(这里只注册一个简单的工具作为演示)
helloTool := mcp.NewTool("hello_world",
mcp.WithDescription("向指定的人打招呼"),
mcp.WithString("name",
mcp.Required(),
mcp.Description("要打招呼的人的名字"),
),
)
mcpServer.AddTool(helloTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 获取 name 参数
name, err := request.RequireString("name")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数错误: %v", err)), nil
}
// 返回问候语
return mcp.NewToolResultText(fmt.Sprintf("你好,%s!这是来自 SSE Server 的问候!", name)), nil
})
host := "0.0.0.0"
port := 10001
// 第三步:创建 SSE Server
// NewSSEServer 参数说明:
// - mcpServer: 上面创建的 MCP Server 实例
// - opts: SSE 配置选项
sseServer := server.NewSSEServer(mcpServer,
// 设置 SSE 端点路径,客户端通过此路径接收服务端推送的消息
server.WithSSEEndpoint("/sse"),
// 设置消息端点路径,客户端通过此路径发送请求
server.WithMessageEndpoint("/message"),
// 设置基础 URL,用于生成完整的端点 URL
server.WithBaseURL(fmt.Sprintf("http://%s:%d", host, port)),
// 启用 Keep-Alive,保持连接活跃
server.WithKeepAlive(true),
// 设置 Keep-Alive 间隔为 30 秒
server.WithKeepAliveInterval(30*time.Second),
)
// 第四步:启动 SSE Server
// 监听地址
fmt.Printf("SSE Server 启动中,监听地址: http://%s:%d\n", host, port)
fmt.Printf("SSE 端点: http://%s:%d/sse\n", host, port)
fmt.Printf("消息端点: http://%s:%d/message\n", host, port)
// 优雅关闭处理
// 创建一个 channel 用于接收系统信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 在 goroutine 中启动服务器
go func() {
// Start 方法会阻塞,直到服务器停止
if err := sseServer.Start(fmt.Sprintf(":%d", port)); err != nil {
fmt.Printf("服务器错误: %v\n", err)
}
}()
// 等待关闭信号
<-sigChan
fmt.Println("\n收到关闭信号,正在优雅关闭...")
// 创建带超时的 context
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// 优雅关闭服务器
if err := sseServer.Shutdown(ctx); err != nil {
fmt.Printf("关闭服务器错误: %v\n", err)
}
fmt.Println("服务器已关闭")
}
在服务端启动

配置使用

使用

4.2.3 SSE Server 与自定义路由集成
如果你已经有一个 HTTP 服务器,可以将 SSE Server 集成到现有路由中:
package main
import (
"context"
"fmt"
"net/http"
"time"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// SSE Server 与自定义路由集成示例
// 功能:演示如何将 SSE Server 集成到现有的 HTTP 服务中
func main() {
// 创建 MCP Server
mcpServer := server.NewMCPServer(
"Go MCP SSE Server",
"1.0.0",
server.WithToolCapabilities(true),
)
// 注册一个简单的工具
mcpServer.AddTool(
mcp.NewTool("ping", mcp.WithDescription("返回 pong")),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText("pong"), nil
},
)
// 创建 SSE Server
sseServer := server.NewSSEServer(mcpServer,
server.WithSSEEndpoint("/sse"),
server.WithMessageEndpoint("/message"),
server.WithKeepAlive(true),
server.WithKeepAliveInterval(30*time.Second),
)
// 创建自定义路由
mux := http.NewServeMux()
// 健康检查端点
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
// 挂载 SSE 端点
// SSEHandler() 返回处理 SSE 连接的 http.Handler
mux.Handle("/mcp/sse", sseServer.SSEHandler())
// 挂载消息端点
// MessageHandler() 返回处理客户端消息的 http.Handler
mux.Handle("/mcp/message", sseServer.MessageHandler())
// 其他业务端点
mux.HandleFunc("/api/info", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"name":"MCP Demo","version":"1.0.0"}`))
})
// 启动 HTTP 服务器
fmt.Println("服务器启动中,监听 :8080")
fmt.Println("SSE 端点: http://localhost:8080/mcp/sse")
fmt.Println("消息端点: http://localhost:8080/mcp/message")
fmt.Println("健康检查: http://localhost:8080/health")
if err := http.ListenAndServe(":8080", mux); err != nil {
fmt.Printf("服务器错误: %v\n", err)
}
}
4.3 Streamable HTTP 传输方式
Streamable HTTP 是 MCP 协议支持的另一种 HTTP 传输方式,它支持直接的 HTTP 请求/响应和 SSE 流式响应。相比 SSE,Streamable HTTP 更加灵活,支持有状态和无状态两种模式。
4.3.1 Streamable HTTP Server 配置选项
| 配置选项 | 说明 | 默认值 |
|---|---|---|
WithEndpointPath(path) |
设置端点路径 | /mcp |
WithStateful(bool) |
启用有状态会话管理 | false |
WithStateLess(bool) |
启用无状态模式 | true |
WithSessionIdManager(manager) |
自定义会话 ID 管理器 | - |
WithHeartbeatInterval(duration) |
设置心跳间隔 | - |
WithDisableStreaming(bool) |
禁用流式响应 | false |
WithHTTPContextFunc(fn) |
设置上下文修改函数 | - |
WithTLSCert(cert, key) |
设置 TLS 证书 | - |
4.3.2 Streamable HTTP Server 完整示例
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)
// MCP Streamable HTTP Server 完整示例
// 功能:演示如何使用 Streamable HTTP 传输方式启动 MCP Server
// 作者:
// Go版本:1.24
// SDK版本:mcp-go v0.44.0
func main() {
// 第一步:创建 MCP Server 实例
mcpServer := server.NewMCPServer(
"Go MCP HTTP Server", // 服务器名称
"1.0.0", // 版本号
server.WithToolCapabilities(true), // 启用 Tools 能力
server.WithResourceCapabilities(true), // 启用 Resources 能力
server.WithPromptCapabilities(true), // 启用 Prompts 能力
server.WithRecovery(), // 启用 panic 恢复
)
// 第二步:注册工具
// 工具1:获取服务器状态
statusTool := mcp.NewTool("get_server_status",
mcp.WithDescription("获取服务器运行状态"),
)
mcpServer.AddTool(statusTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
// 返回服务器状态信息
status := fmt.Sprintf(`{
"status": "running",
"uptime": "%s",
"transport": "streamable_http",
"timestamp": "%s"
}`, time.Since(startTime).String(), time.Now().Format(time.RFC3339))
return mcp.NewToolResultText(status), nil
})
// 工具2:回显工具
echoTool := mcp.NewTool("echo",
mcp.WithDescription("回显输入的消息"),
mcp.WithString("message",
mcp.Required(),
mcp.Description("要回显的消息"),
),
)
mcpServer.AddTool(echoTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
message, err := request.RequireString("message")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数错误: %v", err)), nil
}
return mcp.NewToolResultText(fmt.Sprintf("Echo: %s", message)), nil
})
// 第三步:创建 Streamable HTTP Server
// NewStreamableHTTPServer 参数说明:
// - mcpServer: 上面创建的 MCP Server 实例
// - opts: HTTP 配置选项
httpServer := server.NewStreamableHTTPServer(mcpServer,
// 设置端点路径,所有 MCP 请求都通过此路径处理
server.WithEndpointPath("/mcp"),
// 启用有状态会话管理
// 有状态模式下,服务器会跟踪每个客户端的会话
server.WithStateful(true),
// 设置心跳间隔,保持长连接活跃
server.WithHeartbeatInterval(30*time.Second),
)
// 第四步:启动 HTTP Server
addr := ":8080"
fmt.Printf("Streamable HTTP Server 启动中,监听地址: %s\n", addr)
fmt.Printf("MCP 端点: http://localhost%s/mcp\n", addr)
// 优雅关闭处理
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
// 在 goroutine 中启动服务器
go func() {
if err := httpServer.Start(addr); err != nil {
fmt.Printf("服务器错误: %v\n", err)
}
}()
// 等待关闭信号
<-sigChan
fmt.Println("\n收到关闭信号,正在优雅关闭...")
// 优雅关闭
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := httpServer.Shutdown(ctx); err != nil {
fmt.Printf("关闭服务器错误: %v\n", err)
}
fmt.Println("服务器已关闭")
}
// startTime 记录服务器启动时间
var startTime = time.Now()
4.3.3 无状态模式 vs 有状态模式
Streamable HTTP Server 支持两种会话管理模式:
无状态模式(Stateless):
- 每个请求都是独立的,不保存会话信息
- 适合简单的工具调用场景
- 资源消耗低,易于水平扩展
// 无状态模式配置
httpServer := server.NewStreamableHTTPServer(mcpServer,
server.WithEndpointPath("/mcp"),
server.WithStateLess(true), // 启用无状态模式
)
有状态模式(Stateful):
- 服务器跟踪每个客户端的会话
- 支持复杂的多轮交互
- 适合需要上下文的场景
// 有状态模式配置
httpServer := server.NewStreamableHTTPServer(mcpServer,
server.WithEndpointPath("/mcp"),
server.WithStateful(true), // 启用有状态模式
)
4.3.4 启用 HTTPS
生产环境中建议启用 HTTPS:
// 启用 HTTPS
httpServer := server.NewStreamableHTTPServer(mcpServer,
server.WithEndpointPath("/mcp"),
server.WithStateful(true),
// 设置 TLS 证书和密钥文件路径
server.WithTLSCert("/path/to/cert.pem", "/path/to/key.pem"),
)
// 启动 HTTPS 服务器
if err := httpServer.Start(":8443"); err != nil {
fmt.Printf("服务器错误: %v\n", err)
}
4.4 MCP Client 连接不同传输方式
前面介绍了三种 Server 的实现,现在来看看 Client 如何连接这些不同传输方式的 Server。
4.4.1 连接 stdio Server
// 连接 stdio Server(已在前面介绍)
client, err := client.NewStdioMCPClient(
"./mcp-server", // 可执行文件路径
[]string{}, // 启动参数
)
4.4.2 连接 SSE Server
package main
import (
"context"
"fmt"
"time"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/mcp"
)
// MCP SSE Client 示例
// 功能:演示如何连接 SSE 传输方式的 MCP Server
func main() {
// 创建带超时的 context
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 创建 SSE Client
// NewSSEMCPClient 参数说明:
// - baseURL: SSE Server 的基础 URL
// - opts: 客户端配置选项
c, err := client.NewSSEMCPClient(
"http://localhost:8080", // SSE Server 地址
// 可选:设置自定义 HTTP 客户端
// client.WithHTTPClient(customHTTPClient),
)
if err != nil {
fmt.Printf("创建客户端失败: %v\n", err)
return
}
defer c.Close()
// 初始化连接
fmt.Println("正在连接 SSE Server...")
initResult, err := c.Initialize(ctx, mcp.InitializeRequest{
ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,
ClientInfo: mcp.Implementation{
Name: "Go SSE Client Demo",
Version: "1.0.0",
},
})
if err != nil {
fmt.Printf("初始化失败: %v\n", err)
return
}
fmt.Printf("连接成功!Server: %s v%s\n",
initResult.ServerInfo.Name,
initResult.ServerInfo.Version,
)
// 调用工具
fmt.Println("\n调用 hello_world 工具:")
result, err := c.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "hello_world",
Arguments: map[string]interface{}{
"name": "SSE Client",
},
},
})
if err != nil {
fmt.Printf("调用失败: %v\n", err)
return
}
// 打印结果
for _, content := range result.Content {
if textContent, ok := content.(mcp.TextContent); ok {
fmt.Printf("结果: %s\n", textContent.Text)
}
}
}
4.4.3 连接 Streamable HTTP Server
package main
import (
"context"
"fmt"
"time"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/mcp"
)
// MCP Streamable HTTP Client 示例
// 功能:演示如何连接 Streamable HTTP 传输方式的 MCP Server
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 创建 Streamable HTTP Client
// NewStreamableHttpClient 参数说明:
// - endpointURL: HTTP Server 的端点 URL
// - opts: 客户端配置选项
c, err := client.NewStreamableHttpClient(
"http://localhost:8080/mcp", // HTTP Server 端点
// 可选配置
// client.WithHTTPClient(customHTTPClient),
)
if err != nil {
fmt.Printf("创建客户端失败: %v\n", err)
return
}
defer c.Close()
// 初始化连接
fmt.Println("正在连接 Streamable HTTP Server...")
initResult, err := c.Initialize(ctx, mcp.InitializeRequest{
ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,
ClientInfo: mcp.Implementation{
Name: "Go HTTP Client Demo",
Version: "1.0.0",
},
})
if err != nil {
fmt.Printf("初始化失败: %v\n", err)
return
}
fmt.Printf("连接成功!Server: %s v%s\n",
initResult.ServerInfo.Name,
initResult.ServerInfo.Version,
)
// 获取工具列表
fmt.Println("\n可用工具列表:")
toolsResult, err := c.ListTools(ctx, mcp.ListToolsRequest{})
if err != nil {
fmt.Printf("获取工具列表失败: %v\n", err)
} else {
for _, tool := range toolsResult.Tools {
fmt.Printf("- %s: %s\n", tool.Name, tool.Description)
}
}
// 调用工具
fmt.Println("\n调用 get_server_status 工具:")
statusResult, err := c.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "get_server_status",
Arguments: map[string]interface{}{},
},
})
if err != nil {
fmt.Printf("调用失败: %v\n", err)
return
}
for _, content := range statusResult.Content {
if textContent, ok := content.(mcp.TextContent); ok {
fmt.Printf("结果:\n%s\n", textContent.Text)
}
}
fmt.Println("\n调用 echo 工具:")
echoResult, err := c.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "echo",
Arguments: map[string]interface{}{
"message": "Hello from HTTP Client!",
},
},
})
if err != nil {
fmt.Printf("调用失败: %v\n", err)
return
}
for _, content := range echoResult.Content {
if textContent, ok := content.(mcp.TextContent); ok {
fmt.Printf("结果: %s\n", textContent.Text)
}
}
}
4.5 传输方式选择建议
说白了,选择哪种传输方式主要看你的使用场景:
| 场景 | 推荐传输方式 | 原因 |
|---|---|---|
| 本地 IDE 插件(如 CodeBuddy) | stdio | 简单直接,无需网络配置 |
| Web 应用需要实时推送 | SSE | 支持服务端主动推送消息 |
| 微服务架构 | Streamable HTTP | 标准 HTTP 协议,易于集成 |
| 需要跨网络访问 | SSE 或 HTTP | 支持网络穿透 |
| 需要多客户端连接 | SSE 或 HTTP | 支持并发连接 |
| 需要有状态会话 | Streamable HTTP | 内置会话管理 |
5. CodeBuddy 集成
这一节介绍如何将我们开发的 MCP Server 集成到 CodeBuddy 中。
5.1 配置文件
CodeBuddy 通过配置文件来管理 MCP Server。配置文件位置:
| 操作系统 | 配置文件路径 |
|---|---|
| macOS | ~/Library/Application Support/CodeBuddy/mcp_settings.json |
| Windows | %APPDATA%\CodeBuddy\mcp_settings.json |
| Linux | ~/.config/CodeBuddy/mcp_settings.json |
5.2 配置示例
在配置文件中添加我们的 MCP Server:
{
"mcpServers": {
"go-mcp-demo": {
"command": "/path/to/mcp-server",
"args": [],
"env": {}
}
}
}
配置说明:
| 字段 | 说明 |
|---|---|
go-mcp-demo |
Server 的唯一标识,可自定义 |
command |
MCP Server 可执行文件的完整路径 |
args |
启动参数(可选) |
env |
环境变量(可选) |

5.3 完整配置示例
如果你的 MCP Server 需要额外的配置,可以这样写:
{
"mcpServers": {
"go-mcp-demo": {
"command": "/path"
}
}
}
5.4 验证集成
配置完成后,重启 CodeBuddy,然后:
- 打开 CodeBuddy 的 MCP 面板
- 查看是否显示
go-mcp-demoServer - 检查 Server 状态是否为"已连接"
- 尝试调用工具,如让 AI 执行计算

尝试使用

6. Go 项目集成 MCP Client
除了作为 Server 被 AI 客户端调用,我们也可以在自己的 Go 项目中作为 Client 调用其他 MCP Server。
6.1 MCP Client 完整示例
package main
import (
"context"
"fmt"
"os"
"os/exec"
"time"
"github.com/mark3labs/mcp-go/client"
"github.com/mark3labs/mcp-go/mcp"
)
// MCP Client 完整示例
// 功能:演示如何在 Go 项目中调用 MCP Server
// 作者:
// Go版本:1.24
func main() {
// 第一步:创建 MCP Client
// 这里演示通过 stdio 连接到本地 MCP Server
// MCP Server 的可执行文件路径
serverPath := "./mcp-server"
// 创建 stdio 客户端
// StdioMCPClient 会启动一个子进程运行 MCP Server,并通过 stdin/stdout 通信
c, err := client.NewStdioMCPClient(
serverPath, // MCP Server 可执行文件路径
[]string{}, // 启动参数
)
if err != nil {
fmt.Printf("创建客户端失败: %v\n", err)
os.Exit(1)
}
defer c.Close()
// 创建带超时的 context
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// 第二步:初始化连接
fmt.Println("正在初始化 MCP 连接...")
// Initialize 会发送 initialize 请求,获取 Server 的能力声明
initResult, err := c.Initialize(ctx, mcp.InitializeRequest{
ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,
ClientInfo: mcp.Implementation{
Name: "Go MCP Client Demo",
Version: "1.0.0",
},
Capabilities: mcp.ClientCapabilities{},
})
if err != nil {
fmt.Printf("初始化失败: %v\n", err)
os.Exit(1)
}
fmt.Printf("连接成功!\n")
fmt.Printf("Server: %s v%s\n", initResult.ServerInfo.Name, initResult.ServerInfo.Version)
fmt.Printf("协议版本: %s\n\n", initResult.ProtocolVersion)
// 第三步:获取可用工具列表
fmt.Println("========== 可用工具列表 ==========")
toolsResult, err := c.ListTools(ctx, mcp.ListToolsRequest{})
if err != nil {
fmt.Printf("获取工具列表失败: %v\n", err)
} else {
for _, tool := range toolsResult.Tools {
fmt.Printf("- %s: %s\n", tool.Name, tool.Description)
}
}
fmt.Println()
// 第四步:调用工具
fmt.Println("========== 调用工具示例 ==========")
// 示例1:调用 hello_world 工具
fmt.Println("\n1. 调用 hello_world 工具:")
helloResult, err := c.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "hello_world",
Arguments: map[string]interface{}{
"name": "CodeBuddy",
},
},
})
if err != nil {
fmt.Printf(" 调用失败: %v\n", err)
} else {
printToolResult(helloResult)
}
// 示例2:调用 calculator 工具
fmt.Println("\n2. 调用 calculator 工具:")
calcResult, err := c.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "calculator",
Arguments: map[string]interface{}{
"operation": "multiply",
"x": 12.5,
"y": 8.0,
},
},
})
if err != nil {
fmt.Printf(" 调用失败: %v\n", err)
} else {
printToolResult(calcResult)
}
// 示例3:调用 get_current_time 工具
fmt.Println("\n3. 调用 get_current_time 工具:")
timeResult, err := c.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "get_current_time",
Arguments: map[string]interface{}{
"timezone": "Asia/Shanghai",
},
},
})
if err != nil {
fmt.Printf(" 调用失败: %v\n", err)
} else {
printToolResult(timeResult)
}
// 示例4:调用 string_utils 工具
fmt.Println("\n4. 调用 string_utils 工具:")
stringResult, err := c.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "string_utils",
Arguments: map[string]interface{}{
"action": "reverse",
"text": "Hello MCP",
},
},
})
if err != nil {
fmt.Printf(" 调用失败: %v\n", err)
} else {
printToolResult(stringResult)
}
// 第五步:获取资源列表
fmt.Println("\n========== 可用资源列表 ==========")
resourcesResult, err := c.ListResources(ctx, mcp.ListResourcesRequest{})
if err != nil {
fmt.Printf("获取资源列表失败: %v\n", err)
} else {
for _, resource := range resourcesResult.Resources {
fmt.Printf("- %s: %s\n", resource.URI, resource.Name)
}
}
// 第六步:读取资源
fmt.Println("\n========== 读取资源示例 ==========")
fmt.Println("\n1. 读取 server://info 资源:")
readResult, err := c.ReadResource(ctx, mcp.ReadResourceRequest{
Params: mcp.ReadResourceParams{
URI: "server://info",
},
})
if err != nil {
fmt.Printf(" 读取失败: %v\n", err)
} else {
for _, content := range readResult.Contents {
if textContent, ok := content.(mcp.TextResourceContents); ok {
fmt.Printf(" 内容:\n%s\n", textContent.Text)
}
}
}
// 第七步:获取提示词列表
fmt.Println("\n========== 可用提示词列表 ==========")
promptsResult, err := c.ListPrompts(ctx, mcp.ListPromptsRequest{})
if err != nil {
fmt.Printf("获取提示词列表失败: %v\n", err)
} else {
for _, prompt := range promptsResult.Prompts {
fmt.Printf("- %s: %s\n", prompt.Name, prompt.Description)
}
}
// 第八步:获取提示词内容
fmt.Println("\n========== 获取提示词示例 ==========")
fmt.Println("\n1. 获取 code_review 提示词:")
promptResult, err := c.GetPrompt(ctx, mcp.GetPromptRequest{
Params: mcp.GetPromptParams{
Name: "code_review",
Arguments: map[string]string{
"code": "func Add(a, b int) int { return a + b }",
"language": "go",
},
},
})
if err != nil {
fmt.Printf(" 获取失败: %v\n", err)
} else {
fmt.Printf(" 描述: %s\n", promptResult.Description)
for _, msg := range promptResult.Messages {
if textContent, ok := msg.Content.(mcp.TextContent); ok {
// 只显示前200个字符
text := textContent.Text
if len(text) > 200 {
text = text[:200] + "..."
}
fmt.Printf(" 内容: %s\n", text)
}
}
}
fmt.Println("\n========== 演示完成 ==========")
}
// printToolResult 打印工具调用结果
func printToolResult(result *mcp.CallToolResult) {
if result.IsError {
fmt.Printf(" 错误: %v\n", result.Content)
return
}
for _, content := range result.Content {
if textContent, ok := content.(mcp.TextContent); ok {
fmt.Printf(" 结果: %s\n", textContent.Text)
}
}
}
6.2 运行 Client
# 确保 MCP Server 已编译
go build -o mcp-server main.go
# 编译并运行 Client
go build -o mcp-client client.go
./mcp-client
预期输出:
正在初始化 MCP 连接...
连接成功!
Server: Go MCP Demo Server v1.0.0
协议版本: 2024-11-05
========== 可用工具列表 ==========
- hello_world: 向指定的人打招呼,返回问候语
- calculator: 执行基本的四则运算
- get_current_time: 获取当前系统时间
- string_utils: 字符串处理工具,支持多种操作
========== 调用工具示例 ==========
1. 调用 hello_world 工具:
结果: 你好,CodeBuddy!欢迎使用 Go MCP Server!
2. 调用 calculator 工具:
结果: 12.50 × 8.00 = 100.00
3. 调用 get_current_time 工具:
结果: 当前时间: 2025-01-06 15:30:45
时区: Asia/Shanghai
4. 调用 string_utils 工具:
结果: PCM olleH
========== 演示完成 ==========
7. 最佳实践
7.1 错误处理
// 推荐:使用 NewToolResultError 返回错误
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("操作失败: %v", err)), nil
}
// 不推荐:直接返回 error(会导致 Server 异常)
if err != nil {
return nil, err // 避免这样做
}
7.2 参数验证
// 推荐:使用 SDK 提供的方法获取参数
name, err := request.RequireString("name")
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("参数错误: %v", err)), nil
}
// 可选参数使用 GetString,提供默认值
timezone := request.GetString("timezone", "Asia/Shanghai")
7.3 日志输出
// MCP Server 使用 stdio 通信,日志必须输出到 stderr
fmt.Fprintln(os.Stderr, "这是日志信息")
// 不要输出到 stdout,会干扰 JSON-RPC 通信
// fmt.Println("这样会出问题") // 错误!
7.4 超时处理
// 长时间操作应该检查 context 是否已取消
func longRunningHandler(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
select {
case <-ctx.Done():
return mcp.NewToolResultError("操作超时"), nil
default:
// 继续执行
}
// ... 执行操作
}
8. 总结
这篇文章介绍了如何使用 Go 语言开发 MCP Server 和 Client,重点补充了三种传输方式的详细介绍。说实话,MCP 协议本身并不复杂,但是它解决了一个很实际的问题——让 AI 应用能够以标准化的方式调用外部工具和数据。
通过 mcp-go SDK,我们可以很方便地:
- 开发 MCP Server,提供 Tools、Resources、Prompts 三种能力
- 选择合适的传输方式:stdio 适合本地集成,SSE 适合实时推送,HTTP 适合微服务
- 将 Server 集成到 CodeBuddy 等 AI 客户端
- 在 Go 项目中作为 Client 调用其他 MCP Server
在实际开发中,我觉得最重要的是想清楚两件事:
- 提供什么能力:Tools 适合执行操作,Resources 适合暴露数据,Prompts 适合定义交互模板
- 选择什么传输方式:本地用 stdio,需要网络访问用 SSE 或 HTTP
选对了能力类型和传输方式,开发起来就会顺畅很多。
参考资料
更多推荐



所有评论(0)