> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frogbot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP servers

> Give FrogBot agents the tools of a local or remote MCP server, such as Sidekick's browser.

An agent can use the tools of any [MCP](https://modelcontextprotocol.io) server. Declare the server with `mcpServer()` and put it in the agent's `tools`, or in root `tools` for every agent. FrogBot connects on the agent's first run and gives the model the server's tools next to its own.

To expose your app's collections to other MCP clients instead, use the [MCP plugin](https://docs.frogbot.ai/plugins/mcp.md).

## Add a server

A local server is a program FrogBot starts and talks to over stdin and stdout:

```ts theme={"theme":"tokyo-night"}
import { buildConfig } from 'frogbot'
import { mcpServer } from 'frogbot/tools'

export default buildConfig({
  // secret, db, collections, and ai omitted
  agents: [
    {
      slug: 'researcher',
      instructions: 'Use the browser tools to read pages.',
      tools: [
        mcpServer({
          name: 'sidekick',
          type: 'local',
          command: ['npx', '-y', '@frogbotai/sidekick-mcp'],
          environment: { SIDEKICK_TOKEN: process.env.SIDEKICK_TOKEN! },
        }),
      ],
    },
  ],
})
```

A remote server is a Streamable HTTP endpoint:

```ts theme={"theme":"tokyo-night"}
import { mcpServer } from 'frogbot/tools'

export const docsServer = mcpServer({
  name: 'docs',
  type: 'remote',
  url: 'https://docs.example.com/mcp',
  headers: { Authorization: `Bearer ${process.env.DOCS_MCP_TOKEN}` },
})
```

A server in root `tools` reaches every agent, as root tools do. An agent with `inheritTools: false` skips it, and an agent server with the same `name` replaces it.

### Server options

| Option | Type | Default | Description |
| - | - | - | - |
| `name` | `string` | — | Prefix for the server's tools. Letters, digits, `_` and `-` only, and unique in its `tools` list. |
| `type` | `'local' \| 'remote'` | — | `local` starts `command`; `remote` connects to `url`. |
| `command` | `string[]` | — | Local: the program and its arguments. |
| `cwd` | `string` | FrogBot's | Local: the working directory. |
| `environment` | `Record<string, string>` | — | Local: added to FrogBot's own environment, which the server always gets. |
| `url` | `string` | — | Remote: the Streamable HTTP endpoint. SSE is not supported. |
| `headers` | `Record<string, string>` | — | Remote: sent with every request, for example a bearer token. |
| `timeout.startup` | `number` | `30000` | Milliseconds to start the server and finish the handshake. |
| `timeout.catalog` | `number` | `30000` | Milliseconds to list the server's tools on each run. |
| `timeout.execution` | `number` | 12 hours | Milliseconds one tool call may run. |

## Tool names

The model sees each tool as `<name>_<tool>`, with the server's description and input schema: Sidekick's `navigate` becomes `sidekick_navigate`. Characters outside `[A-Za-z0-9_-]` in a tool's name become `_`.

When an agent already has a tool with that name, its own tool wins and FrogBot logs one warning that the MCP tool is left out.

Text results reach the model as text and images as images, so a screenshot is something the model sees. A result the server marks as an error reaches the model as its text. Stored results replay the same way when the chat continues, even if the server is down by then.

## Lifecycle

* **First run:** FrogBot connects when an agent's run first needs the server, never at startup, so `next build`, type generation and jobs start nothing.
* **One connection per agent:** each agent gets its own connection to each server and keeps it across runs. Two agents that list one local server start two processes.
* **Each run:** FrogBot lists the server's tools from the open connection, so tools the server adds or removes show up on the next run.
* **Stop:** stopping a turn cancels the tool call in flight.
* **Shutdown:** `frogbot.destroy()` and a config reload close every connection and end local processes. A local server still running 2 seconds after `SIGTERM` gets `SIGKILL`.

FrogBot sends the plain `initialize` handshake with the agent's slug as the client name, and never probes with `server/discover`.

## When a server fails

A server that fails to start, or to list its tools within its timeouts, is left out of that run. FrogBot logs one warning, `[frogbot] MCP server '<name>' for agent '<slug>' is unavailable: <reason>`, and the run goes on with the agent's other tools. The next run tries again.

A local server that exits while connected fails the call in flight, and FrogBot logs `[frogbot] MCP server '<name>' for agent '<slug>' exited (<code or signal>); it starts again on the next run.`

## Who can use a server

FrogBot doesn't ask before an MCP tool runs. Whoever may run the agent may use its servers, so list a server only on agents whose [`access`](https://docs.frogbot.ai/agents/overview#agent-options) you trust with it:

```ts theme={"theme":"tokyo-night"}
import type { AgentConfig } from 'frogbot'
import { mcpServer } from 'frogbot/tools'

export const browserAgent: AgentConfig = {
  slug: 'browser',
  instructions: 'Use the browser tools to check pages.',
  access: ({ req }) => req.user?.role === 'admin',
  inheritTools: false,
  tools: [
    mcpServer({
      name: 'sidekick',
      type: 'local',
      command: ['npx', '-y', '@frogbotai/sidekick-mcp'],
      environment: { SIDEKICK_TOKEN: process.env.SIDEKICK_TOKEN! },
    }),
  ],
}
```

## Sidekick

Sidekick is FrogBot's Chrome extension. Its MCP server lets an agent drive your browser: open and switch tabs, navigate, read the page, click, fill and take screenshots. It runs where Chrome runs, so use it with a FrogBot running on the same computer.

1. In Sidekick's settings, open **Connected agents**, choose **Other**, name it, for example `FrogBot`, and add it.
2. Sidekick shows the server's command once. Copy the `SIDEKICK_TOKEN` from it into FrogBot's `.env`.
3. Add the server to each agent that should use the browser:

   ```ts theme={"theme":"tokyo-night"}
   import { mcpServer } from 'frogbot/tools'

   export const sidekick = mcpServer({
     name: 'sidekick',
     type: 'local',
     command: ['npx', '-y', '@frogbotai/sidekick-mcp'],
     environment: { SIDEKICK_TOKEN: process.env.SIDEKICK_TOKEN! },
   })
   ```

Each agent opens its own Sidekick tab group, titled with the pairing's name and the agent's slug, for example `FrogBot · researcher`. Stopping one group stops only that agent. Sidekick never asks before an agent's action; blocked sites still apply.

Restarting FrogBot doesn't open Chrome: Sidekick's helper answers the handshake and the tool list from its cache, and wakes Chrome on the first real call.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.