---
sidebar_position: 6
title: surfaces & entryPoints
---

# `surfaces` and `entryPoints`

Two blocks, one subject: **which channels your agent offers, and where each one
lives.**

- `surfaces` — *which* channels the agent's page should show.
- `entryPoints` — *where* a channel lives when it is not served by the platform
  itself, and every word the card shows.

---

## `surfaces`

### What it is

The deployed agent's page renders one section per interaction channel.
`surfaces` says which of them are meaningful, so a pure knowledge-base agent
does not show a Trigger button that does nothing.

### Syntax

```js
spec: { surfaces: ['mcp', 'connect'] }
```

### Properties

| Name | Type | Required | Allowed values | Default | Update behavior |
|---|---|---|---|---|---|
| `surfaces` | string[] | No | `cli`, `webhook`, `schedule`, `mcp`, `connect` | `['cli','webhook','schedule','mcp']` | Re-applied |

| Value | Meaning |
|---|---|
| `cli` | started from the command line |
| `webhook` | started by an inbound HTTP call |
| `schedule` | started by cron |
| `mcp` | a machine endpoint an MCP client attaches to |
| `connect` | a public page a **human opens in a browser** to link their own account — an OAuth sign-in, a device pairing, a licence activation |

`connect` is not a webhook: nothing third-party posts to it and it starts no run.

Unknown values are ignored. Declaring nothing gives you the four standard
channels — **not** all five, because `connect` is opt-in by design.

### Use cases

| Agent shape | Declare |
|---|---|
| An ordinary triggerable workflow | nothing |
| A pure store — running it directly is a no-op, it is driven over MCP | `['mcp']` |
| A service a person signs into, then uses from their editor | `['mcp', 'connect']` |

### Gotchas

Declaring `mcp` does not create an endpoint — it says the agent has one. The
endpoint still has to exist.

---

## `entryPoints`

### What it is

For a channel that need not be served by the platform, `entryPoints` names which
service serves it and at which path. **The URL itself is derived when the page
is read** — from the service's resolved public mount and your installation's
origin. No template ever writes a URL down.

For `connect` it also carries every word on the card, because the renderer has
none of its own.

### Syntax

An MCP endpoint the platform authenticates and forwards to a service:

```js
spec: {
  surfaces: ['mcp'],
  entryPoints: {
    mcp: {
      sidecar: 'openapi-mcp',
      path: '/mcp',
      auth: 'bearer-pat',
      label: 'MCP endpoint for the APIs this agent bridges',
    },
  },
}
```

A sign-in the platform drives, on an agent with no service container at all:

```js
spec: {
  surfaces: ['mcp', 'connect'],
  entryPoints: {
    connect: {
      kind: 'oauth',
      server: 'figma',                  // a name from this agent's own remoteMcp block
      label: 'Connect your Figma account',
      button: 'Connect',
      buttonDisconnect: 'Disconnect',
      disconnectConfirm: 'Disconnect Figma? The stored sign-in is deleted.',
      connectedLine: 'Connected to Figma as {account}.',
    },
    // NO `mcp` entry — the absence IS the declaration. See below.
  },
}
```

### Properties

Only `mcp` and `connect` can be redirected. `cli`, `webhook` and `schedule` are
platform mechanics with no alternative home.

| Name | Type | Required | Allowed values | Default | Update behavior |
|---|---|---|---|---|---|
| `sidecar` | string | **Yes**, except for `connect` with `kind: 'oauth'` | a service this agent declares, or one the platform ships | — (entry is dropped without it) | Re-applied |
| `path` | string | **Yes**, same exception | must start with `/`; `..` is refused | — | Re-applied |
| `auth` | string | No | `bearer-pat`, `connect-bearer`, `none` | `connect-bearer` for `mcp`, `none` for `connect` | Re-applied |
| `label` | string | No | any | none | Card only |
| `kind` (connect) | string | No | `sidecar-page`, `oauth` | `sidecar-page` | Re-applied |
| `server` (connect + `oauth`) | string | **Yes** for that kind | a name from `remoteMcp` | — | Re-applied |
| copy (`button`, `buttonDisconnect`, `disconnectConfirm`, `disconnectedLine`, `waitingLine`, `connectedLine`, `note`) | string | No | `{account}` is substituted | none — a field you omit renders nothing | Card only |

### `auth` names what the CALLER presents

| Value | The caller presents |
|---|---|
| `bearer-pat` | a Zibby access token — the platform authenticates and forwards |
| `connect-bearer` | the token this agent's own sign-in handed the user |
| `none` | nothing — the surface authenticates the caller itself |

:::warning A misspelt `auth` is silently corrected, not rejected
An unrecognised value falls back to the channel's default, so a typo quietly
describes an authentication your endpoint does not use. Spell it exactly.
:::

### An absent `entryPoints.mcp` is itself a declaration

Leave it out and **the platform serves the surface itself**, at the agent's own
authenticated MCP address, with a Zibby bearer token.

That is the right choice whenever your service has no authentication of its own.
Naming a source when you did not need to is how an endpoint gets taken away from
the sign-in that feeds it: the declared service wins the address and answers
every call with its own "missing token".

:::tip One agent, one MCP address
A declared MCP service takes the **whole** surface — other MCP servers attached
to the same agent are not merged into it. If you need both, you need two agents.
:::

### Use cases

| You want | Declare |
|---|---|
| Platform-served MCP with a Zibby token | `surfaces: ['mcp']` and nothing else |
| MCP forwarded to your own service, platform-authenticated | `entryPoints.mcp` with `auth: 'bearer-pat'` |
| A service that runs its own OAuth and issues its own bearer | `connect` with `auth: 'none'` **and** `mcp` with `auth: 'connect-bearer'` |
| A hosted third-party MCP the platform signs you into | `connect` with `kind: 'oauth'`, and **no** `mcp` entry |

### Gotchas

**Both halves are required.** A channel must be in `surfaces` *and* have an
`entryPoints` entry before anything is derived.

**A path the proxy would not serve is never advertised.** If the mount does not
publish it, the entry is dropped rather than shown as a dead link.

**`connect-bearer` turns off the platform's own token check** on the agent's MCP
address. Only declare it when your service really does authenticate.

**Service-served entries need a self-hosted box.** A `connect` entry with
`kind: 'oauth'` works everywhere.

## See also

- [`sidecars`](./sidecars.md), [`remoteMcp`](./remote-mcp.md),
  [`requires`](./requires.md)
