---
icon: lucide/plug
---

# MCP

Model Context Protocol (MCP) is a standard way for AI applications to connect to external tool servers.
MindRoom's Phase 1 MCP support acts as an MCP client for tools.
It connects to configured servers, discovers their tool catalogs, and exposes those tools to agents.
MindRoom does not yet consume MCP resources or prompts.

## Configuration Overview

Configure MCP servers in the top-level `mcp_servers` block in `config.yaml`.
Each key is the server ID.
Server IDs must use only letters, numbers, and underscores because MindRoom uses them in tool names.

```yaml
mcp_servers:
  chrome_devtools:
    transport: stdio
    command: npx
    args:
      - -y
      - chrome-devtools-mcp@latest
```

Each configured server creates one dynamic MindRoom tool named `mcp_<server_id>`.
Add that tool name to an agent's `tools:` list to let the agent use the server's discovered MCP tools.

## Transport Types

MindRoom supports three MCP transport types:

- `stdio`
- `sse`
- `streamable-http`

### `stdio`

Use `stdio` when MindRoom should start the MCP server as a subprocess.

```yaml
mcp_servers:
  echo:
    transport: stdio
    command: python
    args:
      - ./echo_mcp_server.py
    cwd: .
    env:
      ECHO_PREFIX: "echo:"
```

For `stdio` servers, `command` is required.
`args`, `cwd`, and `env` are optional.
`url` and `headers` are not allowed on this transport.

### `sse`

Use `sse` for MCP servers that expose a Server-Sent Events endpoint.

```yaml
mcp_servers:
  remote_sse:
    transport: sse
    url: http://127.0.0.1:9000/sse
    headers:
      Authorization: Bearer ${MCP_API_TOKEN}
```

For `sse` servers, `url` is required.
`headers` are optional.
`command`, `args`, `cwd`, and `env` are not allowed on this transport.

### `streamable-http`

Use `streamable-http` for MCP servers that expose the newer streamable HTTP endpoint.

```yaml
mcp_servers:
  remote_http:
    transport: streamable-http
    url: http://127.0.0.1:9000/mcp
    headers:
      Authorization: Bearer ${MCP_API_TOKEN}
```

For `streamable-http` servers, `url` is required.
`headers` are optional.
`command`, `args`, `cwd`, and `env` are not allowed on this transport.

`env` and `headers` values support `${ENV_VAR}` interpolation.
MindRoom resolves those placeholders from the current runtime environment when it opens the MCP transport.
Static `headers` are process-global and shared by every requester.
Use the OAuth `auth` block for remote MCP servers that need a different bearer token per requester.

## Per-Server Options

| Option | Type | Default | Notes |
|--------|------|---------|-------|
| `enabled` | bool | `true` | Set to `false` to disable one server without removing its config |
| `description` | string | `null` | What the server provides; appended to the OAuth bridge tool descriptions shown to the model; requires `auth` |
| `required` | bool | `false` | Block dependent agent startup while this server is unavailable instead of degrading |
| `transport` | string | *required* | One of `stdio`, `sse`, or `streamable-http` |
| `command` | string | `null` | Required for `stdio` |
| `args` | list[string] | `[]` | Optional `stdio` arguments |
| `cwd` | string | `null` | Optional `stdio` working directory |
| `env` | map[string,string] | `{}` | Optional `stdio` environment variables; supports `${ENV_VAR}` placeholders |
| `url` | string | `null` | Required for `sse` and `streamable-http` |
| `headers` | map[string,string] | `{}` | Optional remote transport headers; supports `${ENV_VAR}` placeholders |
| `auth` | object | `null` | Optional OAuth configuration for requester-scoped remote MCP access |
| `tool_prefix` | string | server ID | Prefix for model-visible function names |
| `include_tools` | list[string] | `[]` | Optional allowlist of remote tool names to expose |
| `exclude_tools` | list[string] | `[]` | Optional denylist of remote tool names to hide |
| `startup_timeout_seconds` | float | `20.0` | Maximum time to open the transport, initialize, and discover tools |
| `call_timeout_seconds` | float | `120.0` | Default timeout for each tool call |
| `max_concurrent_calls` | int | `1` | Maximum concurrent tool calls for that server |
| `auto_reconnect` | bool | `true` | Retry once after connection or timeout failures during a call |

