游乐游手机版
首页/AI教程/文章详情

MCP工具集成将外部工具转换为Eino Tool

时间:2026-07-03 16:11
MCP协议基于JSON-RPC2 0,通过initialize(初始化)、tools list(工具列表)、tools call(工具调用)三个方法及stdio、SSE、StreamableHTTP传输。Eino框架利用GetTools函数查询工具列表、转换Schema并包装为InvokableTool接口,实现外部工具无缝透传调用。DeepFlux在此基础

通过本文掌握 MCP 协议与 Eino 工具集成的核心要点

MCP(Model Context Protocol)近期热度极高,行业内外广泛讨论。但坦白说,该协议本身并不复杂——它解决的核心问题其实很朴素:当 AI 模型需要调用外部工具时,双方必须建立一套公认的“通信规范”。

本文将从协议基础讲起,深入解析 Eino 框架如何将 MCP 工具“转换”为自身可识别的 Tool 接口。最后补充 DeepFlux 在双向桥接方面的实践方案,展示如何将平台内部能力通过 MCP 协议对外开放。

一、MCP 协议基础解读

MCP 全称为 Model Context Protocol,其目标非常清晰——为 AI 模型与外部工具之间提供统一的通信语言。

MCP 工具集成:外部工具变 Eino Tool

底层通信采用 JSON-RPC 2.0 格式,所有消息结构统一:

{"jsonrpc": "2.0","id": 1,"method": "tools/call","params": {"name": "calculate","arguments": { "operation": "add", "x": 3, "y": 5 }}}

初看可能有些抽象,但您只需关注三个核心方法即可:

方法名功能描述
initialize握手协商,交换协议版本及能力信息
tools/list查询 MCP Server 提供的可用工具
tools/call执行指定工具的调用

传输方式包括三种:stdio(子进程管道)、SSE(HTTP 长连接)、Streamable HTTP。

关键挑战在于:Eino 并不直接支持 MCP,它只识别自身定义的 Tool 接口。因此中间需要一层适配器来完成“协议转换”。

二、Eino 的 Tool 接口设计

Eino 将工具抽象为两个层级:

// 最小接口:仅提供元数据(告知 LLM 工具名称及参数信息)
type BaseTool interface {
    Info(ctx context.Context) (*schema.ToolInfo, error)
}

// 可调用工具:在 BaseTool 基础上增加执行能力
type InvokableTool interface {
    BaseTool
    InvokableRun(ctx context.Context, argumentsInJSON string, opts ...Option) (string, error)
}

其中 ToolInfo 是关键数据结构:

type ToolInfo struct {
    Name       string
    Desc       string
    ParamsOneOf // 参数 schema,支持两种表现形式
}

ParamsOneOf 有两种创建方式:使用 NewParamsOneOfByParams() 通过简化写法传入 map[string]*ParameterInfo,或使用 NewParamsOneOfByJSONSchema() 传入完整 JSON Schema——后者正是适配 MCP 时所需的方式。

只要对象实现了 InvokableTool 接口,Eino 的 ToolsNode 即可调用它。因此目标非常明确:将 MCP 工具封装一层,使其实现该接口。

三、GetTools():30 行代码完成三个关键任务

eino-ext 的核心函数位于 components/tool/mcp/mcp.go

func GetTools(ctx context.Context, conf *Config) ([]tool.BaseTool, error) {
    // ① 向 MCP Server 查询工具列表
    listResults, err := conf.Cli.ListTools(ctx, mcp.ListToolsRequest{})
    ret := make([]tool.BaseTool, 0)
    for _, t := range listResults.Tools {
        // ② 将 MCP 的 InputSchema 转换为 Eino 兼容格式
        marshaledInputSchema, _ := sonic.Marshal(t.InputSchema)
        inputSchema := &jsonschema.Schema{}
        sonic.Unmarshal(marshaledInputSchema, inputSchema)
        // ③ 创建包装器,实现 InvokableTool 接口
        ret = append(ret, &toolHelper{
            cli: conf.Cli,
            info: &schema.ToolInfo{
                Name:        t.Name,
                Desc:        t.Description,
                ParamsOneOf: schema.NewParamsOneOfByJSONSchema(inputSchema),
            },
        })
    }
    return ret, nil
}

第一步:查询工具列表。conf.Cli.ListTools() 发送 tools/list 的 JSON-RPC 请求,获取所有工具的名称、描述及参数 schema。

第二步:Schema 类型转换。这一步最容易踩坑——MCP SDK 中的 InputSchema 类型为 map[string]interface{}(动态类型),而 Eino 需要强类型的 *jsonschema.Schema

