MCP网关服务器

MCP网关是一个反向代理服务器,通过统一入口转发客户端请求至MCP服务器或聚合多个MCP服务器的功能。支持SSE和Streamable HTTP两种传输协议,提供动态能力聚合和OAuth 2.1认证。

  • by lucky-aeon
  • Go
  • 本地部署
  • 34 星标
  • 更新于
在 GitHub 查看 lucky-aeon/mcp-gateway

使用说明

MCP网关

描述

MCP网关是一个反向代理服务器,它将客户端的请求转发到MCP服务器,或者通过统一的门户使用网关下的所有MCP服务器。

支持两种传输协议(启动时可切换):

  • 上海证券交易所 (默认,传统MCP传输)
  • 可流式传输的HTTP (MCP规范 2025-03-26)

特性

  • 部署多个MCP服务器
  • 连接到MCP服务器
  • 使用网关呼叫MCP服务器
  • 获取所有MCP服务器的SSE流
  • 获取所有MCP服务器的工具
  • 可流式HTTP聚合端点,通过会话管理 Mcp-Session-Id 头球
  • 动态能力聚合(网关仅通告至少一个下游MCP支持的能力)
  • MCP OAuth 2.1资源服务器身份验证与受保护资源元数据发现

安装

  1. 拉取github包
docker pull ghcr.io/lucky-aeon/mcp-gateway:latest
  1. 自建docker镜像
docker build -t mcp-gateway .

用法

运行github docker容器

docker run -d --name mcp-gateway -p 8080:8080 ghcr.io/lucky-aeon/mcp-gateway

运行自建docker容器

docker run -d --name mcp-gateway -p 8080:8080 mcp-gateway

配置

网关读取 config.json 从配置目录(默认为 ./vm 如果存在,否则 .).一个最小的例子:

{
    "LogLevel": 0,
    "Bind": "[::]:8080",
    "Auth": {
        "Enabled": true,
        "AuthorizationServers": ["https://auth.example.com"],
        "TokenIssuer": "https://auth.example.com",
        "TokenJWKSURI": "https://auth.example.com/.well-known/jwks.json",
        "TokenAudience": "http://localhost:8080/stream",
        "RequiredScopes": ["mcp:read"],
        "ScopesSupported": ["mcp:read"]
    },
    "GatewayProtocol": "all",
    "McpServiceMgrConfig": {
        "McpServiceRetryCount": 3
    }
}

关键字段:

字段 默认值 描述
Bind [::]:8080 服务器侦听地址。
GatewayProtocol all 暴露的网关协议: all, sse,或 streamhttp.也可通过 --protocol 旗帜。
Auth.Enabled true 是否强制执行身份验证。集 false 用于本地未经身份验证的MCP使用。
Auth.ApiKey 123456 传统的单密钥管理API登录令牌;未用作MCP身份验证。
Auth.Mode single-key single-key 或 saasSaaS模式使用MCP网关帐户和密码登录。
Auth.AuthorizationServers empty 可选的外部OAuth颁发者URL。在SaaS模式下为空意味着网关将自己宣传为授权服务器。
Auth.TokenIssuer 第一个授权服务器 需要 iss MCP OAuth访问令牌的索赔。
Auth.TokenJWKSURI 从发行者处发现 用于验证JWT访问令牌的JWKS URL。
Auth.TokenIntrospectionURL 空 不透明访问令牌的RFC 7662自检端点。如果设置,则使用内省而不是JWKS。
Auth.TokenAudience 请求资源URL 需要 aud 索赔。配置到授权服务器使用的MCP资源指示器。
Auth.RequiredScopes 空 MCP请求所需的范围,并在 WWW-Authenticate.
Auth.ScopesSupported 空 受保护资源元数据中通告的作用域。
SessionGCInterval 10s 垃圾收集空闲代理会话的间隔。
ProxySessionTimeout 1m GC前空闲代理会话超时。
McpServiceMgrConfig.McpServiceRetryCount 3 标记失败的MCP服务之前的最大重试次数 failed.

选择网关协议

任一组 GatewayProtocol 在 config.json:

{ "GatewayProtocol": "all" }

或者传递CLI标志(优先):

./mcp-gateway --protocol=streamhttp

有效值: all (默认), sse,或 streamhttp.

认证

当 Auth.Enabled 是 true,每个MCP协议请求都必须提供一个承载令牌:

Authorization: Bearer <access-token>

网关不再处理 api_key, sessionId, Mcp-Session-Id,或 X-Session-Id 作为身份验证凭证。 Mcp-Session-Id 仍然是传输会话标识符,必须在经过身份验证的Streamable HTTP请求上与Bearer令牌一起发送。

对于MCP OAuth发现,网关公开OAuth受保护的资源元数据:

GET /.well-known/oauth-protected-resource
GET /.well-known/oauth-protected-resource/stream

未经授权的MCP请求返回 401 带着一个 WWW-Authenticate: Bearer ... resource_metadata="..." OAuth发现可用时的挑战。对于本地未经身份验证的开发,设置 Auth.Enabled 到 false.

在 Auth.Mode = "saas" 无外部 Auth.AuthorizationServers,网关使用自己的帐户系统登录MCP。Discovery将网关源作为授权服务器进行公告,并公开:

GET /.well-known/oauth-authorization-server
POST /oauth/token
POST /oauth/register

/oauth/token 接受已编码的表单 grant_type=password 随着 username/password 并返回与所使用的网关JWT相同的网关 /api/v1/auth/login.配置 Auth.AuthorizationServers 仅当您需要Keycloak、Auth0或其他外部OAuth提供程序时。

基于浏览器的MCP客户端还可以使用公告的授权端点:

GET /oauth/authorize

内置的授权端点呈现网关帐户登录表单,并完成OAuth授权代码流,包括PKCE。 /oauth/register 为MCP Inspector等客户端实现了最小的动态客户端注册。

API

部署

支持:uvx、npx。或sse url

POST /deploy HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "mcpServers": {
        "time": {
            "url": "http://mcp-server:8080",  // url 和 command 二选一
            "command": "uvx",  // url 和 command 二选一
            "args": ["mcp-server-time", "--local-timezone=America/New_York"],  // 可选,command 的参数
            "env": {  // 可选,环境变量
                "KEY1": "VALUE1",
                "KEY2": "VALUE2"
            }
        }
    }
}

使用MCP(SSE模式)

可用时间: GatewayProtocol 是 all (默认)或 sse.

得到SSE

GET /{mcp-server-name}/sse HTTP/1.1
Host: localhost:8080

发布消息

POST /{mcp-server-name}/message HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "method": "tools/call",
    "params": {
        "name": "get_current_time",
        "arguments": {
            "timezone": "Asia/Seoul"
        }
    },
    "jsonrpc": "2.0",
    "id": 2
}

使用网关(SSE模式)

可用时间: GatewayProtocol 是 all (默认)或 sse.

网关和直连MCP的区别在于,只需要与网关交互,网关会自动将请求转发到对应的MCP服务器。在call 时,需要在method前面添加 mcpServerName 内容,标识该请求来自哪个 MCP 服务器。

得到SSE

GET /sse HTTP/1.1
Host: localhost:8080

这里 sse 是整个网关下所有的 MCP 服务器的 SSE 流。

当客户端订阅 sse 时,网关会为每个 MCP 服务器创建一个 SSE 连接,并将所有 MCP 服务器的 SSE 流合并到一起。

在响应的所有tools/call 的结果中,会在method前面添加 mcpServerName 内容,标识该结果来自哪个 MCP 服务器。

发布消息

POST /message HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "method": "tools/call",
    "params": {
        "name": "{mcp-server-name}-get_current_time",
        "arguments": {
            "timezone": "Asia/Seoul"
        }
    },
    "jsonrpc": "2.0",
    "id": 2
}

获取网关下所有工具

POST /message HTTP/1.1
Host: localhost:8080
Content-Type: application/json

{
    "method": "tools/list",
    "jsonrpc": "2.0",
    "id": 1
}

