
# Client Mode

Client mode is the mirror of [server mode](/en/docs/mcp-server): instead of an outside assistant calling your data, your own [agents](/en/docs/ai-agents) call tools hosted elsewhere — web search, document fetch, whatever an external MCP server publishes.

Unlike server mode, this one **requires `AI_PROVIDER`**. Sovrium is the caller here, so a model has to run somewhere on your side to decide when a tool is worth invoking.

## Declaring External Servers

The servers are operator configuration, not schema — a URL and a credential are deployment facts. `MCP_CLIENT_SERVERS` is a comma-separated list, and each entry's auth is configured by its **1-based position** in that list:

```bash
MCP_CLIENT_SERVERS=https://search.example.com/mcp,https://docs.example.com/mcp

MCP_AUTH_TYPE_1=bearer
MCP_AUTH_TOKEN_1=sk-...

MCP_AUTH_TYPE_2=header
MCP_AUTH_HEADER_2=X-Api-Key
MCP_AUTH_TOKEN_2=...
```

| Variable              | Values                     | Meaning                                                |
| --------------------- | -------------------------- | ------------------------------------------------------ |
| `MCP_CLIENT_SERVERS`  | comma-separated URLs       | The external servers. Unset means client mode is off.  |
| `MCP_AUTH_TYPE_{N}`   | `bearer`, `header`, `none` | How to authenticate to server _N_. Defaults to `none`. |
| `MCP_AUTH_TOKEN_{N}`  | string                     | The secret sent to server _N_.                         |
| `MCP_AUTH_HEADER_{N}` | header name                | Which header carries it, when type is `header`.        |

:::callout
**The index is positional, so reordering the list re-points your secrets.** Inserting a server at the front of `MCP_CLIENT_SERVERS` shifts every subsequent `_{N}` suffix by one, silently sending server 1's token to a different origin. Append rather than insert, and re-check the numbering whenever the list changes.
:::

## Scoping an Agent

An agent's `mcp` block is an allowlist over the discovered tool catalog. It answers "which of these may _this_ agent use", not "which servers exist".

```yaml
agents:
  - name: research-agent
    role: analyst
    systemPrompt: Research topics using approved external tools. Always cite sources.
    tools:
      tables: [findings]
      actions: [record.create, record.read]
    mcp:
      allowedTools: [web-search]
```

| Property           | Effect                                                                      |
| ------------------ | --------------------------------------------------------------------------- |
| `mcp.allowedTools` | External tool names this agent may invoke. Omitted means the whole catalog. |

Two allowlists are now in play and they do not overlap. `tools` scopes what the agent may do **inside** Sovrium — its tables and its [actions](/en/docs/agent-tools). `mcp.allowedTools` scopes what it may reach **outside**. An agent can be read-only internally and still search the web, or the reverse.

:::callout
**An unrecognised tool name is dropped, not rejected.** `allowedTools` is filtered against the discovered catalog, so a typo — `web_search` for `web-search` — silently yields an agent with no external tools rather than a validation error. Confirm the real names against `/api/ai/mcp/client/tools` before trusting an allowlist.
:::

## Inspecting What Is Available

Two read-only endpoints let you check the wiring without prompting a model. Both answer `404` with `{ "enabled": false }` when `MCP_CLIENT_SERVERS` is unset, so "disabled" is distinguishable from "route missing".

```bash
curl https://your-app.example.com/api/ai/mcp/client/status
```

```json
{
  "enabled": true,
  "servers": [
    { "url": "https://search.example.com/mcp", "authType": "bearer", "status": "connecting" }
  ]
}
```

Tokens are never echoed back — the summary carries the URL, the auth _type_, and the header name, and nothing secret.

```bash
curl https://your-app.example.com/api/ai/mcp/client/tools
```

The tool catalog seeds with `web-search` and `document-fetch`, and real `tools/list` discovery supersedes it once a configured server is reachable. Those seed names are what an `allowedTools` entry must match today.

## Calling an Agent

`POST /api/agents/{name}/chat` forwards a message to the provider along with that agent's filtered tool catalog, and returns a `{ "reply": … }` envelope. The runtime is deliberately tolerant: if an external server is unreachable or a tool call fails, the model still answers in text rather than the request erroring out. An agent that depends on a flaky server degrades to a worse answer, not to a 500.

## Related Pages

- [MCP Overview](/en/docs/mcp-integration) — server mode versus client mode.
- [Agents Overview](/en/docs/ai-agents) — the agents doing the calling.
- [Tools](/en/docs/agent-tools) — the internal allowlist this complements.
- [AI Providers](/en/docs/ai-providers) — the `AI_PROVIDER` client mode needs.
- [Server Mode](/en/docs/mcp-server) — the opposite direction.
