协议桥接插件

一个EMQX插件,用于桥接可流式HTTP或MCP-SSE到MCP-over-MQTT协议,支持HTTP MCP客户端与MQTT MCP客户端通信及自定义工具创建。

  • by emqx
  • Erlang
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 emqx/emqx_mcp_bridge

使用说明

MCP网桥插件

一个EMQX插件,通过MQTT将可流式HTTP或MCP-SSE桥接到MCP。

概述

此插件允许HTTP MCP客户端通过桥接两种协议,通过MQTT客户端与MCP通信。此插件还允许用户直接创建MCP工具并将其暴露给HTTP MCP客户端。

配置

这 listening_address 参数指定流式HTTP MCP客户端的地址和路径。这 sse_path 参数指定传统SSE MCP客户端的路径。两个端点都在同一台服务器上运行(主机和端口取自 listening_address).

listening_address = "http://0.0.0.0:9998/mcp"
sse_path = "/sse"

如果 sse_path 未配置,默认为 "/sse".如果 listening_address 未配置自定义路径,默认为 "/mcp".

它是如何工作的

从MCP服务器加载工具

当使用MQTT协议上的MCP的MCP服务器连接到EMQX时,插件通过两种方式从MCP服务器加载工具:

  • 如果MCP服务器报告“通知/服务器/在线”事件,且其“元数据”中有“工具”字段,则插件直接使用此字段中的工具列表。

  • 如果“通知/服务器/在线”事件中不存在“工具”字段,则插件会向MCP服务器发送“工具/列表”请求以检索工具列表。

按工具类型存储工具

工具使用其工具类型作为主键进行存储,其中工具类型是MQTT服务器上的MCP的ServerName。如果多个MCP服务器报告相同的ServerName,则插件仅保留最新MCP服务器的工具列表。

从MCP服务器加载工具后,插件按如下方式转换工具列表:

  • 它为每个工具名称添加工具类型前缀,从而形成“ToolType:ToolName”的格式。这避免了不同MCP服务器之间的工具名称冲突,并允许MCP-HTTP客户端按类型过滤工具。

  • 如果 get_target_clientid_from 设置为 tool_params,MCP桥注入一个名为 target-mqtt-client-id 进入每个工具。MCP-HTTP客户端在调用工具时必须提供此参数,插件使用其值将工具调用请求发送到指定的MQTT MCP服务器。

  • 如果 get_target_clientid_from 设置为 http_headers 或 jwt_claims,MCP-HTTP客户端不需要提供 target-mqtt-client-id 参数。相反,该插件从HTTP标头或JWT声明中获取目标MQTT客户端ID。此方法仅适用于MCP-HTTP客户端和MQTT MCP服务器之间存在一对一映射的情况,即每个MCP-HTTP客户机访问单个MQTT MCP Server上的工具。

仅列出特定的工具类型

当MCP-HTTP客户端请求工具列表时,它可以指定在响应中包含哪些工具类型。插件检索所需的 tool-types 基于HTTP标头或JWT声明 get_tool_types_from 配置。

当通过 tool-types 在HTTP标头中,客户端应使用 Tool-Types 带有逗号分隔的工具类型列表的标题。例如:

tool-types: type1,type2,type3

当通过 tool-types 在JWT声明中,客户端应包含一个名为 tool-types 带有工具类型列表。例如:

{
  "sub": "client1",
  "tool-types": ["type1", "type2", "type3"]
}

如果没有指定工具类型,插件将返回所有可用工具类型中的工具。

创建自定义MCP工具

要创建自定义MCP工具,用户需要创建一个带有前缀的模块 mcp_bridge_tools_ 并实现回调函数。该模块应该只有一个 -mcp_tool_type 属性指定工具类型,以及至少一个 -mcp_tool 属性来定义工具。查看模块 mcp_bridge_tools_clients 举个例子。

这是一个最小的示例模块,它导出一个添加两个数字的工具:

-module(mcp_bridge_tools_sample).
-export([add/3]).

-mcp_tool_type(<<"sample">>).
-mcp_tool_vsn(<<"1.0.0">>).
-mcp_tool(#{
    name => <<"add">>,
    title => <<"Add Tool">>,
    description => <<"Adds two numbers">>,
    inputSchema => #{
        type => <<"object">>,
        properties => #{
            num1 => #{
                type => <<"number">>,
                title => <<"First Number">>,
                description => <<"The first number to add">>
            },
            num2 => #{
                type => <<"number">>,
                title => <<"Second Number">>,
                description => <<"The second number to add">>
            }
        },
        required => [num1, num2]
    },
    opts => #{}
}).

add(_ReqId, #{<<"num1">> := Num1, <<"num2">> := Num2} = _Params, _Opts) ->
    {ok, Num1 + Num2}.

部署

看 EMQX文件 有关如何部署自定义插件的详细信息。