`tool_prefix` must use only letters, numbers, and underscores.
`include_tools` and `exclude_tools` are matched against the remote MCP tool names, not the MindRoom-prefixed function names.
`include_tools` and `exclude_tools` cannot overlap.

## Agent Access

Each MCP server becomes one MindRoom tool named `mcp_<server_id>`.
Add that name to an agent's `tools:` list to expose the server's discovered tools.

```yaml
mcp_servers:
  chrome_devtools:
    transport: stdio
    command: npx
    args:
      - -y
      - chrome-devtools-mcp@latest

agents:
  browser:
    display_name: Browser
    role: Debug and inspect web apps in Chrome
    model: sonnet
    tools:
      - mcp_chrome_devtools
```

You can also apply per-agent overrides when you assign the MCP tool:

```yaml
agents:
  browser:
    tools:
      - mcp_chrome_devtools:
          include_tools:
            - new_page
            - navigate_page
            - take_snapshot
          call_timeout_seconds: 180
```

These per-agent overrides filter the already discovered catalog for that agent assignment.
They are useful when one server exposes many tools but one agent should see only a focused subset.

MCP tools are available on every worker scope, including private per-user agents.
Non-OAuth `mcp_<server_id>` tools always execute through the shared MCP server session; requester identity and requester credentials are never passed to the server.
OAuth-backed remote MCP servers instead load a scoped OAuth token for the current requester at tool-call time and keep one session per requester scope.

## OAuth-Backed Remote MCP

Use `auth.type: oauth` for a remote MCP server that requires a requester-owned OAuth bearer token.
OAuth-backed MCP requires `sse` or `streamable-http`; `stdio` servers cannot use this mode.

```yaml
mcp_servers:
  example:
    transport: streamable-http
    url: https://mcp.example.com/mcp
    tool_prefix: example
    description: Example workspace search, documents, and calendar for the signed-in user.
    auth:
      type: oauth
      provider_id: mcp_example
      display_name: Example MCP
      resource: https://mcp.example.com/mcp
      discovery: auto
      token_endpoint_auth_method: none
      pkce_code_challenge_method: S256
      dynamic_client_registration: true
      scopes: []

agents:
  assistant:
    display_name: Assistant
    role: Use requester-scoped MCP tools
    model: sonnet
    worker_scope: user_agent
    tools:
      - mcp_example
```

MindRoom registers a generated OAuth provider for the server.
If `provider_id` is omitted, the provider ID defaults to `mcp_<server_id>`.
When the provider ID starts with `mcp_`, OAuth tokens are stored under `<provider_id>_oauth`, and OAuth client configuration is read from `<provider_id>_oauth_client` unless you specify `client_config_services` or `shared_client_config_services`.
When a custom provider ID does not start with `mcp_`, MindRoom prefixes `mcp_` for the generated credential service names so the tokens remain classified as MCP-owned runtime credentials.

OAuth-backed MCP servers always expose a stable bridge surface:

- `<prefix>_connection_status`
- `<prefix>_list_tools`
- `<prefix>_call_tool`

The bridge functions let an agent trigger the normal MindRoom OAuth connect flow before the remote server has revealed a requester-specific tool catalog.
When credentials are missing, the bridge returns the same structured OAuth-required payload used by built-in OAuth tools.
Until the requester connects, the bridge functions are the only model-visible surface for the server, and their generic descriptions say nothing about what the server offers.
Set the per-server `description` option to tell the model what connecting would unlock; it is appended to all three bridge tool descriptions.
After the user connects, `list_tools` returns the remote catalog and `call_tool` sends `Authorization: Bearer <requester access token>` to the MCP server.
After MindRoom has a cached requester-specific catalog, the toolkit also exposes typed `<prefix>_<remote_tool_name>` functions for that requester in addition to the bridge functions.

