> Raw Markdown twin (generated at build time from the source Markdown). Rendered page: https://docs.gatellm.io/en/reference/mcp-config · Doc index: https://docs.gatellm.io/en/llms.txt


# MCP configuration

The MCP (Model Context Protocol) gateway aggregates external tool servers into one entry point: callers use a single gateway access key to call multiple tools, with per-key-group tool ACL and context budget.

For the steps to onboard external MCP servers see [Onboard external MCP servers](/en/howto/onboard-external-mcp.md); for a quick working example see [Proxy external tools through MCP](/en/quickstart/mcp-proxy.md). This chapter is the configuration reference.

## Clients calling MCP

Callers point their MCP client at the gateway:

- **Streamable HTTP**: `POST http://<host>:7890/mcp` (JSON-RPC 2.0)
- **SSE transport**: `GET http://<host>:7890/mcp/sse`
- **Auth**: `Authorization: Bearer <your access key>`

### JSON-RPC methods

| 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, naming format `{server_name}.{tool_name}` |
| `tools/search` | BM25 keyword search (for locating tools when there are many) |

Tool names are two-part `{server name}.{tool name}`. For example `github.list_repos`, `slack.send-message`.

## MCP server fields

| Field | Description |
|------|------|
| Name | Unique, used as the tool prefix, regex `^[a-z0-9][a-z0-9_-]*$` |
| Description | UI display |
| Transport transport | `streamable_http` (default) / `sse` |
| Connection type connection_type | `stateful` / `stateless` / `rest_bridge` (REST bridge) |
| Endpoint endpoint | The upstream tool server URL |
| Auth header auth_header | The upstream Authorization header (e.g. `Bearer ...`) |
| Extra headers extra_headers | Additional request headers |
| Tags tags | Coarse-grained ACL filtering |
| Priority priority | 0–999, smaller = higher priority; used for context-budget trimming |
| Idle timeout idle_timeout_secs | default 300 |
| Lazy loading defer_loading | load tool schema only on first call |
| Health check health_check | path / interval (default 60s) / timeout (default 5s) |
| Concurrency cap max_concurrency | per-access-key concurrency cap for that server |
| Rate limit rate_limit | `requests_per_second` / `tokens_per_minute` |
| extra_config | REST bridge needs the simplified REST spec JSON here (`{title, version, operations[]}`) |
| Enabled | |

