Proxying external tools with MCP
The MCP (Model Context Protocol) gateway aggregates external tool servers into a single entry point: callers use one gateway access key to call multiple tools, and it supports tool ACLs and context budgets per key group. This chapter walks you through adding an external MCP server (a filesystem MCP example) and calling it from Claude Desktop via the gateway's /mcp endpoint to see the tools proxied.
What you'll build
- An external MCP server connected to the gateway (e.g. filesystem MCP)
- A gateway access key that can call the gateway from any MCP client
- Call tools via the gateway from Claude Desktop and see the tool list + call results
Prerequisites
The gateway is running (
http://localhost:7890) and you can log in to the ConsoleENCRYPTION_KEYis set — the access key saved below is encrypted before being stored; if it's not set, saving reportsencryption_key not set in config. See Docker single node → PrerequisitesA running external MCP server using streamable HTTP or SSE transport (the example below uses
http://localhost:8000/mcp).The gateway does not support stdio / command / args / env / OAuth style MCP servers — only streamable HTTP / SSE / REST-bridge transports. The official
@modelcontextprotocol/server-*packages are mostly stdio and cannot be connected directly.No ready-made HTTP MCP server? Wrap a stdio server into streamable HTTP with a bridge tool. For example, use supergateway to bridge the filesystem server (one command starts it on port 8000 at path
/mcp, matching the example below):bashnpx -y supergateway \ --stdio "npx -y @modelcontextprotocol/server-filesystem /tmp" \ --outputTransport streamableHttpOnce started, the endpoint is
http://localhost:8000/mcp. It's the same with other HTTP/SSE MCP servers — just replace the endpoint below with its actual address.
1. Add an MCP server
Console → MCP Servers → New:
| Field | Value | Description |
|---|---|---|
| Name | files | Unique; used as the tool prefix, regex ^[a-z0-9][a-z0-9_-]*$ |
| Description | filesystem MCP | For UI display |
| Transport (transport) | streamable_http | Or sse |
| Connection type (connection_type) | stateless | Stateless tool server |
| Endpoint (endpoint) | http://localhost:8000/mcp | The upstream tool server URL (for streamable HTTP, fill in the specific path) |
| Auth header (auth_header) | (leave empty if the upstream MCP requires no auth) | The upstream Authorization header (e.g. Bearer ...) |
| Tags (tags) | ["dev-tools"] | Coarse-grained ACL filtering |
| Priority (priority) | 0 | 0–999, smaller = higher priority; used for context-budget trimming |
| Idle timeout (idle_timeout_secs) | 300 | Default 300 |
| Deferred loading (defer_loading) | false (default) | Set to true to load the tool schema only on first call |
| Health check (health_check) | (optional) e.g. { "path": "/health", "interval_secs": 60, "timeout_secs": 5 } | Probes liveness periodically when configured. path must be an endpoint the upstream actually provides — if the upstream has no /health but you set it, probes keep failing, the server is judged dead, and its tools become invisible. Leave empty if unsure |
| Concurrency cap (max_concurrency) | (as needed) | Per-access-key concurrency cap for this server |
| Rate limit (rate_limit) | (as needed) | requests_per_second / tokens_per_minute |
| Enabled | ✓ |
Save. The tool name format is {server_name}.{tool_name}, e.g. files.read_file, files.list_directory.
2. Configure the key group's MCP ACL
Tool permissions are configured in the key group's mcp_tool_acl (not on the MCP server). Console → Access Keys → Key Groups → Edit → MCP sub-tab (shown only when at least one MCP server exists):
| Field | Value |
|---|---|
allowed_tools | ["files.*"] (allow all tools of the files server) |
denied_tools | (leave empty) |
allowed_tags | (leave empty) |
{
"name": "default",
"models": ["*"],
"mcp_tool_acl": {
"allowed_tools": ["files.*"],
"denied_tools": []
}
}ACL evaluation order: denied (highest) → allowed_tools → allowed_tags → default. The "default" at the end is allow: when both allowed_tools and allowed_tags are empty, anything not hit by denied_tools is allowed; once any allow list is configured, it switches to whitelist mode, allowing only hits.
3. Issue an access key
Console → Access Keys → New:
| Field | Value | Description |
|---|---|---|
| Name | my-mcp-key | The key's identifier, used for management and audit |
| API key | Click "Generate" | Auto-generates a string; the credential clients send when calling |
| Group | default | Determines which models this key can access |
| Enabled | ✓ |
4. Call from Claude Desktop
Claude Desktop's MCP configuration (macOS path ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"gatellm": {
"url": "http://localhost:7890/mcp",
"headers": {
"Authorization": "Bearer <your access key>"
}
}
}
}Restart Claude Desktop and ask Claude in the chat to "list the /tmp directory"; Claude will automatically call the files.list_directory tool, and the gateway proxies the request to the upstream MCP server.
5. Or use the Python mcp SDK
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 <your access key>"}
) 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 methods
The gateway's /mcp endpoint supports:
| Method | Description |
|---|---|
initialize | Handshake, exchange capabilities |
tools/list | List available tools (filtered by that key group's MCP ACL), including the built-in search_tools meta-tool |
tools/call | Call a tool, named as {server_name}.{tool_name} |
tools/search | BM25 keyword search over tools (for locating when there are many tools) |
FAQ
Q: Claude Desktop doesn't show the tool list? Check: ① whether the MCP server is enabled; ② whether defer_loading is on (the schema loads only on first call, so you need to trigger a tools/list); ③ whether the key group's mcp_tool_acl filtered it out; ④ whether the upstream MCP server is actually alive; ⑤ if health_check is configured, whether path is a real upstream endpoint — if you set a path the upstream doesn't have, probes keep failing, the server is judged dead, and its tools become invisible (leave health_check empty if unsure).
Q: Tool calls often time out? Adjust that server's max_concurrency (concurrency cap) and rate_limit, or check the upstream MCP server's own response speed. Each MCP server has independent circuit-breaker protection — consecutive failures trip the breaker and pause forwarding to that server (not "temporarily open"), and it's automatically allowed again after health recovers.
Q: What does the . prefix in a tool name (e.g. files.read_file) mean? It's the two-part {server_name}.{tool_name} naming, avoiding collisions between same-named tools of different servers.
Q: How do I make a key group unable to use MCP at all? In the key group's MCP sub-tab, leave allowed_tools empty and allowed_tags empty, or don't check any tools.
Q: If an MCP server goes down, does it affect other tools? No. Each server has independent connection management and circuit-breaker protection. When one server is unavailable, its tools are excluded from the list and other servers keep working.
Next: Onboard an external MCP server for the full operations (add/list/detail/delete); MCP configuration for tool ACL, context budget, REST bridge, and other field details; Endpoints · auth · protocol interop for the full endpoint list.
