MCP协议详解

cuixiaogang

简介

MCP,全称 Model Context Protocol,是 Anthropic 在 2024 年 11 月开源的一套协议。它的目标非常明确:为 LLM 应用和外部数据源、工具之间的连接,提供一种标准化的集成方式。

可以把 MCP 理解成 AI 场景里的“通用接口规范”。

在 MCP 出现之前,常见情况是这样的:

  • 想让模型查数据库 → 手写一套 DB 工具
  • 想让模型读文件 → 再写一套 File 工具
  • 想让模型调 Git/CI/内部系统 → 各写各的
  • 每个工具:
    • 接口不一样
    • 参数不一样
    • 返回结构不一样
  • 结果就是:
    • 大量重复劳动
    • 工具接入方式极不统一
    • 模型/框架一换,工具层全崩

MCP 想做的事,其实很简单:

用一套统一协议,把「模型如何调用外部能力」这件事规范下来。

MCP 统一的内容:

  • 工具怎么描述
  • 参数怎么定义
  • 怎么调用
  • 返回结果长什么样
  • 错误怎么处理

MCP 整体结构

MCP 的基本角色

MCP 的基本角色
MCP 的基本角色

  • Client:AI 应用这一侧
  • Server:真正干活的地方(工具、数据、系统)

MCP Server 分类

MCP 按能力能分成以下几类:

  • Tool(工具,是最常用的)
    • 有输入参数
    • 会执行动作
    • 会返回结果
  • Resource(资源)
    • 偏“读取型”
    • 像文件、文档、数据
  • Prompt(提示模板)
    • 统一的话术模板(可选)

MCP 运行流程

MCP 运行流程
MCP 运行流程

MCP 协议是处于模型真实系统之间的。

MCP 的通信方式

MCP 本身不限制怎么传,只规定传什么格式

常见的通信方式有:

  • stdio(本地工具)
  • WebSocket
  • HTTP+SSE(最常见,最通用)

三者相互对比

维度 stdio WebSocket HTTP / SSE
通信模型 进程内管道 长连接 请求 + 事件流
是否网络 ❌ 否 ✅ 是 ✅ 是
服务是否常驻 ❌ 否 ✅ 是 ✅ 是
并发能力 ❌ 低 ✅ 高 ✅ 中
实现复杂度 ⭐ 最简单 ⭐⭐ 中等 ⭐⭐⭐ 稍高
流式返回 ❌ 不适合 ✅ 天然支持 ✅ SSE 支持
鉴权 / 网关 ❌ 困难 ✅ 方便 ✅ 最方便
生产环境适合度 ✅✅
典型用途 本地插件 AI 服务 企业 API

使用 HTTP 通信方式改造 REST API

如何把一个已存在的 REST API 改造成 MCP?这是很多人关系的一个问题。下面以一个示例来说明:

  • 比如我有一个 REST API
1
GET /api/orders/{id}
  • 返回结果:
1
2
3
4
5
6
{
"id": "123",
"status": "paid",
"amount": 99.00,
"user_id": "u_1"
}

Step 1:声明Tool

要在 MCP 里,告知客户端,我是一个 Tool:

  • 这个工具叫啥
  • 什么时候该用
  • 参数长什么样子

注意:这一层不调用 API,只是在“声明能力”。

  • 概念层的 MCP 协议内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "get_order_status",
"description": "根据订单ID查询订单状态",
"inputSchema": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "订单ID"
}
},
"required": ["order_id"]
}
}
  • 实际工程中 MCP 协议结构(github.com/mark3labs/mcp-go)
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
// NewMCPServer creates a new MCP server instance with the given name, version and options
func NewMCPServer(
name, version string,
opts ...ServerOption,
) *MCPServer {
s := &MCPServer{
resources: make(map[string]resourceEntry),
resourceTemplates: make(map[string]resourceTemplateEntry),
prompts: make(map[string]mcp.Prompt),
promptHandlers: make(map[string]PromptHandlerFunc),
tools: make(map[string]ServerTool),
taskTools: make(map[string]ServerTaskTool),
toolHandlerMiddlewares: make([]ToolHandlerMiddleware, 0),
resourceHandlerMiddlewares: make([]ResourceHandlerMiddleware, 0),
promptHandlerMiddlewares: make([]PromptHandlerMiddleware, 0),
name: name,
version: version,
notificationHandlers: make(map[string]NotificationHandlerFunc),
tasks: make(map[string]*taskEntry),
expiredTasks: make(map[string]time.Time),
promptCompletionProvider: &DefaultPromptCompletionProvider{},
resourceCompletionProvider: &DefaultResourceCompletionProvider{},
capabilities: serverCapabilities{
tools: nil,
resources: nil,
prompts: nil,
logging: nil,
sampling: nil,
elicitation: nil,
roots: nil,
tasks: nil,
completions: nil,
},
tracer: tracing.NoopTracer(),
propagator: tracing.NoopPropagator(),
}

for _, opt := range opts {
opt(s)
}

return s
}
  • 给一个对照表
抽象概念(协议层) go-mcp 里的实现
MCP Server MCPServer
Tool 描述 ServerTool
Tool 执行逻辑 ToolHandlerFunc
Tool 调用 MCP 的 tools/call
Resource resourceEntry
Prompt mcp.Prompt
Middleware ToolHandlerMiddleware
  • 注册 MCP 协议的这个接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
srv := mcp.NewMCPServer("order-mcp", "0.1.0")

srv.RegisterTool(
mcp.NewTool(
"get_order",
"根据订单ID查询订单信息",
GetOrderHandler,
).
WithInputSchema(`
{
"type":"object",
"properties":{
"order_id":{"type":"string"}
},
"required":["order_id"]
}
`),
)

Step 2:MCP Server 调用 REST API

在 MCP 的服务中,直接调用原来的 REST API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
func GetOrderHandler(
ctx context.Context,
req mcp.ToolCallRequest,
) (any, error) {

orderID := req.Arguments["order_id"].(string)

// 调你原来的 REST API
order, err := callOrderREST(ctx, orderID)
if err != nil {
return nil, err
}

// 返回给模型的结果
return map[string]any{
"order_id": order.ID,
"status": order.Status,
"amount": order.Amount,
}, nil
}

Step 3:启动 MCP 服务

1
2
3
4
5
6
7
srv := mcp.NewMCPServer("order-mcp", "0.1.0")

// 注册 tools(略),Step 2总的代码

httpHandler := mcp.NewHTTPHandler(srv)

log.Fatal(http.ListenAndServe(":8080", httpHandler))

鉴权中间件

MCP 服务也需要支持鉴权,保护数据安全,一般需要使用中间件类完成鉴权处理

1
2
3
4
5
6
7
8
9
10
11
12
srv.UseToolMiddleware(func(
next mcp.ToolHandlerFunc,
) mcp.ToolHandlerFunc {
return func(ctx context.Context, req mcp.ToolCallRequest) (any, error) {
// 鉴权的逻辑
tenantID := ctx.Value("tenant_id")
if tenantID == nil {
return nil, errors.New("unauthorized")
}
return next(ctx, req)
}
})