`discovery: auto` performs protected-resource metadata discovery from the configured `resource` or server `url`, then resolves authorization-server metadata.
MindRoom tries `/.well-known/oauth-protected-resource` at the resource origin and at the resource path.
It then reads the advertised authorization server and fetches OAuth authorization-server metadata.
The discovered metadata supplies the authorization endpoint, token endpoint, optional registration endpoint, supported token endpoint auth methods, and supported PKCE methods.

If `dynamic_client_registration` is enabled and no client config has been stored yet, MindRoom registers a public client lazily when the first OAuth flow starts.
The generated client registration is stored in the generated OAuth client config service and reused for later users.
Public clients using `token_endpoint_auth_method: none` only need `client_id`; confidential methods still require `client_secret`.
Use `extra_auth_params` and `extra_token_params` when the OAuth server requires additional parameters such as `resource` during authorization, code exchange, or refresh.

Use `discovery: manual` when the provider does not publish metadata or when you want to pin endpoints explicitly:

```yaml
mcp_servers:
  example_manual:
    transport: streamable-http
    url: https://mcp.example.com/mcp
    auth:
      type: oauth
      discovery: manual
      authorization_url: https://mcp.example.com/oauth/authorize
      token_url: https://mcp.example.com/oauth/token
      registration_url: https://mcp.example.com/oauth/register
      token_endpoint_auth_method: none
```

OAuth discovery requires HTTPS by default and does not follow redirects.
For local development, set `MINDROOM_MCP_OAUTH_ALLOW_INSECURE_DISCOVERY=1` to allow non-HTTPS discovery URLs, and set `MINDROOM_MCP_OAUTH_ALLOW_PRIVATE_DISCOVERY=1` to allow loopback or private-network discovery hosts.

## Tool Naming

There are two names to keep in mind:

1. The MindRoom tool entry that you put in `tools:` is `mcp_<server_id>`.
2. The model-visible function names inside that toolkit are `<prefix>_<remote_tool_name>`.

If `tool_prefix` is omitted, MindRoom uses the server ID as the prefix.

For example, this config:

```yaml
mcp_servers:
  chrome_devtools:
    transport: stdio
    command: npx
    args:
      - -y
      - chrome-devtools-mcp@latest
    tool_prefix: chrome
```

means a remote MCP tool named `navigate_page` becomes `chrome_navigate_page` inside the agent's tool list.

MindRoom rejects duplicate function names after prefixing.
That includes collisions inside one server and collisions across different servers.
The final function name must also fit within 64 characters.

## Example: Echo MCP Server

This is the smallest useful local example.
Save it as `echo_mcp_server.py`:

```python
from mcp.server.fastmcp import FastMCP

server = FastMCP("Echo Server")


@server.tool()
def echo(text: str) -> str:
    return f"echo:{text}"


if __name__ == "__main__":
    server.run()
```

Then point MindRoom at it:

```yaml
mcp_servers:
  echo:
    transport: stdio
    command: ./.venv/bin/python
    args:
      - ./echo_mcp_server.py

agents:
  code:
    display_name: Code
    role: Test MCP tools
    model: sonnet
    tools:
      - mcp_echo
```

With that setup, the remote `echo` tool is exposed to the model as `echo_echo`.
If you prefer a shorter function name, set `tool_prefix` to something else.
Use whatever Python interpreter has the `mcp` package installed.
Inside this repository, `./.venv/bin/python` is usually the right choice.

The same server can also be run over HTTP transports.
If you change the script to `server.run(transport="sse")`, the default FastMCP endpoint is `/sse`.
If you change it to `server.run(transport="streamable-http")`, the default FastMCP endpoint is `/mcp`.

## Example: Chrome DevTools MCP

Chrome DevTools MCP is a good fit for browser debugging, page inspection, performance work, and interactive web automation.

```yaml
mcp_servers:
  chrome_devtools:
    transport: stdio
    command: npx
    args:
      - -y
      - chrome-devtools-mcp@latest
    tool_prefix: chrome
    startup_timeout_seconds: 20

agents:
  browser:
    display_name: Browser
    role: Debug web apps with Chrome DevTools
    model: sonnet
    tools:
      - mcp_chrome_devtools
```

