MCP详解
MCP详解 本文介绍如何使用 Go 语言开发 MCP (Model Context Protocol) Server 和 Client,并集成到 CodeBuddy 等 AI 客户端中。 1. MCP 协议介绍 1.1 什么是 MCP MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 于 2024 年 11 月发布的开放标准协议。说白了,MCP 就是
文章信息
- 原文链接:https://jiayq.blog.csdn.net/article/details/156689641
- 发布时间:2026-08-06 15:50:04
- 标签:#ai, #智能体, #agent, ##SSE, ##codebuddy
MCP详解
本文介绍如何使用 Go 语言开发 MCP (Model Context Protocol) Server 和 Client,并集成到 CodeBuddy 等 AI 客户端中。
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 Server (Go 实现)
Host (CodeBuddy/IDE)
执行操作
读取数据
JSON-RPC 2.0
LLM 大语言模型
MCP Client
Tools 工具
Resources 资源
Prompts 提示词
外部系统/API
数据源
从图中可以看到,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 调用流程:
外部系统 MCP Server (Go) MCP Client CodeBuddy 用户 外部系统 MCP Server (Go) MCP Client CodeBuddy 用户 发送请求 解析意图 initialize 请求 返回能力声明 tools/list 请求 返回可用工具列表 tools/call 请求 执行具体操作 返回结果 返回执行结果 整合响应 展示结果
整个流程可以分为三个阶段:
- 初始化阶段 :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 创建项目
1
2
3
4
5
6
7
8
9
# 创建项目目录
mkdir mcp-demo && cd mcp-demo
# 初始化 Go 模块
go mod init mcp-demo
# 安装 mcp-go SDK
go get github.com/mark3labs/mcp-go
执行完成后,go.mod 文件内容如下:
1
2
3
4
5
6
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 项目结构
1
2
3
4
5
mcp-demo/
├── go.mod
├── go.sum
└── main.go # MCP Server 完整实现
3.2 完整代码实现
下面是一个功能完整的 MCP Server 实现,包含详尽的中文注释:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
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 编译运行
1
2
3
4
5
6
# 编译
go build -o mcp-server main.go
# 测试运行(会等待 stdin 输入)
./mcp-server
4. 传输方式详解
MCP 协议支持三种传输方式:stdio、SSE(Server-Sent Events)、Streamable HTTP。前面的示例使用的是 stdio 方式,这一节详细介绍另外两种传输方式。
4.1 三种传输方式对比
先来看一张对比图,了解三种传输方式的特点:
Streamable HTTP 传输
HTTP /mcp
Client
MCP Server
SSE 传输
POST /message
SSE /sse
Client
MCP Server
stdio 传输
stdin/stdout
Client
MCP Server
| 特性 | 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 实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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 集成到现有路由中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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 完整示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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) :
- 每个请求都是独立的,不保存会话信息
- 适合简单的工具调用场景
- 资源消耗低,易于水平扩展
1
2
3
4
5
6
// 无状态模式配置
httpServer := server.NewStreamableHTTPServer(mcpServer,
server.WithEndpointPath("/mcp"),
server.WithStateLess(true), // 启用无状态模式
)
有状态模式(Stateful) :
- 服务器跟踪每个客户端的会话
- 支持复杂的多轮交互
- 适合需要上下文的场景
1
2
3
4
5
6
// 有状态模式配置
httpServer := server.NewStreamableHTTPServer(mcpServer,
server.WithEndpointPath("/mcp"),
server.WithStateful(true), // 启用有状态模式
)
4.3.4 启用 HTTPS
生产环境中建议启用 HTTPS:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 启用 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
1
2
3
4
5
6
// 连接 stdio Server(已在前面介绍)
client, err := client.NewStdioMCPClient(
"./mcp-server", // 可执行文件路径
[]string{}, // 启动参数
)
4.4.2 连接 SSE Server
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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:
1
2
3
4
5
6
7
8
9
10
{
"mcpServers": {
"go-mcp-demo": {
"command": "/path/to/mcp-server",
"args": [],
"env": {}
}
}
}
配置说明:
| 字段 | 说明 |
|---|---|
go-mcp-demo | Server 的唯一标识,可自定义 |
command | MCP Server 可执行文件的完整路径 |
args | 启动参数(可选) |
env | 环境变量(可选) |
5.3 完整配置示例
如果你的 MCP Server 需要额外的配置,可以这样写:
1
2
3
4
5
6
7
8
{
"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 完整示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
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
1
2
3
4
5
6
7
# 确保 MCP Server 已编译
go build -o mcp-server main.go
# 编译并运行 Client
go build -o mcp-client client.go
./mcp-client
预期输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
正在初始化 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 错误处理
1
2
3
4
5
6
7
8
9
10
// 推荐:使用 NewToolResultError 返回错误
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("操作失败: %v", err)), nil
}
// 不推荐:直接返回 error(会导致 Server 异常)
if err != nil {
return nil, err // 避免这样做
}
7.2 参数验证
1
2
3
4
5
6
7
8
9
// 推荐:使用 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 日志输出
1
2
3
4
5
6
// MCP Server 使用 stdio 通信,日志必须输出到 stderr
fmt.Fprintln(os.Stderr, "这是日志信息")
// 不要输出到 stdout,会干扰 JSON-RPC 通信
// fmt.Println("这样会出问题") // 错误!
7.4 超时处理
1
2
3
4
5
6
7
8
9
10
11
12
// 长时间操作应该检查 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
选对了能力类型和传输方式,开发起来就会顺畅很多。







