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

# Connect MCP servers

> Add a remote MCP server's tools to an agent, authenticate it with a vault credential, and test the connection before a session depends on it.

An MCP server is a remote service that offers tools over the Model Context Protocol. When you add one to an agent, each session connects to the server, discovers its tools, and lets the agent call them. Calls are made outside the sandbox, so the server's token never enters the sandbox or the model's context.

This page covers agents calling out to MCP servers. To connect your coding assistant to these docs, see [Use with AI coding agents](/managed-agents/ai-coding-agents).

The TypeScript samples assume the `rl` client from [Client setup](/managed-agents/api#client-setup).

## Before you begin

* You need the organization developer or admin role to change an agent or test a server. See [Organizations and roles](/managed-agents/organizations-and-roles).
* The server must use the Streamable HTTP transport at a public `http` or `https` URL. Servers on private, loopback, or link-local addresses are refused. Local servers that run over stdio aren't supported.
* If the server needs a token, have it ready. The token goes in a vault. If the service is one of the 164+ apps in [Integrations](/managed-agents/integrations), you can connect it there instead and choose its tools, without handling a token.

## How a session uses an MCP server

```mermaid theme={"theme":"css-variables"}
sequenceDiagram
  participant S as Session start
  participant V as Granted vaults
  participant M as MCP server
  participant A as Agent
  S->>V: Find a token whose server URL matches
  S->>M: Connect and list tools (token attached)
  M-->>S: Tool names and schemas
  S->>A: Tools offered as name__tool
  A->>M: Tool call (token attached outside the sandbox)
  M-->>A: Result
```

1. When a session starts, it reads the MCP servers on its agent version.
2. For each server, it looks for an **MCP API token** in the session's granted vaults whose server URL matches.
3. It connects, lists the server's tools, and fixes that list and the tool schemas for the whole session.
4. The agent calls a tool as `<server name>__<tool name>`. The token is attached to every call outside the sandbox.

Tools, prompts, and resources: sessions use the server's tools. Text and images that a tool returns are passed to the agent. Server prompts and standalone resources aren't offered, and resource links in a result aren't fetched.

## Add an MCP server to an agent

Each server entry has a `name` and a `url`.

| Field  | Description                                                                                                                               |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | The label the server's tools are grouped under. Characters other than letters, digits, `_`, and `-` become `-` in tool names.             |
| `url`  | The server's full Streamable HTTP endpoint, `http` or `https`, with no user name or password in it. Use `https` whenever a token is sent. |

If the server `project-tracker` offers `list_issues`, the agent sees `project-tracker__list_issues`. The prefix keeps two servers from clashing.

MCP servers belong to the agent version, so adding one creates a new version. Running sessions keep the tools they started with.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Agents** and open the agent.
    2. On **Configuration**, find **Tools** and click **Add MCP server**.
    3. Enter a **Server name**, then choose a server from the list or type its **Server URL**. The list suggests common servers, such as GitHub, Notion, Linear, Sentry, Context7, and Slack, and says what kind of token each needs.
    4. Click **Save new version**.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const agent = await rl.managedAgents.getAgent({
      agent_id: '5f0c2a1e-8b7d-4c3a-9e21-6d4f0b9a7c55',
    });

    await rl.managedAgents.createAgentVersion({
      agent_id: agent.agent_id,
      body: {
        base_agent_version_id: agent.latest_agent_version_id,
        name: agent.name,
        model: 'anthropic/claude-sonnet-5',
        system: agent.system,
        mcp_servers: [{ name: 'project-tracker', url: 'https://mcp.example.com/mcp' }],
        default_vault_ids: ['26d4b1a8-73c9-4f60-8a15-9e2c7b5d4f31'],
      },
    });
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/agents/5f0c2a1e-8b7d-4c3a-9e21-6d4f0b9a7c55/versions' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "base_agent_version_id": "c1a94e07-2f6b-4d18-b3a5-0e7d8c6f4a21",
        "name": "Release coordinator",
        "model": "anthropic/claude-sonnet-5",
        "system": "Track the release and report blocked work.",
        "mcp_servers": [{"name": "project-tracker", "url": "https://mcp.example.com/mcp"}],
        "default_vault_ids": ["26d4b1a8-73c9-4f60-8a15-9e2c7b5d4f31"]
      }'
    ```
  </Tab>
</Tabs>

The response is the agent at its new version:

```json theme={"theme":"css-variables"}
{
  "agent_id": "5f0c2a1e-8b7d-4c3a-9e21-6d4f0b9a7c55",
  "latest_agent_version_id": "7a4e1c95-6d2f-4380-b915-3e7a0c6f2d84",
  "mcp_servers": [{ "name": "project-tracker", "url": "https://mcp.example.com/mcp" }],
  "default_vault_ids": ["26d4b1a8-73c9-4f60-8a15-9e2c7b5d4f31"]
}
```

A new version replaces the whole definition, so send `skills`, tool settings, vault defaults, and every other field you want to keep. See [Agents](/managed-agents/agents).

## Authenticate the server

Store the server's token in a [vault](/managed-agents/vaults) as an **MCP API token** (`bearer_token`) whose server URL matches the agent's `url`. Then grant the vault to the agent with `default_vault_ids`, or to a session with `vault_ids`. If you narrow grants with credential refs, include this credential.

How matching works:

* URLs match after lowercasing the scheme and host, dropping a default port (`:443` or `:80`), and ignoring a trailing slash. A different path, subdomain, or port is a different server, so the token isn't sent there.
* The token is sent as `Authorization: Bearer <token>` by default. If the credential sets `secret_name`, the token is sent as-is in a header with that name instead, for servers that read a header such as `X-Api-Key`.
* If two granted vaults hold a token for the same server, the vault listed first in the session's grants wins.
* If no token matches, the session connects without one. That works for public servers and fails for servers that need authorization.

**A granted token also adds its server.** An MCP API token in a granted vault adds its server's tools to the session even if the agent doesn't list that server. Those tools get a generated `vault-...` prefix. To keep a server out of a session, don't grant the vault that holds its token, or leave the credential out of the credential refs.

Tokens that you paste are stored as-is and aren't refreshed. If the server issues short-lived OAuth access tokens, [rotate the credential](/managed-agents/vaults#rotate-a-credential) before it expires.

## Test a server before you use it

`probeMcpServer` makes one live connection using the same URL matching and vault lookup as a session. It creates and changes nothing. Omit `vault_ids` to test without a token.

<Tabs>
  <Tab title="Console">
    1. In the sidebar, click **Credential vaults** and open the vault.
    2. Expand the server's **MCP API token** row. It shows whether the server is reachable and how many tools it offers.
    3. Click **Test again** to run a new test.
  </Tab>

  <Tab title="TypeScript">
    ```typescript theme={"theme":"css-variables"}
    const probe = await rl.managedAgents.probeMcpServer({
      body: {
        server_url: 'https://mcp.example.com/mcp',
        vault_ids: ['26d4b1a8-73c9-4f60-8a15-9e2c7b5d4f31'],
      },
    });
    console.log(probe.ok, probe.credential_matched, probe.tools.length);
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={"theme":"css-variables"}
    curl -X POST 'https://api.recursion.labelbox.com/managed-agents/v1/mcp/probe' \
      -H "Authorization: Bearer $RECURSION_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "server_url": "https://mcp.example.com/mcp",
        "vault_ids": ["26d4b1a8-73c9-4f60-8a15-9e2c7b5d4f31"]
      }'
    ```
  </Tab>
</Tabs>

```json theme={"theme":"css-variables"}
{
  "ok": true,
  "reachable": true,
  "credential_matched": true,
  "deployment_authorized": false,
  "protocol_version": "2025-06-18",
  "server_name": "project-tracker",
  "tools": [
    { "name": "list_issues", "description": "List issues and their current status." }
  ]
}
```

| Field                   | Meaning                                                                                                                                                                                                                           |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ok`                    | The server completed the handshake and returned its tool list.                                                                                                                                                                    |
| `reachable`             | The handshake succeeded, even if listing tools then failed.                                                                                                                                                                       |
| `credential_matched`    | A token from the named vaults was attached. `false` means the test ran without a token.                                                                                                                                           |
| `credential_unopenable` | A token matched the URL but couldn't be used, so none was sent. `material_missing` means the credential has no stored value; re-enter it. `external_reference` means its value is held outside Recursion; re-enter it as a value. |
| `deployment_authorized` | Always `false` for servers you add in Recursion. You can ignore it.                                                                                                                                                               |
| `server_name`           | What the server called itself. Use it to confirm you reached the right service.                                                                                                                                                   |
| `protocol_version`      | The Model Context Protocol version the test used.                                                                                                                                                                                 |
| `tools`                 | The tools offered on this attempt, as the agent would see them before the name prefix.                                                                                                                                            |
| `error`                 | Why the test failed. Never contains the token.                                                                                                                                                                                    |

