「MCP(Model Context Protocol)」 是 Anthropic 于 2024 年 11 月发布的一个开放协议,旨在标准化 AI 模型与外部工具、数据源之间的通信方式。
简单来说,MCP 定义了一套 「"AI 应用如何调用外部能力"」 的标准协议,就像 HTTP 定义了 Web 通信标准一样。
┌──────────────────────────────────────────────────────────────────────────────┐
│ MCP 在 AI 应用中的位置 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ 传统方式(各家自定义): │
│ ┌────────────┐ 各种私有协议 ┌────────────┐ │
│ │ AI 应用 A │ ──────────────────────► │ 工具服务1 │ │
│ │ │ ◄────────────────────── │ │ │
│ └────────────┘ └────────────┘ │
│ │
│ ┌────────────┐ 另一种私有协议 ┌────────────┐ │
│ │ AI 应用 B │ ──────────────────────► │ 工具服务2 │ │
│ │ │ ◄────────────────────── │ │ │
│ └────────────┘ └────────────┘ │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ MCP 方式(统一标准): │
│ ┌────────────┐ ┌────────────┐ │
│ │ AI 应用 A │ ────┐ │ MCP Server1│ │
│ └────────────┘ │ MCP 协议 │ (天气服务) │ │
│ ├─────────────────►├────────────┤ │
│ ┌────────────┐ │ │ MCP Server2│ │
│ │ AI 应用 B │ ────┘ │ (数据库) │ │
│ └────────────┘ └────────────┘ │
│ │
│ 任何 MCP Client 都能连接任何 MCP Server,无需定制适配 │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
概念 | 说明 | 类比 |
|---|---|---|
「MCP Server」 | 提供工具能力的服务端,暴露工具列表和执行接口 | 类似 REST API 服务 |
「MCP Client」 | 调用 MCP Server 的客户端,集成在 AI 应用中 | 类似 HTTP Client |
「Tool」 | MCP Server 暴露的具体能力(如查天气、搜索文档) | 类似 API 接口 |
「Resource」 | MCP Server 暴露的只读数据(如文件内容、数据库 Schema) | 类似静态资源 |
「Prompt」 | MCP Server 提供的预定义提示词模板 | 类似模板文件 |
「Transport」 | 底层传输方式(STDIO、HTTP SSE、Streamable HTTP) | 类似 TCP/WebSocket |
MCP 协议定义了三种核心能力,满足 AI 应用的不同需求:
┌──────────────────────────────────────────────────────────────────────────────┐
│ MCP 三大核心能力 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Tools │ │ Resources │ │ Prompts │ │
│ │ (工具) │ │ (资源) │ │ (提示词) │ │
│ ├──────────────────┤ ├──────────────────┤ ├──────────────────┤ │
│ │ │ │ │ │ │ │
│ │ 可执行的动作 │ │ 只读的数据 │ │ 预定义的模板 │ │
│ │ │ │ │ │ │ │
│ │ • 调用 API │ │ • 文件内容 │ │ • 对话模板 │ │
│ │ • 执行命令 │ │ • DB Schema │ │ • 指令模板 │ │
│ │ • 发送消息 │ │ • 配置信息 │ │ • 常用提示 │ │
│ │ • 修改数据 │ │ • 日志/监控 │ │ │ │
│ │ │ │ │ │ │ │
│ ├──────────────────┤ ├──────────────────┤ ├──────────────────┤ │
│ │ 有副作用 │ │ 无副作用(安全) │ │ 无副作用 │ │
│ │ 需要审批/确认 │ │ 可自动授权 │ │ 辅助生成 │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │
│ 典型协作流程: │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ 1. 读取 Resource(获取上下文)→ 2. AI 分析决策 → 3. 调用 Tool(执行) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
│ 例如:"修复 user 表索引" │
│ 1. Resource: 读取 db://schema/users(获取表结构) │
│ 2. AI 分析:需要在 email 字段加索引 │
│ 3. Tool: 执行 CREATE INDEX 语句 │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
维度 | Resource(资源) | Tool(工具) |
|---|---|---|
「操作类型」 | 只读 | 可读可写 |
「副作用」 | 无 | 有 |
「权限控制」 | 可默认放行 | 通常需审批 |
「使用场景」 | 获取上下文信息 | 执行具体动作 |
「协议方法」 | resources/list、resources/read | tools/list、tools/call |
「特有能力」 | 支持订阅变化、缓存友好 | 支持动态参数 |
场景 | Resource URI 示例 | 说明 |
|---|---|---|
文件系统 | file:///project/README.md | 读取项目文件作为上下文 |
数据库 Schema | db://mysql/schema/users | 让 AI 了解表结构以生成 SQL |
配置信息 | config://env/current | 获取当前环境配置 |
日志/监控 | logs://app/error/recent | 分析最近的错误日志 |
知识库 | wiki://api/authentication | 访问企业文档 |
「Prompt 与 Tool/Resource 的核心区别」:Tool 和 Resource 是给 「LLM」 用的,而 Prompt 是给 「人/应用」 用的。
维度 | Tool/Resource | Prompt |
|---|---|---|
「使用者」 | LLM 决定调用 | 用户主动选择 |
「用途」 | AI 执行任务 | 快捷指令、标准化模板 |
「协议方法」 | tools/*、resources/* | prompts/list、prompts/get |
「典型场景」:
「为什么不用 Tool?」 Prompt 用于 UI 展示和用户直接选择,不需要经过 LLM 理解意图,更直接、准确、省 Token。
MCP 协议定义了几个关键的 RPC 方法:
┌──────────────────────────────────────────────────────────────────────────────┐
│ MCP 协议核心方法 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. initialize(初始化握手) │
│ Client → Server: 协议版本、客户端能力 │
│ Server → Client: 服务端能力、支持的功能 │
│ │
│ 2. tools/list(获取工具列表) │
│ Client → Server: ListToolsRequest │
│ Server → Client: ListToolsResponse │
│ [ │
│ {name: "get_weather", description: "...", │
│ inputSchema: {type: "object", properties: {...}}} │
│ ] │
│ │
│ 3. tools/call(调用工具) │
│ Client → Server: CallToolRequest {name: "get_weather", args: {...}} │
│ Server → Client: CallToolResponse {content: [...]} │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
传输方式 | 特点 | 适用场景 |
|---|---|---|
「STDIO」 | 通过标准输入输出通信,启动子进程 | 本地工具、CLI 集成 |
「HTTP SSE」 | 基于 Server-Sent Events,单向流 | Web 服务、远程调用 |
「Streamable HTTP」 | 基于 HTTP,支持双向流式传输 | 云服务、高性能场景(推荐) |
技术 | 来源 | 说明 |
|---|---|---|
「SSE」 | W3C 标准 | Server-Sent Events,浏览器原生支持 EventSource API |
「HTTP/JSON-RPC」 | 行业标准 | IETF RFC / jsonrpc.org 定义 |
「Streamable HTTP」 | MCP 定义 | 基于 HTTP + SSE + JSON-RPC 的「组合使用方式」,非独立标准 |
❝「理解要点」:Streamable HTTP 不是新的网络协议,而是 MCP 规范定义的一种使用模式——用 HTTP POST 发送请求、用 SSE 接收响应和通知、消息格式为 JSON-RPC 2.0。类似于 REST 是使用 HTTP 的一种约定风格。 ❞
「常见误解」:MCP 支持 Streamable HTTP,是不是工具调用支持流式输出?
「答案:不是。」 MCP 的 "Streamable" 是「消息流」,不是「内容流」。
概念 | 含义 | 类比 |
|---|---|---|
「消息流」(MCP Streamable) | 多个完整的 JSON-RPC 消息在连接上流动 | 快递车持续送多个完整包裹 |
「内容流」(LLM 流式输出) | 单个响应边生成边返回(delta) | 一个包裹边打包边送 |
「Streamable HTTP 的真正目的」:
「tools/call 的响应特性」:
stream=true(边生成 Token 边返回)是不同的┌──────────────────────────────────────────────────────────────────────────────┐
│ trpc-agent-go MCP 实现架构 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Agent 层 │ │
│ │ │ │
│ │ LLMAgent / Graph Node │ │
│ │ │ │ │
│ │ │ 统一工具接口: tool.Tool / tool.ToolSet │ │
│ │ ▼ │ │
│ └───────┼─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────┼─────────────────────────────────────────────────────────────┐ │
│ │ ▼ 工具层 │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ ┌────────────────┐ │ │
│ │ │ 本地工具 │ │ Agent 工具 │ │ MCP 工具集 │ │ │
│ │ │ (Function) │ │ (AgentTool) │ │ (MCPToolSet) │ │ │
│ │ └─────────────────┘ └─────────────────┘ └───────┬────────┘ │ │
│ │ │ │ │
│ └─────────────────────────────────────────────────────────┼───────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────┼───────────┐ │
│ │ MCP 会话层 │ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ mcpSessionManager │ │ │
│ │ │ │ │ │
│ │ │ • connect() - 建立连接 │ │ │
│ │ │ • initialize() - MCP 协议握手 │ │ │
│ │ │ • listTools() - 获取工具列表 │ │ │
│ │ │ • callTool() - 调用工具 │ │ │
│ │ │ • close() - 关闭连接 │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────┬──────────────────────────────────┘ │ │
│ │ │ │ │
│ └──────────────────────────────┼──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────────┼──────────────────────────────────────┐ │
│ │ MCP 传输层 │ │
│ │ ▼ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │ │
│ │ │ STDIO │ │ HTTP SSE │ │ Streamable HTTP │ │ │
│ │ │ Transport │ │ Transport │ │ Transport │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └───────────┬─────────────┘ │ │
│ │ │ │ │ │ │
│ └──────────┼─────────────────┼──────────────────────┼─────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ MCP Server │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
组件 | 职责 | 文件位置 |
|---|---|---|
MCPToolSet | 实现 ToolSet 接口,管理 MCP 工具集合 | tool/mcp/toolset.go |
mcpTool | 实现 Tool 接口,封装单个 MCP 工具 | tool/mcp/tool.go |
mcpSessionManager | 管理 MCP 连接、重连、协议交互 | tool/mcp/toolset.go |
ConnectionConfig | MCP 连接配置(地址、传输方式、超时等) | tool/mcp/config.go |
接下来,我们聚焦四个核心问题:
┌──────────────────────────────────────────────────────────────────────────────┐
│ LLM 获取 MCP 工具列表流程 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ Agent.Run("查询天气") │
│ │ │
│ ▼ │
│ 收集所有工具 ◄─────────────────────────────────────────────────────┐ │
│ │ │ │
│ ├─► 本地工具: localTool1, localTool2 │ │
│ │ │ │
│ └─► mcpToolSet.Tools(ctx) ─────────────────────────────────► │ │
│ │ │ │
│ │ MCP 协议: tools/list │ │
│ ▼ │ │
│ ┌────────────────┐ │ │
│ │ MCP Server │ │ │
│ │ │ │ │
│ │ ListToolsResponse: │ │
│ │ [ │ │
│ │ {name: "get_weather", desc: "...", schema: {...}}, │ │
│ │ {name: "search_docs", desc: "...", schema: {...}} │ │
│ │ ] │ │
│ └────────────────┘ │ │
│ │ │ │
│ │ 转换为 tool.Tool 接口 │ │
│ ▼ │ │
│ 返回: [get_weather, search_docs] ─────────────────────────┘ │
│ │
│ │ │
│ ▼ │
│ 构建 System Prompt(包含所有工具的名称、描述、参数 Schema) │
│ │ │
│ ▼ │
│ 发送给 LLM │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
// tool/mcp/toolset.go
// Tools 实现 ToolSet 接口,返回 MCP Server 的工具列表
func (ts *ToolSet) Tools(ctx context.Context) []tool.Tool {
if err := ts.listTools(ctx); err != nil {
log.ErrorContext(ctx, "Failed to refresh tools", err)
}
return ts.tools
}
// listTools 从 MCP Server 获取工具列表
func (ts *ToolSet) listTools(ctx context.Context) error {
// 1. 确保已连接
if !ts.sessionManager.isConnected() {
if err := ts.sessionManager.connect(ctx); err != nil {
return err
}
}
// 2. 调用 MCP 协议的 ListTools 方法
mcpTools, err := ts.sessionManager.listTools(ctx)
if err != nil {
return err
}
// 3. 转换为框架统一的 tool.Tool 接口
tools := make([]tool.Tool, 0, len(mcpTools))
for _, mcpTool := range mcpTools {
tools = append(tools, newMCPTool(mcpTool, ts.sessionManager))
}
ts.tools = tools
returnnil
}
MCP 工具实现 tool.Tool 接口,LLM 看到的和本地工具一模一样┌──────────────────────────────────────────────────────────────────────────────┐
│ MCP 工具执行流程 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ LLM 决定调用工具: tool_calls: [{name: "get_weather", args: {city: "北京"}}] │
│ │ │
│ ▼ │
│ Agent 解析 tool_calls,找到对应的 Tool │
│ │ │
│ │ 调用: tool.Call(ctx, `{"city": "北京"}`) │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ mcpTool.Call() │ │
│ │ │ │
│ │ 1. 解析 JSON 参数 │ │
│ │ 2. 调用 sessionManager.callTool(ctx, "get_weather", args) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ MCP 协议: tools/call │
│ │ {method: "tools/call", params: {name: "get_weather", args: {...}}} │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MCP Server │ │
│ │ │ │
│ │ 执行 get_weather("北京") │ │
│ │ 返回: {temperature: 22, weather: "晴"} │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ MCP 协议: CallToolResponse │
│ ▼ │
│ 返回结果给 Agent,Agent 再发给 LLM 进行下一轮对话 │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
// tool/mcp/tool.go
type mcpTool struct {
mcpToolRef *mcp.Tool // MCP 工具元数据(名称、描述、Schema)
sessionManager *mcpSessionManager // 会话管理器(用于发起 MCP 调用)
}
// Call 实现 CallableTool 接口
func (t *mcpTool) Call(ctx context.Context, jsonArgs []byte) (any, error) {
// 1. 解析 JSON 参数
var args map[string]any
iflen(jsonArgs) > 0 {
json.Unmarshal(jsonArgs, &args)
}
// 2. 通过 MCP 协议调用远程工具
return t.sessionManager.callTool(ctx, t.mcpToolRef.Name, args)
}
// tool/mcp/toolset.go
// callTool 发起 MCP 工具调用
func (m *mcpSessionManager) callTool(ctx context.Context, name string, args map[string]any) (any, error) {
var result any
err := m.executeWithSessionReconnect(ctx, func() error {
// MCP 协议调用
resp, err := m.client.CallTool(ctx, mcp.CallToolParams{
Name: name,
Arguments: args,
})
if err != nil {
return err
}
result = resp.Content
returnnil
})
return result, err
}
mcpTool
实现 CallableTool 接口,Agent 不知道这是远程调用┌──────────────────────────────────────────────────────────────────────────────┐
│ 连接生命周期 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ 方式一:懒加载(默认) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 创建时:NewMCPToolSet(config) │ │
│ │ │ │ │
│ │ └──► 只保存配置,不建立连接 │ │
│ │ │ │
│ │ 首次使用:toolSet.Tools(ctx) 或 tool.Call() │ │
│ │ │ │ │
│ │ └──► 建立连接 + MCP 初始化握手 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ 方式二:显式初始化(推荐) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 创建时:toolSet := NewMCPToolSet(config) │ │
│ │ │ │
│ │ 显式初始化:err := toolSet.Init(ctx) ← 此时建立连接 │ │
│ │ │ │ │
│ │ └──► 连接失败立即报错(Fail Fast) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ 关闭时机: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ toolSet.Close() ← 显式调用,释放连接资源 │ │
│ │ │ │
│ │ 推荐写法: │ │
│ │ toolSet := mcp.NewMCPToolSet(config) │ │
│ │ if err := toolSet.Init(ctx); err != nil { │ │
│ │ log.Fatal(err) │ │
│ │ } │ │
│ │ defer toolSet.Close() // 程序退出时关闭 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
// tool/mcp/toolset.go
// 创建时:只保存配置,不连接
func NewMCPToolSet(config ConnectionConfig, opts ...ToolSetOption) *ToolSet {
sessionManager := newMCPSessionManager(config, ...)
return &ToolSet{
sessionManager: sessionManager,
tools: nil, // 工具列表为空,懒加载
}
}
// 显式初始化:立即连接
func (ts *ToolSet) Init(ctx context.Context) error {
return ts.listTools(ctx) // 内部会触发连接
}
// 首次使用时触发连接
func (m *mcpSessionManager) connect(ctx context.Context) error {
m.mu.Lock()
defer m.mu.Unlock()
// 1. 创建客户端(建立传输层连接)
client, err := m.createClient()
if err != nil {
return err
}
m.client = client
m.connected = true
// 2. MCP 协议初始化握手
if err := m.initialize(ctx); err != nil {
m.client.Close()
return err
}
m.initialized = true
returnnil
}
// 关闭连接
func (ts *ToolSet) Close() error {
return ts.sessionManager.close()
}
懒加载:首次 Tools() 或 Call() 时;显式:调用 Init() 时┌──────────────────────────────────────────────────────────────────────────────┐
│ 新工具感知机制 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ MCP Server 工具列表变化: │
│ 初始:[get_weather, search_docs] │
│ 新增:[get_weather, search_docs, translate] ← 新增了 translate │
│ │
│ ──────────────────────────────────────────────────────────────────────── │
│ │
│ 方式一:每次 Run 时自动刷新(推荐) │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ import "trpc.group/trpc-go/trpc-agent-go/agent/llmagent" │ │
│ │ │ │
│ │ agent := llmagent.New("assistant", │ │
│ │ llmagent.WithToolSets([]tool.ToolSet{mcpToolSet}), │ │
│ │ llmagent.WithRefreshToolSetsOnRun(true), // 关键配置 │ │
│ │ ) │ │
│ │ │ │
│ │ // 配置位置: agent/llmagent/option.go:383 │ │
│ │ │ │
│ │ 每次 agent.Run() 时: │ │
│ │ 1. 调用 mcpToolSet.Tools(ctx) │ │
│ │ 2. 内部重新调用 MCP Server 的 ListTools │ │
│ │ 3. 获取最新工具列表 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ 方式二:手动刷新 │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // 手动调用 Tools() 会重新获取工具列表 │ │
│ │ tools := mcpToolSet.Tools(ctx) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ 方式三:重新初始化 │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ // 关闭旧连接,重新初始化 │ │
│ │ mcpToolSet.Close() │ │
│ │ mcpToolSet = mcp.NewMCPToolSet(config) │ │
│ │ mcpToolSet.Init(ctx) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
// agent/llmagent/option.go:377-386
// WithRefreshToolSetsOnRun controls whether tools from ToolSets are
// refreshed from the underlying ToolSet on each run.
// When enabled, the agent will call ToolSet.Tools again when building
// the tools list for each invocation instead of using a fixed snapshot.
// This is useful when ToolSets provide a dynamic tool list (for example,
// MCP ToolSets that support ListTools at runtime).
func WithRefreshToolSetsOnRun(refresh bool) Option {
returnfunc(opts *Options) {
opts.RefreshToolSetsOnRun = refresh
}
}
// agent/llmagent/llm_agent.go
func (a *Agent) Run(ctx context.Context, prompt string) (string, error) {
// 如果配置了每次运行刷新工具
if a.option.RefreshToolSetsOnRun && len(a.option.ToolSets) > 0 {
a.refreshTools(ctx) // 重新获取所有 ToolSet 的工具
}
// ... 继续执行
}
// tool/mcp/toolset.go
// Tools 每次调用都会尝试刷新(从 MCP Server 重新获取)
func (ts *ToolSet) Tools(ctx context.Context) []tool.Tool {
if err := ts.listTools(ctx); err != nil {
// 刷新失败返回缓存的工具
log.ErrorContext(ctx, "Failed to refresh tools", err)
}
return ts.tools
}
配置 llmagent.WithRefreshToolSetsOnRun(true),每次 Run 自动获取最新工具MCP 连接可能因网络问题或服务端重启而断开,框架提供了自动重连机制:
┌──────────────────────────────────────────────────────────────────────────────┐
│ 自动重连机制 │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ 配置: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ mcpToolSet := mcp.NewMCPToolSet( │ │
│ │ config, │ │
│ │ mcp.WithSessionReconnect(3), // 最多重试 3 次 │ │
│ │ ) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
│ 重连流程: │
│ ┌────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ tool.Call() 或 toolSet.Tools() │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 执行操作 ──► 成功 ──► 返回结果 │ │
│ │ │ │ │
│ │ │ 失败 │ │
│ │ ▼ │ │
│ │ 是连接/会话错误? │ │
│ │ │ │ │
│ │ 否 │ 是 │ │
│ │ ↓ └──► 断开连接 → 重新连接 → 重新初始化 → 重试操作 │ │
│ │ 返回错误 │ │ │
│ │ ▼ │ │
│ │ 超过重试次数? │ │
│ │ 是 → 返回错误 │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
// tool/mcp/toolset.go
func (m *mcpSessionManager) executeWithSessionReconnect(ctx context.Context, fn func() error) error {
var lastErr error
for attempt := 0; attempt <= m.maxReconnectAttempts; attempt++ {
lastErr = fn()
if lastErr == nil {
returnnil
}
// 检查是否是可重连的错误
if !m.isReconnectableError(lastErr) {
return lastErr
}
// 重建连接
if err := m.reconnect(ctx); err != nil {
return err
}
}
return lastErr
}
func (m *mcpSessionManager) isReconnectableError(err error) bool {
// 检测连接断开、会话过期等错误
return errors.Is(err, ErrConnectionClosed) ||
errors.Is(err, ErrSessionExpired) ||
isNetworkError(err)
}
package main
import (
"context"
"log"
"time"
"trpc.group/trpc-go/trpc-agent-go/agent/llmagent"
"trpc.group/trpc-go/trpc-agent-go/model/openai"
"trpc.group/trpc-go/trpc-agent-go/tool"
"trpc.group/trpc-go/trpc-agent-go/tool/mcp"
)
func main() {
ctx := context.Background()
// 1. 创建 MCP 工具集(此时不连接)
mcpToolSet := mcp.NewMCPToolSet(
mcp.ConnectionConfig{
Transport: "streamable_http",
ServerURL: "http://localhost:3000/mcp",
Timeout: 10 * time.Second,
},
mcp.WithSessionReconnect(3), // 断连自动重试 3 次
)
// 2. 显式初始化(此时建立连接,推荐做法)
if err := mcpToolSet.Init(ctx); err != nil {
log.Fatalf("MCP 初始化失败: %v", err)
}
defer mcpToolSet.Close() // 程序退出时关闭连接
// 3. 创建 Agent
agent := llmagent.New(
"assistant",
llmagent.WithModel(openai.New("gpt-4o-mini")),
llmagent.WithToolSets([]tool.ToolSet{mcpToolSet}),
llmagent.WithRefreshToolSetsOnRun(true), // 每次运行刷新工具列表
)
// 4. 运行(内部会获取工具列表、调用 LLM、执行工具)
result, err := agent.Run(ctx, "查询北京今天的天气")
if err != nil {
log.Fatalf("运行失败: %v", err)
}
log.Printf("结果: %s", result)
}