转换方式看似绕路:先将数据 Marshal 为 JSON 字节,再 Unmarshal 为目标类型。但实践证明,这是最稳健的做法——无需依赖字段名映射,不受 struct tag 影响,有效避免各类隐式转换陷阱。

第三步:包装器实现。toolHelper 是一个私有结构体,持有 MCP 客户端引用。当 Eino 的 ToolsNode 需要执行工具时,调用它的 InvokableRun() 方法:

func (m *toolHelper) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
    result, err := m.cli.CallTool(ctx, mcp.CallToolRequest{
        Request: mcp.Request{Method: "tools/call"},
        Params: mcp.CallToolParams{
            Name:      m.info.Name,
            Arguments: json.RawMessage(argumentsInJSON), // 直接透传,不解析
        },
    })
    // ...
    return sonic.MarshalString(result)
}

参数是 Eino 传来的 JSON 字符串,直接作为 json.RawMessage 传递给 MCP,结果序列化为字符串返回。没有额外解析,没有类型转换——整条数据链路保持 JSON 格式,两端透传,干净高效。

四、完整调用链路解析

整个流程串联如下:

用户输入 → LLM 决定调用工具
↓
Eino ToolsNode(接收 ToolCall 消息:工具名 + JSON 参数)
↓
找到对应的 toolHelper
↓
InvokableRun(argumentsInJSON)
↓
MCP Client 发送 JSON-RPC: tools/call
↓
MCP Server 执行工具逻辑
↓
JSON-RPC 响应
↓
序列化为字符串 → 返回给 LLM 作为 ToolMessage

Eino 仅看到一个实现了接口的对象,MCP Server 仅看到标准 JSON-RPC 消息,中间的适配层完全透明。

五、三步完成 MCP 工具接入

实际使用中,接入 MCP 工具仅需三步:

// 1. 创建 MCP 客户端并完成握手
cli, _ := client.NewSSEMCPClient("https://your-mcp-server/sse")
cli.Start(ctx)
cli.Initialize(ctx, mcp.InitializeRequest{
    Params: mcp.InitializeRequestParams{
        ProtocolVersion: mcp.LATEST_PROTOCOL_VERSION,
        ClientInfo:      mcp.Implementation{Name: "my-agent", Version: "1.0.0"},
    },
})

// 2. 拉取工具列表并完成转换
import mcpTool "github.com/cloudwego/eino-ext/components/tool/mcp"
tools, _ := mcpTool.GetTools(ctx, &mcpTool.Config{Cli: cli})

// 3. 注入 ToolsNode
toolsNode, _ := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{Tools: tools})

之后 Agent 即可透明地调用该 MCP Server 上的所有工具。新增工具无需修改 Agent 代码——下一次 GetTools() 调用时会自动发现。

六、两套 SDK 如何选择

eino-ext 同时提供了两套实现,这一细节值得关注:

对比维度mark3labs 版本官方 SDK 版本
包路径components/tool/mcpcomponents/tool/mcp/officialmcp
底层依赖github.com/mark3labs/mcp-gogithub.com/modelcontextprotocol/go-sdk
运行时选项支持(自定义 Header、Meta)不支持
分页能力不支持支持(Cursor 参数)

两套核心逻辑几乎一致,差异主要体现在选项支持上。mark3labs 版本额外提供了 ToolCallResultHandler,这个功能非常实用:

conf := &mcpTool.Config{
    Cli: cli,
    ToolCallResultHandler: func(ctx context.Context, name string, result *mcp.CallToolResult) (*mcp.CallToolResult, error) {
        // 工具返回后、交给 LLM 前的拦截点
        // 可用于:截断超长输出、过滤敏感内容、记录日志
        return result, nil
    },
}

对于返回大量文本的工具(例如网页抓取、数据库查询),该钩子可在返回前进行裁剪,避免单次工具输出撑爆上下文。

七、DeepFlux 的双向桥接实践

eino-ext 的适配是单向的——将外部 MCP 工具转换为 Eino Tool(入方向)。

DeepFlux 在此基础上实现了出方向:将平台自身的能力(知识库 KB、长期记忆 Memory、提示词模板)暴露给外部 MCP 客户端(如 Cursor、Cline 等)。

入方向的问题已由 eino-ext 解决:外部 MCP 工具 → Eino Tool → DeepFlux Agent 可用。

出方向是 DeepFlux 新增的核心能力,关键代码在 server/internal/mcp/bridge.go

// 知识库 → MCP Resource(deepflux://kb/
type kbResources struct { svc KBService }

// 知识库搜索 → MCP Tool(kb_search)
type kbSearchTool struct { svc KBService }
func (k *kbSearchTool) Call(ctx context.Context, raw json.RawMessage) (json.RawMessage, error) {
    // 解析参数,调用 KB 服务,返回 top-K 结果
}