A failed test still returns `200`. Read the body:

| Result                                                      | Meaning                                                                                                           |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `ok: true`                                                  | The server works with this token.                                                                                 |
| `ok: false`, `reachable: true`                              | The connection worked, but listing tools failed. Read `error`.                                                    |
| `ok: false`, `reachable: false`, `credential_matched: true` | A token was sent, and the server refused or failed. The token may be wrong or expired, or the server may be down. |
| `ok: false`, `credential_matched: false`                    | No token was sent. Check the credential's server URL and the vaults you named.                                    |

A test gives up after 15 seconds. A server on a private, loopback, or link-local address is refused before anything is sent, so the test returns `reachable: false` with the reason in `error`.

**What success means:** a passing test proves the server is reachable and that the token lets you list tools. It doesn't prove that every tool call will succeed; permissions, arguments, and the server's own state still matter.

## Choose which tools an agent gets

A session receives every tool the server lists for its token. You can't hide individual tools of a server. To limit what an agent can do, give it a token whose scopes allow only what it needs, or point it at a server that offers fewer tools.

## What happens when a server fails

* **At session start:** an unreachable server or rejected token doesn't stop the session. Other servers still load, and the session's events record an `mcp_discovery_failed` warning naming the servers. Fix the URL, server, or token, then start a new session. Running sessions don't rediscover tools.
* **During a call:** each request to the server has a 60-second timeout, and a response can be at most 8 MiB. A failed call returns an error to the agent as the tool result, and the agent can decide what to do next.

