---
sidebar_position: 8
title: remoteMcp
---

# `remoteMcp`

## What it is

The hosted, third-party MCP servers this agent is **for**. Declaring one means a
customer who installs the card never has to know an API call exists: deploy
binds the server onto the agent, the owner authorises it once from the agent's
page, and the platform holds the grant encrypted from then on.

The declaration is pure data — a name, the server's own public URL, and which
authentication **the server** uses. It can never carry a credential.

## Syntax

```js
spec: {
  remoteMcp: [{
    name: 'figma',
    url:  'https://mcp.figma.com/mcp',
    auth: 'oauth',
  }],
}
```

## Properties

| Name | Type | Required | Allowed values | Default | Update behavior |
|---|---|---|---|---|---|
| `name` | string | **Yes** | any — this is the handle `entryPoints.connect.server` refers to | — | Re-applied (matched by URL) |
| `url` | string | **Yes** | must parse; credential-shaped query parameters are stripped | — | Re-applied |
| `auth` | string | No | `oauth`, `bearer` — **anything else drops the whole entry** | absent | Re-applied |

`bearer` and "no `auth`" produce the same stored entry, on purpose: a template
carries no token, so there is nothing to distinguish.

## What deploy does

**Without `auth: 'oauth'`** — the server is bound onto the agent immediately.

**With `auth: 'oauth'`** — deploy binds **nothing**. The agent carries the
declaration; the server entry is created **when the sign-in completes**. A
freshly deployed agent therefore shows no server under its custom MCP list,
because nothing is connected yet — that list holds exactly what is connected.

Binding is **idempotent by URL and additive**. Re-deploying keeps the existing
entry byte-for-byte, which is what keeps the OAuth grant intact. Removing the
declaration never removes a server somebody connected — that stays an explicit
action.

## Pairing it with a Connect card

`remoteMcp` says *what the server is*. [`entryPoints.connect`](./surfaces.md)
says *what the sign-in looks like*, and refers to the server by **name**:

```js
spec: {
  remoteMcp: [{ name: 'figma', url: 'https://mcp.figma.com/mcp', auth: 'oauth' }],
  surfaces: ['mcp', 'connect'],
  entryPoints: {
    connect: {
      kind: 'oauth',
      server: 'figma',
      label: 'Connect your Figma account',
      button: 'Connect',
      buttonDisconnect: 'Disconnect',
      connectedLine: 'Connected to Figma as {account}.',
    },
  },
}
```

Because the card comes from the declaration rather than from a live server row,
the Connect button is on the page from the moment you deploy and stays there
whatever happens to the connection.

## Gotchas

:::warning The identity is the owner's
An agent runs with its own role and resolves nothing about whoever is talking to
it. Anyone who can reach an authorised agent's chat surface acts with the
owner's third-party account. Say so in your card copy, and do not put an
authorised agent in a shared channel.
:::

**Never give one agent two MCP addresses.** An agent that both declares a remote
server *and* hosts its own MCP sidecar will have the sidecar win the address and
answer every call with its own "missing token", while the OAuth grant sits
unused on the other one. One card, one address, one sign-in.

**Credential-shaped query parameters are stripped from the URL** before it is
stored. A template must never carry a credential — and the strip is also what
stops a duplicate server being attached on every re-deploy.

**Servers are matched by URL, not by name**, because the owner may rename an
entry on their own page.

## See also

- [`surfaces` and `entryPoints`](./surfaces.md)
- [`requires`](./requires.md) — the same shape, for one of your own agents.