// 长期记忆 → 两个 MCP Tool
// memory_recall:召回
// memory_write:写入(高风险操作,需 HITL 审批)

RegisterAll 将这些能力挂载到 MCP Server,同时配置了 HITL(人工介入审批):

func RegisterAll(srv *Server, kb KBService, mem MemoryService, opts BridgeOptions) {
    srv.RegisterResources(&kbResources{svc: kb})
    srv.RegisterTool(&kbSearchTool{svc: kb})
    srv.RegisterTool(&memoryRecallTool{svc: mem})
    srv.RegisterTool(&memoryWriteTool{svc: mem})
    // memory_write 属于高风险操作,需触发人工审批
    srv.SetHITL(func(ctx context.Context, tool string, input json.RawMessage) (bool, string) {
        if highRisk[tool] && opts.HITL != nil {
            return opts.HITL.Decide(ctx, tool, input)
        }
        return true, ""
    })
}

两者对比总结:

维度eino-ext MCP 适配DeepFlux MCP 桥接
方向单向(入)双向(入 + 出)
职责协议转换(通用)业务语义暴露(领域特定)
暴露类型ToolTool + Resource + Prompt
安全控制HITL 审批高风险工具
传输层SSE / stdiostdio / SSE / Streamable HTTP

简而言之,eino-ext 解决的是“语言不通”的问题——MCP 与 Eino 接口不兼容;DeepFlux 解决的是“门没开”的问题——让外部工具能够访问平台自有数据。

小结

回到开篇的观点——MCP 协议其实并不复杂。底层依赖 JSON-RPC 2.0,核心只有三个方法(initialize / tools/list / tools/call),三种传输方式(stdio / SSE / Streamable HTTP),仅此而已。

eino-ext 的适配核心集中在 GetTools() 这 30 行代码:查询工具列表 → Schema 类型转换(通过 JSON 序列化绕一圈)→ 包装为 toolHelper 实现 Eino 接口。参数透传,结果透传,无冗余逻辑。

DeepFlux 在此基础上增加了反向能力,将 KB 和 Memory 通过 MCP 协议暴露,同时对高风险写操作引入了 HITL 审批机制。

协议桥接的真正价值在于生态复用:凡是遵守 MCP 协议的工具,无论由谁提供,接入 Eino 都使用同一套代码。这正是标准化的魅力——它让“连接”变得可预期、可复用。

下篇继续。

代码来源:cloudwego/eino-ext · cloudwego/eino-examples

来源:https://juejin.cn/post/7657147946866786313
上一篇PaddleOCR太慢?换成RapidOCR速度飙升实录 下一篇美团最新版大模型LongCat-2.0 2026年6月30日正式发布完整解读
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
批处理BAT入门教程第一篇
AI教程 · 2026-07-03

批处理BAT入门教程第一篇

提供13个批处理实战技巧,覆盖全盘查找并删除文件夹或文件、拷贝移动文件、创建畸形文件夹及设置隐藏属性等场景,可一键完成系统维护与文件管理工作,极大提升自动化操作效率和便捷性。

从零开始批处理命令For循环详解与实战案例
AI教程 · 2026-07-03

从零开始批处理命令For循环详解与实战案例

批处理For命令支持 d、 l、 r、 f四个参数。 d仅列出当前目录下的目录名; r递归搜索指定路径及其子目录中的文件; l生成数值序列; f可解析文件、字符串或命令输出,通过delims、tokens、skip、eol等选项灵活处理内容。

批评你的人是你生命中的贵人
AI教程 · 2026-07-03

批评你的人是你生命中的贵人

批评你的人往往最值得珍惜,因为他们关注你、助你成长。面对批评应包容反思,用行动改进而非辩解。接受批评是自我完善的过程,能让人少走弯路,避免重复犯错。这样的人正是生命中的贵人,值得感恩与珍惜。

测试人员角色定位与职责详解
AI教程 · 2026-07-03

测试人员角色定位与职责详解

测试人员角色经历了从找问题、保证质量到分析风险的转变,最终核心职责是提供关键信息,协助团队创造优秀产品。这包括识别问题、评估风险及帮助团队了解项目状态,而非单纯把关或追求完美。

经营成功测试生涯的实用方法与策略
AI教程 · 2026-07-03

经营成功测试生涯的实用方法与策略

一、测试生涯的起点 1989年,我在田纳西大学攻读研究生时,意外地从软件开发人员转行成为一名软件测试工程师。这并非我主动选择,说起来还有些戏剧性——某个早晨,教授质问我为何缺席那么多开发会议,我解释说这些会议总是安排在周末早上,对我这个第一次离家、刚入学的学生来说实在不便。结果呢?等待我的不是解聘通