> 原始 Markdown 孪生体（构建期从源 Markdown 生成）。渲染页：https://docs.gatellm.io/zh-CN/quickstart/mcp-proxy · 文档索引：https://docs.gatellm.io/zh-CN/llms.txt


# MCP 代理外部工具

MCP（Model Context Protocol）网关把外部工具服务器聚合成统一入口：调用方用一把网关访问密钥就能调多个工具，且支持按密钥组做工具 ACL 与上下文预算。本章带你添加一个外部 MCP 服务器（filesystem MCP 示例），用 Claude Desktop 经网关 `/mcp` 端点调用，看到工具被代理。

## 你将完成什么

- 一个外部 MCP 服务器接入网关（如 filesystem MCP）
- 一把网关访问密钥，能从任意 MCP 客户端调用网关
- 用 Claude Desktop 经网关调用工具，看到工具列表 + 调用结果

## 前置

- 网关已运行（`http://localhost:7890`），能登录控制台
- 已设 `ENCRYPTION_KEY`——下面要保存的访问密钥落库前加密，没设会在保存时报 `encryption_key not set in config`。见 [Docker 单机跑通 → 前置](/zh-CN/quickstart/docker-single-node.md#prereq)
- 一个运行中的外部 MCP 服务器，**streamable HTTP 或 SSE 传输**（下文以 `http://localhost:8000/mcp` 为例）。

  > 网关**不支持** stdio / command / args / env / OAuth 形式的 MCP 服务器，仅 streamable HTTP / SSE / REST-bridge 三种传输。官方 `@modelcontextprotocol/server-*` 多为 stdio，不能直接接入。
  >
  > **没有现成的 HTTP MCP 服务器？** 用桥接工具把 stdio 服务器包成 streamable HTTP 即可。例如用 [supergateway](https://github.com/supercorp-ai/supergateway) 桥 filesystem 服务器（一条命令起在 8000 端口、路径 `/mcp`，与下文示例一致）：
  >
  > ```bash
  > npx -y supergateway \
  >   --stdio "npx -y @modelcontextprotocol/server-filesystem /tmp" \
  >   --outputTransport streamableHttp
  > ```
  >
  > 起好后端点就是 `http://localhost:8000/mcp`。用别的 HTTP/SSE MCP 服务器同理，把下文端点换成它的实际地址即可。

## 1. 添加 MCP 服务器

控制台 → **MCP 服务器** → **新建**：

| 字段 | 值 | 说明 |
|------|-----|------|
| 名称 | `files` | 唯一，作工具前缀，正则 `^[a-z0-9][a-z0-9_-]*$` |
| 描述 | filesystem MCP | UI 展示用 |
| 传输 transport | `streamable_http` | 或 `sse` |
| 连接类型 connection_type | `stateless` | 无状态工具服务器 |
| 端点 endpoint | `http://localhost:8000/mcp` | 上游工具服务器 URL（streamable HTTP 填到具体路径） |
| 认证头 auth_header | （上游 MCP 不要求认证则留空） | 上游 Authorization 头（如 `Bearer ...`） |
| 标签 tags | `["dev-tools"]` | 粗粒度 ACL 过滤 |
| 优先级 priority | `0` | 0–999，小=高优先；上下文预算裁剪时按此 |
| 空闲超时 idle_timeout_secs | `300` | 默认 300 |
| 延迟加载 defer_loading | `false`（默认） | 首次调用才加载工具 schema 时设 true |
| 健康检查 health_check | （可选）如 `{ "path": "/health", "interval_secs": 60, "timeout_secs": 5 }` | 配置后定期探测存活。**`path` 必须是上游真实提供的端点**——上游没有 `/health` 却填了它，探测会一直失败、服务器被判死、工具不可见。不确定就留空 |
| 并发上限 max_concurrency | （按需） | 每访问密钥对该服务器的并发上限 |
| 限流 rate_limit | （按需） | `requests_per_second` / `tokens_per_minute` |
| 启用 | ✓ | |

保存。工具名格式是 `{服务器名}.{工具名}`，例如 `files.read_file`、`files.list_directory`。

## 2. 配密钥组 MCP ACL

工具权限配在**密钥组**的 `mcp_tool_acl`（不是配在 MCP 服务器）。控制台 → **访问密钥** → **密钥组** → 编辑 → MCP 子页签（仅当至少存在一个 MCP 服务器时显示）：

| 字段 | 值 |
|------|-----|
| `allowed_tools` | `["files.*"]`（放行 files 服务器全部工具） |
| `denied_tools` | （留空） |
| `allowed_tags` | （留空） |

```json
{
  "name": "default",
  "models": ["*"],
  "mcp_tool_acl": {
    "allowed_tools": ["files.*"],
    "denied_tools": []
  }
}
```

ACL 评估顺序：`denied`（最高）→ `allowed_tools` → `allowed_tags` → 默认。末尾的「默认」是**放行**：当 `allowed_tools` 和 `allowed_tags` 都为空时，只要没被 `denied_tools` 命中就放行；一旦配了任一 allow 清单，就切换成白名单模式，只对命中者放行。

## 3. 签发访问密钥

控制台 → **访问密钥** → **新建**：

| 字段 | 值 | 说明 |
|------|-----|------|
| 名称 | `my-mcp-key` | 密钥的标识，用于管理与审计 |
| API 密钥 | 点「生成」 | 自动生成一串，客户端调用时带的凭证 |
| 分组 | `default` | 决定这把密钥能访问哪些模型 |
| 启用 | ✓ | |

## 4. 用 Claude Desktop 调用

Claude Desktop 的 MCP 配置（macOS 路径 `~/Library/Application Support/Claude/claude_desktop_config.json`）：

```json
{
  "mcpServers": {
    "gatellm": {
      "url": "http://localhost:7890/mcp",
      "headers": {
        "Authorization": "Bearer <你的访问密钥>"
      }
    }
  }
}
```

重启 Claude Desktop，在对话框里问 Claude「列出 /tmp 目录」，Claude 会自动调用 `files.list_directory` 工具，网关代理请求到上游 MCP 服务器。

## 5. 或用 Python mcp SDK

```python
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    async with streamablehttp_client(
        "http://localhost:7890/mcp",
        headers={"Authorization": "Bearer <你的访问密钥>"}
    ) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print([t.name for t in tools.tools])
            result = await session.call_tool("files.list_directory", {"path": "/tmp"})
            print(result)

asyncio.run(main())
```

## JSON-RPC 方法

网关 `/mcp` 端点支持：

| 方法 | 说明 |
|------|------|
| `initialize` | 握手，交换能力 |
| `tools/list` | 列出可用工具（按该密钥组的 MCP ACL 过滤），含内置 `search_tools` 元工具 |
| `tools/call` | 调用工具，命名格式 `{server_name}.{tool_name}` |
| `tools/search` | BM25 关键词检索工具（工具多时用于定位） |

## 常见问题

**Q：Claude Desktop 看不到工具列表？**
检查：① MCP 服务器是否启用；② `defer_loading` 是否开了（首次调用才加载 schema，要触发一次 `tools/list`）；③ 密钥组的 `mcp_tool_acl` 是否把它过滤掉了；④ 上游 MCP 服务器是否真的存活；⑤ 若配了 `health_check`，`path` 是否是上游真实端点——填了上游没有的路径，探测会一直失败、服务器被判死、工具就不可见（不确定就把 `health_check` 留空）。

**Q：工具调用经常超时？**
调该服务器的 `max_concurrency`（并发上限）和 `rate_limit`，或检查上游 MCP 服务器本身的响应速度。每个 MCP 服务器有独立熔断保护——连续失败会**触发熔断、暂停向该服务器转发**（不是「临时开放」），恢复健康后自动放行。

**Q：工具名带 `.` 前缀（如 `files.read_file`）是什么意思？**
是 `{服务器名}.{工具名}` 两段式命名，避免不同服务器的同名工具冲突。

**Q：怎么让某个密钥组完全不能用 MCP？**
密钥组 MCP 子页签里 `allowed_tools` 留空、`allowed_tags` 留空，或不勾选任何工具。

**Q：MCP 服务器挂了会影响其他工具吗？**
不会。每个服务器有独立的连接管理和熔断保护。一个服务器不可用时，其工具从列表中排除，其他服务器正常工作。

**下一步**：[接入外部 MCP 服务器](/zh-CN/howto/onboard-external-mcp.md) 看完整操作（添加/列表/详情/删除）；[MCP 配置](/zh-CN/reference/mcp-config.md) 看工具 ACL、上下文预算、REST bridge 等字段细节；[端点 · 认证 · 协议互通](/zh-CN/reference/endpoints.md) 看完整端点清单。