## What can go wrong

| Symptom or code                                  | Cause                                                                                               | Fix                                                                                                                             |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `400 invalid_request` on `server_url`            | The URL is missing, isn't `http` or `https`, has no host, or includes a user name or password.      | Use the server's full endpoint URL, and put the token in a vault.                                                               |
| `404 not_found` from a test                      | A vault in `vault_ids` doesn't exist in your organization.                                          | Check the vault id.                                                                                                             |
| `403 forbidden` from a test                      | Your role can't test servers.                                                                       | See [Organizations and roles](/managed-agents/organizations-and-roles). Use an account or key with the developer or admin role. |
| A session has an `mcp_discovery_failed` warning  | The server was unreachable, refused the token, or is on a private address.                          | Run a test with the same URL and vault, fix the cause, and start a new session.                                                 |
| The server's tools are missing without a warning | The server isn't on the agent version the session used, or the session started before you saved it. | Check the session's agent version and start a new session.                                                                      |
| Unexpected `vault-...` tools appear              | A granted vault holds an MCP API token for a server the agent doesn't list.                         | Remove the credential from the session's grants, or add the server to the agent with a name you choose.                         |
| Tools worked yesterday and now fail with 401     | A short-lived access token expired.                                                                 | Rotate the credential.                                                                                                          |
| A tool call times out                            | The server took longer than 60 seconds.                                                             | Make the tool faster, or have it return a job id the agent can check later.                                                     |

For all error codes, see [Errors](/managed-agents/errors). For limits, see [Limits](/managed-agents/limits).

## Next steps

<CardGroup cols={2}>
  <Card title="Vaults and credentials" href="/managed-agents/vaults">
    Store MCP API tokens and control which sessions receive them.
  </Card>

  <Card title="Agents" href="/managed-agents/agents">
    Publish a new agent version with its MCP servers attached.
  </Card>

  <Card title="Tools" href="/managed-agents/tools">
    See the built-in tools every session can use.
  </Card>

  <Card title="Events" href="/managed-agents/events">
    Read discovery warnings and MCP tool calls in the session timeline.
  </Card>
</CardGroup>