> Transport has only `streamable_http` / `sse`; REST onboarding is via connection type `connection_type = rest_bridge` (it's a connection type, not a transport). stdio / command / args / env / OAuth forms of MCP servers are **not** supported.

## Choosing a connection type

| Type | Use case |
|------|---------|
| `stateless` (default) | Stateless tool server, each request independent |
| `stateful` | Stateful tool server, maintains a session |
| `rest_bridge` | Wrap an ordinary REST API as an MCP tool |

## Lazy loading

When `defer_loading = true` is set, the server loads tool schema only on the first `tools/list` or `tools/call`. Suitable for:
- Many tool servers but most are rarely used
- Reducing startup time

## Health check

When configured, the gateway periodically probes whether the tool server is alive:

```json
{
  "health_check": {
    "path": "/health",
    "interval_secs": 60,
    "timeout_secs": 5
  }
}
```

`path` is configurable but **must be an endpoint the upstream actually provides** (if the upstream has no `/health` but you fill it, probes keep failing, the server is judged dead, and its tools become invisible); `interval_secs` defaults to 60, `timeout_secs` defaults to 5.

> ⚠️ `health_check` (and `max_concurrency` / `rate_limit`) are currently not in the console's "New MCP server" form — they are Console API (PUT `/console/api/mcp-servers/{name}`) fields; the UI form doesn't render them. Set them via the Console API when needed.

Unhealthy servers' tools are excluded from `tools/list`, and repeated call failures **trigger circuit breaking, pausing forwarding to that server** (not "temporarily opening"); once healthy, it's automatically allowed again.

## REST bridge

Wrap an ordinary REST API as an MCP tool: `connection_type = rest_bridge`, provide the simplified REST spec JSON (`{title, version, operations[]}`) in `extra_config`; the gateway auto-generates the tool definition per the spec.

**Note**: the spec is a simplified operation list (`{title, version, operations[]}`), not the standard OpenAPI 3.x `paths` shape, and Swagger 2.0 is not supported either.

Console → MCP servers → New, fill in: **Name**=`weather-api`, **Connection type**=`REST Bridge` (selecting it expands the "OpenAPI spec (JSON)" editor), **Endpoint**=`https://api.weather.example.com`; paste the spec into the "OpenAPI spec (JSON)" editor — it is the simplified `{title, version, operations[]}` structure (each operation contains `operation_id` / `description` / `method` / `path` / `parameters_schema`), not the standard OpenAPI 3.x `paths` shape, for example:

```json
{
  "title": "Weather API",
  "version": "1.0.0",
  "operations": [
    {
      "operation_id": "getForecast",
      "description": "Get weather forecast",
      "method": "GET",
      "path": "/forecast",
      "parameters_schema": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    }
  ]
}
```

Once configured, clients can call `weather-api.getForecast` via `tools/call`, and the gateway automatically translates the MCP call into a REST request.

## Tool ACL

Tool permissions are configured on 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 | Description |
|------|------|
| `allowed_tools` | Allowed tools, supports wildcards `server.*` / `*.tool` |
| `denied_tools` | Denied tools (highest priority) |
| `allowed_tags` | Allowed server tags |

### Evaluation order

`denied` (highest) → `allowed_tools` → `allowed_tags` → default

i.e. first check whether denied, then whether allowed.

### ACL examples

**Allow all github tools + slack message-send, deny channel deletion**:
In the key group's **MCP** sub-tab: **Allowed MCP tools** check `github.*` (whole-service grant). The original example also wants "only allow `slack.send-message`, only deny `slack.delete-channel`" — this **single-tool-level** ACL can't be done in the console UI (the allow/deny selectors only offer whole-service `server.*` switches, plus one extra `*` all-tools for allow), so it must use the Console API's `allowed_tools` / `denied_tools` to fill precise tool names.

**Only allow servers of specific tags**:
In the key group's **MCP** sub-tab, **Allowed MCP tags** fill `internal, read-only` (comma-separated free text).

**Deny all MCP tools**:
Leave both `allowed_tools` and `allowed_tags` empty.

## Context budget and priority

When there are many tools and a tight context window, the gateway trims tool descriptions by `priority`:

- Servers with a smaller `priority` are kept first (0 = highest priority)
- Lower-priority tool descriptions may be omitted, but `tools/search` can still find them

Suitable for many tools (100+) but a limited context window.

## Complete example

For a complete walkthrough of aggregating two MCP servers (github + slack) with per-group authorization, see the complete example in [Onboard external MCP servers](/en/howto/onboard-external-mcp.md). This page only covers the MCP config fields.

::: tip Single-tool-level ACL
The console UI's allow/deny selectors only offer whole-service `server.*` switches; single-tool-level ACL (e.g. allow `slack.send-message` but deny `slack.delete-channel`) must use the Console API's `allowed_tools` / `denied_tools` to fill precise tool names.
:::

## FAQ

**Q: I can't see the tools of a just-added server in the tool list?**
Check: ① whether the server is enabled; ② whether `defer_loading` is on (schema loads on first call); ③ whether the key group's `mcp_tool_acl` filters it out.

**Q: REST bridge reports the spec unsupported?**
The spec is the simplified `{title, version, operations[]}` structure, not the standard OpenAPI 3.x `paths` shape, and Swagger 2.0 is not supported either. Rewrite the spec as `operations[]`: each operation contains `operation_id` / `description` / `method` / `path` / `parameters_schema`.

**Q: Tool calls frequently time out?**
Tune that server's `max_concurrency` (concurrency cap) and `rate_limit`, or check the upstream tool server's own response speed. Each MCP server has independent circuit-breaker protection — repeated failures **trigger circuit breaking and pause forwarding** (not "temporarily opening"); once healthy, it's automatically allowed again.

**Q: How do I completely disable MCP for a key group?**
Leave `allowed_tools` and `allowed_tags` empty in the key group's MCP sub-tab, or check no tools.

**Q: Does one MCP server going down 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 work normally.

**Q: What is tools/search?**
A built-in meta-tool that searches all available tools with BM25 keywords. Used to quickly locate a target tool when there are many (50+).

**Next**: [Onboard external MCP servers](/en/howto/onboard-external-mcp.md) for steps; [MCP proxy quickstart](/en/quickstart/mcp-proxy.md) for a quick working example; [Access key and key group fields](/en/reference/access-keys-groups-fields.md) for the key group's MCP dimension; [Environment variable reference](/en/reference/configuration.md) for all environment variables.
