---
title: "MCP"
description: "Extend agents with Model Context Protocol servers"
---

MCP ([Model Context Protocol](https://modelcontextprotocol.io/)) lets you connect external tool servers to Fabro agents. An MCP server exposes tools over a standardized protocol — databases, APIs, file systems, custom services — and Fabro discovers and registers them automatically. Agents call MCP tools the same way they call built-in tools.

## How it works

When an agent session starts with MCP servers configured, Fabro:

1. **Connects** to each server using the configured transport (stdio, HTTP, or sandbox)
2. **Performs the MCP handshake** — exchanging protocol versions and capabilities
3. **Discovers tools** — calls `tools/list` to get every tool the server exposes
4. **Registers tools** — each MCP tool is added to the agent's tool registry with a qualified name

Once registered, MCP tools are indistinguishable from built-in tools to the LLM. The agent sees them in its tool list and can call them during its session.

## Tool naming

MCP tools are registered with a qualified name that combines the server name and original tool name:

```
mcp__{server}__{tool}
```

For example, a server named `filesystem` exposing a `read_file` tool becomes `mcp__filesystem__read_file`. Special characters in server or tool names (hyphens, dots, etc.) are replaced with underscores.

## Configuration

MCP servers can be configured in two places:

- **`~/.fabro/settings.toml`** — applies to `fabro exec` sessions. See [User Configuration](/reference/user-configuration#mcp_servers-section).
- **Run config TOML** — applies to workflow runs (`fabro run`). See [Run Configuration](/execution/run-configuration#mcp_servers).

Each server entry specifies a transport type and optional timeouts. The server name is the TOML table key and is used in qualified tool names.

## Transports

### Stdio

The most common transport. Fabro spawns a child process on the host and communicates over stdin/stdout:

```toml
[mcp_servers.filesystem]
type = "stdio"
command = ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
startup_timeout_secs = 15
tool_timeout_secs = 90

[mcp_servers.filesystem.env]
NODE_ENV = "production"
```

| Field | Description | Default |
|---|---|---|
| `type` | Must be `"stdio"`. | — |
| `command` | Array: the executable followed by its arguments. | — |
| `env` | Additional environment variables for the child process. | `{}` |
| `startup_timeout_secs` | Max seconds to wait for the MCP handshake. | `10` |
| `tool_timeout_secs` | Max seconds to wait for a single tool call. | `60` |

### HTTP

For remote MCP servers accessible over Streamable HTTP:

```toml
[mcp_servers.sentry]
type = "http"
url = "https://mcp.sentry.dev/mcp"

[mcp_servers.sentry.headers]
Authorization = "Bearer sk-xxx"
```

| Field | Description | Default |
|---|---|---|
| `type` | Must be `"http"`. | — |
| `url` | The MCP server endpoint URL. | — |
| `headers` | Optional HTTP headers (e.g., for authentication). | `{}` |
| `startup_timeout_secs` | Max seconds to wait for the MCP handshake. | `10` |
| `tool_timeout_secs` | Max seconds to wait for a single tool call. | `60` |

### Sandbox

Runs the MCP server inside the workflow's sandbox (e.g., a [Daytona](/integrations/daytona) cloud VM). Fabro starts the server as a background process, waits for it to listen on the specified port, obtains an authenticated preview URL, and connects via HTTP. This is the right transport for MCP servers that need access to the sandbox environment — for example, [Playwright](https://github.com/microsoft/playwright-mcp) for browser automation.

```toml
[mcp_servers.playwright]
type = "sandbox"
command = ["npx", "@playwright/mcp@latest", "--port", "3100", "--headless", "--browser", "chromium"]
port = 3100
startup_timeout_secs = 60
tool_timeout_secs = 120
```

| Field | Description | Default |
|---|---|---|
| `type` | Must be `"sandbox"`. | — |
| `command` | Array: the command to run inside the sandbox. Must include a flag that makes the server listen on `port`. | — |
| `port` | The port the MCP server listens on inside the sandbox. | — |
| `env` | Additional environment variables for the server process. | `{}` |
| `startup_timeout_secs` | Max seconds to wait for the server to start listening and complete the MCP handshake. | `10` |
| `tool_timeout_secs` | Max seconds to wait for a single tool call. | `60` |

The sandbox transport requires a remote sandbox provider (Daytona) that supports preview URLs. During session initialization, Fabro:

1. Launches the server inside the sandbox using `setsid` to fully detach the process
2. Polls until the server is listening on the configured port (up to 30 seconds)
3. Obtains an authenticated preview URL from the sandbox provider
4. Connects to the server over HTTP using the preview URL

## Lifecycle

### Startup

Each MCP server is started sequentially during session initialization. The startup sequence for each server is:

1. **Spawn / connect** — For stdio, spawn the child process. For HTTP, create the HTTP client. For sandbox, start the server inside the sandbox and connect via preview URL.
2. **Handshake** — Perform the MCP protocol handshake within the `startup_timeout_secs` window.
3. **Tool discovery** — Call `tools/list` to enumerate available tools.
4. **Registration** — Add each tool to the agent's registry with its qualified name.

If a server fails to start (process crash, timeout, handshake error), it is logged and skipped. Other servers continue starting normally. The agent session proceeds with whatever tools were successfully registered.

### Tool execution

When the LLM calls an MCP tool:

1. Fabro looks up the qualified name in the connection manager
2. The call is routed to the correct server using the original (unqualified) tool name
3. The server executes the tool and returns a result
4. The result is converted to text and returned to the LLM as a tool result

Tool calls are subject to the `tool_timeout_secs` configured on the server. If a call exceeds the timeout, it fails with a timeout error.

### Content handling

MCP tool results can contain multiple content blocks. Fabro converts them to text:

| Content type | Conversion |
|---|---|
| Text | Used as-is |
| Image | Replaced with `[image content]` |
| Audio | Replaced with `[audio content]` |
| Resource | Replaced with `[resource content]` |

If the server marks the result as an error (`is_error: true`), the tool result is returned to the LLM as an error.

## Example: Playwright browser automation

A workflow that uses Playwright MCP to automate a browser inside a Daytona sandbox:

```toml title="run.toml"
_version = 1

[workflow]
graph = "workflow.fabro"

[run]
goal = "Test the login page"

[run.sandbox]
provider = "daytona"

[run.sandbox.daytona.snapshot]
name = "daytona-medium"

[run.artifacts]
include = ["screenshots/**"]

[run.agent.mcps.playwright]
type = "sandbox"
command = ["npx", "@playwright/mcp@latest", "--port", "3100", "--headless", "--browser", "chromium"]
port = 3100
startup_timeout = "60s"
tool_timeout = "2m"
```

After startup, the agent sees 22 Playwright tools including:
- `mcp__playwright__browser_navigate`
- `mcp__playwright__browser_click`
- `mcp__playwright__browser_snapshot`
- `mcp__playwright__browser_take_screenshot`
- `mcp__playwright__browser_type`
- `mcp__playwright__browser_fill_form`

The agent uses `browser_snapshot` (accessibility tree) for structured page understanding and `browser_take_screenshot` to save visual captures. Screenshots saved to `screenshots/` are automatically collected as [artifacts](/execution/run-configuration#assets).

<Note>
When using Playwright MCP with the sandbox transport, call the `browser_install` tool first to ensure the Playwright browser binaries are available inside the sandbox.
</Note>

<Note>
MCP servers that fail to start do not block the agent session. The agent proceeds with its built-in tools plus any MCP tools from servers that started successfully.
</Note>

## Protocol details

Fabro implements the MCP client side using the `rmcp` SDK (protocol version `2025-03-26`). The client identifies itself as `fabro-mcp` and supports:

- Tool listing and invocation
- Server logging notifications (routed to Fabro's tracing system)
- Progress notifications
- Resource update notifications
- Cancellation notifications