# SSE 响应 message event

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "{mcpServerName}-get_current_time",
        "description": "Get current time in a specific timezones",
        "inputSchema": {
          "type": "object",
          "properties": {
            "timezone": {
              "type": "string",
              "description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
            }
          },
          "required": [
            "timezone"
          ]
        }
      },
      {
        "name": "{mcpServerName}-convert_time",
        "description": "Convert time between timezones",
        "inputSchema": {
          "type": "object",
          "properties": {
            "source_timezone": {
              "type": "string",
              "description": "Source IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no source timezone provided by the user."
            },
            "time": {
              "type": "string",
              "description": "Time to convert in 24-hour format (HH:MM)"
            },
            "target_timezone": {
              "type": "string",
              "description": "Target IANA timezone name (e.g., 'Asia/Tokyo', 'America/San_Francisco'). Use 'America/New_York' as local timezone if no target timezone provided by the user."
            }
          },
          "required": [
            "source_timezone",
            "time",
            "target_timezone"
          ]
        }
      }
    ]
  }
}

使用网关(流式HTTP模式)

可用时间: GatewayProtocol 是 all (默认)或 streamhttp.

实现规范中定义的MCP流式HTTP传输 2025-03-26网关公开单个聚合端点 /stream 接受 POST, GET 和 DELETE会话标识符在 Mcp-Session-Id HTTP标头。

1.建立会话(初始化)

POST /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Accept: application/json, text/event-stream
Content-Type: application/json

{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
        "protocolVersion": "2025-03-26",
        "capabilities": {},
        "clientInfo": {"name": "my-client", "version": "1.0.0"}
    }
}

答复:

HTTP/1.1 200 OK
Content-Type: application/json
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "protocolVersion": "2025-03-26",
        "serverInfo": {"name": "mcp-gateway", "version": "1.0.0"},
        "capabilities": { /* OR-merged from all downstream MCP servers */ },
        "instructions": "MCP Gateway aggregates multiple MCP servers. Tools are namespaced as <serverName>_<toolName>."
    }
}

保留退回的 Mcp-Session-Id 并在每次后续请求时发送它。

2.完成握手(通知)

POST /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Content-Type: application/json
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

{"jsonrpc": "2.0", "method": "notifications/initialized"}

答复: 202 Accepted (空的身体)。

3.调用工具或列出资源

POST /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Accept: application/json, text/event-stream
Content-Type: application/json
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
        "name": "{mcp-server-name}_get_current_time",
        "arguments": {"timezone": "Asia/Seoul"}
    }
}

聚合工具名称遵循以下模式 <serverName>_<toolName>,规则与SSE网关模式相同。

响应同步到达HTTP响应体:

{"jsonrpc": "2.0", "id": 2, "result": { /* ... */ }}

通知(JSON-RPC消息,无 id)答案是 202 Accepted 并异步转发。

4.订阅服务器发起的事件(可选)

GET /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Accept: text/event-stream
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

网关保持连接打开并发出 event: message 服务器框架→ 客户端JSON-RPC 请求: 和 通知 (例如进度更新、日志消息)。JSON-RPC 回应 永远不会被推送到这里——它们会在发起的HTTP响应中返回 POST /stream 请求。

以开头的行 : 是SSE保持活动的评论,可以忽略。

5.结束会议

DELETE /stream HTTP/1.1
Host: localhost:8080
Authorization: Bearer <access-token>
Mcp-Session-Id: 7782f2f9-563c-4379-b961-df06e49e54c0

答复: 200 OK.

单服务器直通

在Streamable HTTP模式下,您还可以直接访问单个MCP服务器:

POST /{mcp-server-name} HTTP/1.1
GET  /{mcp-server-name} HTTP/1.1

网关将请求转发给目标MCP message 终点。此模式下的会话管理由下游服务器负责。

与MCP检查员联系

  1. 在检查器中选择 传输类型: Streamable HTTP.
  2. 网址: http://localhost:8080/stream.
  3. 在您的授权服务器中完成OAuth流程,并提供 Authorization: Bearer <access-token>.
  4. 点击 连接.检查员负责处理 Mcp-Session-Id 自动交换。