从后端视角彻底理解 MCP:别再把 Tool 和 MCP 搞混了
一、先说结论(避免混淆)
很多人学 MCP 第一坑就是:
❌ 把 MCP 和 Tool 当成一回事
正确关系应该是:
MCP = 协议(通信规则)
Tool = 能力(具体接口)
👉 一句话记住:
MCP 负责"怎么调",Tool 负责"调什么"
二、用后端架构彻底拆开
从你熟悉的系统来看:
LLM(Agent)
↓
MCP Client(协议实现)
↓
MCP Server(协议网关)
↓
Tool(具体能力)
↓
后端服务(DB / IBE / Redis)
👉 分层解释
|
层 |
本质 |
对标后端 |
|
MCP |
协议 |
HTTP / Dubbo |
|
MCP Server |
网关 |
API Gateway |
|
Tool |
能力接口 |
Service 方法 |
|
后端系统 |
数据源 |
DB / 微服务 |
三、MCP 到底是什么?
MCP = 一套标准化的调用协议(基于 JSON-RPC 2.0)
它解决的问题是:
让 LLM 可以用统一方式调用外部能力
类比你熟的东西:
HTTP:浏览器调用接口
Dubbo:服务间调用
MCP:LLM 调用能力
四、从 chao-go 项目看 MCP 协议层
4.1 协议类型定义(types.go)
// JSON-RPC 请求 - 协议层不关心业务
type Request struct {
JSONRPC string `json:"jsonrpc"` // 协议版本,固定 "2.0"
ID interface{} `json:"id,omitempty"` // 请求ID,用于匹配响应
Method string `json:"method"` // 方法名,如 "tools/call"
Params interface{} `json:"params,omitempty"` // 参数,由具体方法解析
}
// JSON-RPC 响应
type Response struct {
JSONRPC string `json:"jsonrpc"`
ID interface{} `json:"id,omitempty"`
Result interface{} `json:"result,omitempty"`
Error *Error `json:"error,omitempty"`
}
// 标准错误码 - 协议层错误,不是业务错误
var (
ErrParseError = &Error{Code: -32700, Message: "Parse error"}
ErrInvalidRequest = &Error{Code: -32600, Message: "Invalid request"}
ErrMethodNotFound = &Error{Code: -32601, Message: "Method not found"}
ErrInvalidParams = &Error{Code: -32602, Message: "Invalid params"}
ErrInternal = &Error{Code: -32603, Message: "Internal error"}
)
👉 关键点:这些类型定义的是"怎么传",完全不关心"传什么"。
4.2 协议方法路由(server.go)
// HandleMessage 处理 MCP 请求 - 协议层只做路由
func (s *Server) HandleMessage(body []byte) *Response {
var req Request
if err := json.Unmarshal(body, &req); err != nil {
return &Response{JSONRPC: "2.0", Error: ErrParseError}
}
// 验证协议版本
if req.JSONRPC != "2.0" {
return &Response{JSONRPC: "2.0", ID: req.ID, Error: ErrInvalidRequest}
}
// 协议层路由 - 不执行业务逻辑
switch req.Method {
case "initialize":
return s.handleInitialize(req)
case "tools/list":
return s.handleToolsList(req)
case "tools/call":
return s.handleToolsCall(req)
case "ping":
return s.handlePing(req)
default:
return &Response{JSONRPC: "2.0", ID: req.ID, Error: ErrMethodNotFound}
}
}
👉 关键点:MCP Server 只是一个"协议网关",它:
- 解析协议格式
- 路由到对应方法
- 不执行任何业务逻辑
五、Tool 到底是什么?
Tool = MCP 暴露出来的"可调用能力"
本质就是:
一个带描述的 RPC 方法
5.1 Tool 的组成(非常关键)
// Tool 工具定义 - 能力层
type Tool struct {
Name string `json:"name"` // 接口名
Description string `json:"description"` // 给 LLM 的说明书(核心!)
InputSchema InputSchema `json:"inputSchema"` // 参数定义
}
// InputSchema 输入参数 Schema - 告诉 LLM 怎么调用
type InputSchema struct {
Type string `json:"type"`
Properties map[string]Property `json:"properties,omitempty"`
Required []string `json:"required,omitempty"`
}
// Property 参数属性
type Property struct {
Type string `json:"type"`
Description string `json:"description,omitempty"`
Enum []string `json:"enum,omitempty"`
Default interface{} `json:"default,omitempty"`
}
👉 对应关系:
|
字段 |
作用 |
|
name |
接口名 |
|
description |
给 LLM 的说明书(核心) |
|
schema |
参数定义 |
六、从 chao-go 看分层实现
6.1 项目目录结构
internal/
├── mcp/ # MCP 协议层
│ ├── types.go # 协议类型定义
│ ├── server.go # 协议服务器(网关)
│ ├── handler.go # HTTP 传输层
│ └── tools.go # 工具注册(能力层入口)
│
├── handler/ # HTTP Handler(传统接口)
│ └── ai.go
│
└── service/ # 业务服务层
├── json.go
├── encode.go
├── crypto.go
└── ...
6.2 分层调用链
┌─────────────────────────────────────────────────────────────┐
│ 调用链路图 │
├─────────────────────────────────────────────────────────────┤
│ │
│ LLM 发起请求 │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ HTTP Handler (handler.go) │ │
│ │ - 接收 HTTP 请求 │ │
│ │ - 支持 SSE 长连接 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MCP Server (server.go) │ │
│ │ - 解析 JSON-RPC 协议 │ │
│ │ - 路由到对应方法 │ │
│ │ - initialize / tools/list / tools/call │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Tool Registry (tools.go) │ │
│ │ - 查找工具定义 │ │
│ │ - 获取工具处理器 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Tool Handler (闭包函数) │ │
│ │ - 解析业务参数 │ │
│ │ - 调用 Service 层 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Service 层 (service/*.go) │ │
│ │ - 执行具体业务逻辑 │ │
│ │ - JSON格式化、编解码、加密等 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
SSE 长连接 是指 Server-Sent Events(服务器推送事件),是一种让服务器主动向客户端(LLM/Agent)推送数据的通信方式。
七、Tool 注册实战(tools.go)
7.1 工具注册表设计
// ToolHandler 工具处理函数类型 - 能力层接口
type ToolHandler func(args map[string]interface{}) (*ToolResult, error)
// ToolRegistry 工具注册表
type ToolRegistry struct {
tools map[string]Tool // 工具定义(给 LLM 看)
handlers map[string]ToolHandler // 工具处理器(执行逻辑)
}
// Register 注册工具 - 同时注册定义和处理器
func (r *ToolRegistry) Register(tool Tool, handler ToolHandler) {
r.tools[tool.Name] = tool
r.handlers[tool.Name] = handler
}
7.2 具体工具注册示例
// JSON 格式化工具
r.Register(Tool{
Name: "json_format",
Description: "格式化 JSON 字符串,使其更易读", // ← LLM 靠这个理解工具用途
InputSchema: InputSchema{
Type: "object",
Properties: map[string]Property{
"input": {Type: "string", Description: "要格式化的 JSON 字符串"},
"indent": {Type: "string", Description: "缩进字符,默认为两个空格", Default: " "},
},
Required: []string{"input"},
},
}, func(args map[string]interface{}) (*ToolResult, error) {
// 这是 Tool Handler - 调用 Service 层
input, _ := args["input"].(string)
indent, _ := args["indent"].(string)
if indent == "" {
indent = " "
}
result, err := services.JSON.Format(input, indent, false, false)
if err != nil {
return &ToolResult{Content: []Content{{Type: "text", Text: err.Error()}}, IsError: true}, nil
}
return &ToolResult{Content: []Content{{Type: "text", Text: result.Output}}}, nil
})
7.3 更多工具示例
// Base64 编解码工具
r.Register(Tool{
Name: "encode_base64",
Description: "Base64 编码或解码字符串",
InputSchema: InputSchema{
Type: "object",
Properties: map[string]Property{
"input": {Type: "string", Description: "要处理的字符串"},
"mode": {Type: "string", Description: "模式:encode 或 decode",
Enum: []string{"encode", "decode"}}, // ← 枚举限制
},
Required: []string{"input", "mode"},
},
}, func(args map[string]interface{}) (*ToolResult, error) {
input, _ := args["input"].(string)
mode, _ := args["mode"].(string)
decode := mode == "decode"
result, err := services.Encode.Base64(input, decode, false)
// ...
})
// AI 智能日志分析工具
r.Register(Tool{
Name: "ai_analyze_log",
Description: "使用 AI 分析日志内容,识别错误类型并给出排查建议",
InputSchema: InputSchema{
Type: "object",
Properties: map[string]Property{
"log_content": {Type: "string", Description: "要分析的日志内容"},
},
Required: []string{"log_content"},
},
}, func(args map[string]interface{}) (*ToolResult, error) {
logContent, _ := args["log_content"].(string)
result, err := services.AI.AnalyzeLog(logContent)
// ...
})
八、为什么会混淆?
因为在传统后端里:
协议 + 接口 = 一起出现
比如:
HTTP + /api/query
Dubbo + method()
👉 所以你会下意识觉得:
MCP = 接口
但在 MCP 里是拆开的:
MCP(协议)
+ Tool(能力)
九、一个完整调用流程(彻底打通理解)
用户:帮我格式化这段 JSON
↓ LLM 推理
决定调用 Tool:json_format
↓ MCP Client 发送请求
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "json_format",
"arguments": {
"input": "{\"name\":\"test\"}",
"indent": " "
}
}
}
↓ MCP Server 接收 (server.go)
HandleMessage() 解析 JSON-RPC
↓
路由到 handleToolsCall()
↓
从 ToolRegistry 获取 handler
↓ Tool Handler 执行 (tools.go)
解析 arguments
↓
调用 services.JSON.Format()
↓ Service 层执行业务 (service/json.go)
格式化 JSON
↓
返回结果
↓ MCP Server 返回响应
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\n \"name\": \"test\"\n}"
}
]
}
}
↓ LLM 组织语言回复用户
"已为您格式化完成:..."
👉 关键点:
- MCP 不关心业务 ❗
- Tool 才承载业务 ❗
十、用一句话彻底区分
MCP 是"高速公路",Tool 是"车"
再换一个后端类比:
MCP = HTTP协议
Tool = Controller接口
十一、从后端角度的正确建模
你以后设计系统时应该这样拆:
❌ 错误建模
MCP = 接口
✅ 正确建模
MCP(协议层) → internal/mcp/server.go
↓
Tool(能力层) → internal/mcp/tools.go
↓
Service(业务层) → internal/service/*.go
十二、chao-go 项目中的工具清单
|
工具名 |
功能 |
对应 Service |
|
|
JSON 格式化 |
JSONService |
|
|
JSON 压缩 |
JSONService |
|
|
Base64 编解码 |
EncodeService |
|
|
URL 编解码 |
EncodeService |
|
|
Unicode 编解码 |
EncodeService |
|
|
哈希计算 |
CryptoService |
|
|
时间转换 |
TimeService |
|
|
文本统计 |
TextService |
|
|
文本转换 |
TextService |
|
|
二维码生成 |
QRCodeService |
|
|
正则测试 |
RegexService |
|
|
文本对比 |
DiffService |
|
|
Markdown 渲染 |
MarkdownService |
|
|
Cron 解析 |
CronService |
|
|
颜色转换 |
ColorService |
|
|
AI 日志分析 |
AIService |
|
|
AI 正则生成 |
AIService |
|
|
AI 代码解释 |
AIService |
十三、后端工程师最容易犯的错误
❌ 错误 1:把 Tool 写成底层接口
queryDB
getList
👉 LLM 不会用,因为 description 不够清晰
❌ 错误 2:把 MCP 当业务层
// 错误:在 MCP Server 里写业务逻辑
func (s *Server) handleToolsCall(req Request) *Response {
// 不要在这里写业务逻辑!
if params.Name == "query_user" {
// 直接查数据库...
}
}
👉 MCP 只做协议路由,业务逻辑在 Service 层
❌ 错误 3:忽略 description
// 错误:description 太简单
Tool{
Name: "format",
Description: "格式化", // ← LLM 无法理解
}
// 正确:description 要详细
Tool{
Name: "json_format",
Description: "格式化 JSON 字符串,使其更易读。支持自定义缩进,默认为两个空格。",
}
十四、你应该建立的新认知
你不是在"写接口",而是在"暴露能力给 AI"
传统接口:
设计给 人 调用的
文档在 Wiki/Swagger
调用者看文档决定怎么调
MCP Tool:
设计给 LLM 调用的
文档在 description 字段
LLM 根据 description 自动决定怎么调
十五、最终总结
✔ MCP 是什么?
一套让 LLM 调用外部能力的协议(基于 JSON-RPC 2.0)
✔ Tool 是什么?
MCP 协议下暴露的具体能力,包含 name、description、inputSchema
✔ 两者关系?
MCP(怎么调) + Tool(调什么)
✔ 分层架构
协议层 (MCP Server) → 能力层 (Tool) → 业务层 (Service)
最后一刀(重点)
如果你还在问"Tool 是不是接口",说明你还在用传统后端思维
真正的转变是:
从"设计接口" → "设计 AI 能理解的能力"
更多推荐



所有评论(0)