This exposes Chrome DevTools functions with names like `chrome_<tool_name>`.
By default, `chrome-devtools-mcp` starts its own Chrome instance with a dedicated profile.

To attach to an already running debuggable Chrome instance instead, add `--browser-url=http://127.0.0.1:9222`:

```yaml
mcp_servers:
  chrome_devtools:
    transport: stdio
    command: npx
    args:
      - -y
      - chrome-devtools-mcp@latest
      - --browser-url=http://127.0.0.1:9222
    tool_prefix: chrome
```

If Chrome startup is slow on your machine, increase `startup_timeout_seconds`.
If individual browser operations can take a while, increase `call_timeout_seconds`.

## Example: MemPalace AI Memory

[MemPalace](https://github.com/milla-jovovich/mempalace) is a local AI memory system that stores conversations in ChromaDB, organized as a navigable "palace" with wings, rooms, and halls.
It exposes 19 MCP tools for search, knowledge graph operations, agent diaries, and more — all without API keys.

Using `uvx` keeps MemPalace in an isolated virtual environment, avoiding dependency conflicts with MindRoom's own ChromaDB version:

```yaml
mcp_servers:
  mempalace:
    transport: stdio
    command: uvx
    args:
      - --from
      - mempalace
      - python
      - -m
      - mempalace.mcp_server
      - --palace
      - /path/to/.mempalace/palace
    startup_timeout_seconds: 30
    call_timeout_seconds: 60

agents:
  assistant:
    display_name: Assistant
    role: General assistant with persistent memory
    model: sonnet
    tools:
      - mcp_mempalace
```

Before the MCP server can return results, initialize and seed the palace:

```bash
uvx mempalace init /path/to/content
uvx mempalace mine /path/to/content
```

Agents can also add memories on the fly via the `mempalace_add_drawer` tool.
The palace enforces deduplication at a 0.9 similarity threshold.

## Error Handling

MindRoom connects to MCP servers during startup and whenever `config.yaml` changes.
If a server fails to start, initialize, or publish a valid tool catalog, MindRoom marks that server as failed and logs a warning.

By default a failed server degrades gracefully: agents and teams that reference `mcp_<server_id>` still start, with that server's tools omitted from their tool schema.
MindRoom keeps retrying failed MCP discovery in the background with exponential backoff, and restarts the affected agents and teams once the server recovers so they pick up its tools.
Set `required: true` on a server to restore hard-fail behavior, where dependent agents and teams stay blocked from starting until the server is available.

During tool execution, explicit MCP tool failures are surfaced as tool errors.
Those explicit server-side errors are not retried automatically.

Connection drops and timeouts are treated differently.
When `auto_reconnect: true`, MindRoom refreshes the server connection and retries the tool call once.
If reconnect also fails, the error is surfaced to the caller.

If an MCP server sends a `tools/list_changed` notification, MindRoom refreshes that server's catalog.
If the catalog changed, MindRoom restarts the agents and teams that reference that server so they pick up the updated tool list.
The catalog-change callback schedules this replacement asynchronously so the triggering MCP tool call can finish before response draining starts.
Configured servers with no dependent entity return before admission draining while still invalidating the worker validation snapshot cache.
Referenced-entity replacement shares one serialized global response-admission owner with config reload.
It waits up to 600 seconds for active Matrix responses to drain, then force-applies while keeping admission closed over the replacement window.

## Limitations

- Phase 1 supports MCP tools only.
- MCP resources and prompts are not exposed in MindRoom yet.
- Non-OAuth MCP integrations always use the shared server session, even on isolating worker scopes; per-requester isolation requires an OAuth-backed server.
- OAuth-backed remote MCP integrations use requester-scoped OAuth credentials and sessions.
- OAuth-backed remote MCP typed functions appear only after MindRoom has cached a requester-specific remote catalog.
- `server_id` and `tool_prefix` must use letters, numbers, and underscores.
- The final function name `<prefix>_<remote_tool_name>` must be 64 characters or fewer.
