---
sidebar_position: 4
title: requires
---

# `requires`

## What it is

One agent needs a capability another agent publishes — a browser, a knowledge
base, a bridge to an internal API. `requires` declares that need by **name**.

At deploy the platform installs the provider as one of **this agent's own
members** — the same cascade, the same binding record and the same delete
cascade a sub-graph child gets — and then attaches the provider's endpoint to
your agent as a managed MCP server.

Nothing concrete is written in the template: no id, no URL, no credential.

## There is no field to put a token in

This is the part people look for and do not find, so it is worth stating plainly:

- **A consumer declares WHO it uses.** Board Autopilot declares
  `requires: [{ ref: 'endpoint:gbrain-kb/mcp', as: 'kb' }]` — and nothing else.
- **A provider declares WHAT IT NEEDS.** The knowledge base declares its own
  embedding model, in its own template.
- **The credential that lets one call the other is in neither declaration.** The
  platform mints it, encrypts it into the consumer's own environment, and never
  returns it, logs it, or shows it to a model.

So there is no token field, no shared secret to distribute and nothing for you
to rotate. If you find yourself wanting to write a credential into a template,
the design has gone wrong somewhere — a declaration is public data that travels
with the card.

:::info Your agent installs its own members
A consumer never borrows a provider that happens to already exist in the
project. It installs its own, always. The heavy engine underneath is shared per
box anyway, so the extra agent row is just configuration.
:::

## Syntax

```js
spec: {
  requires: [{ ref: 'endpoint:browser/mcp', as: 'browser' }],
}
```

A bare string works too — `as` then defaults to the provider's slug:

```js
requires: ['endpoint:browser/mcp'],
```

## Properties

| Name | Type | Required | Allowed values | Default | Update behavior |
|---|---|---|---|---|---|
| `ref` | string | **Yes** | `<kind>:<provider-slug>/<entry-name>`. All lowercase, letters/digits/hyphens; each part must start with a letter or digit. **`endpoint` is the only kind today** | — | Re-applied. The member is installed and the endpoint attached, idempotently |
| `as` | string | No | letters, digits, `-`, `_`; must start with a letter or digit | the provider slug | Re-applied. This is the handle your code looks the link up by |

`<entry-name>` is a surface the provider **publishes** — `mcp` for an agent that
serves an MCP endpoint.

## What deploy does

1. Adds the provider to this agent's member list, alongside any sub-graph
   children.
2. Installs it (recursively — its own members come too).
3. Records the binding on your agent.
4. Attaches the bound provider's endpoint as a managed MCP server, tagged with
   your alias.

Re-deploying is safe. An existing link is kept as-is; a stale one is replaced;
a link you added by hand to the same provider is adopted rather than duplicated.

## The two ways to consume it

This is the part worth reading twice. Both shipped examples are mechanically
identical to the platform — the attached endpoint is reachable from every node's
model either way. **What differs is the choice your template makes.**

| | **A. Model-driven** | **B. Code-driven** |
|---|---|---|
| Who calls the endpoint | the model, by calling its tools | your node's own code |
| What the node does to resolve it | nothing | looks the link up by its alias |
| What the model sees | tools in its tool list | nothing — just text in the prompt |
| When it is missing | the model is told to skip and say why | one log line, an empty block |
| Reach for it when | the work is open-ended | the retrieval must happen every run |

### A. Model-driven — the model calls the tools

The **Frontend Specialist** agent requires a browser. Its QA node does not
resolve anything: it simply prompts, and the model discovers `browser_navigate`,
`browser_take_screenshot` and the rest in its tool list.

```js
// the declaration
requires: [{ ref: 'endpoint:browser/mcp', as: 'browser' }],

// the node — no MCP wiring at all
const out = await invokeAgent(prompt, { state });
```

The prompt is written to degrade honestly:

> If you do **not** have browser tools, or the preview is unreachable: do not
> fabricate anything and do not mark checks passed. Set `skippedReason`
> explaining exactly what was missing.

So with no browser attached the node returns `qaRan: false` plus an honest
reason, and the run still completes.

**Choose this** when the work is open-ended and the model should decide how many
calls to make.

### B. Code-driven — your code calls it, the model never sees a tool

The **Board Autopilot** manager requires a knowledge base. Its `observe` node
retrieves the top few relevant records *in code* and splices the text into the
briefing. The model is never told a KB exists.

```js
const KB_REQUIRE_AS = 'kb';                        // must equal the `as` above
const KB_MANAGED_BY = `requires:${KB_REQUIRE_AS}`;

// 1. read this agent's own row
//    GET {api}/projects/{PROJECT_ID}/workflows/{WORKFLOW_TYPE}
// 2. find the link the platform attached under our alias
const entry = (row.customMcp || [])
  .find((e) => e && e.managedBy === KB_MANAGED_BY && e.id && e.url);

// 3. call it through the broker
//    POST {api}/mcp/broker/{WORKFLOW_UUID}/{entry.id}   → tools/call
```

If the link is missing, the node logs once, returns an empty block, and the tick
carries on.

**Choose this** when the retrieval is deterministic, must happen on every run,
and the model must not be able to skip it or spend a turn deciding to.

:::tip Pin the alias with a test
A code-driven consumer looks the link up by the literal
`requires:<alias>`. Assert in a test that the constant in your node and the `as`
in your `spec` block are the same string — nothing else checks it.
:::

## Gotchas

**A malformed `ref` is dropped silently.** A wrong kind, a capital letter or an
underscore inside a segment makes the entry vanish at normalisation, with no
diagnostic anywhere — the agent deploys without its capability. Copy a working
ref; do not retype one.

**A bad `as` degrades to the provider slug** rather than failing. That then
breaks a code-driven consumer looking for a different alias.

**The provider has to publish the entry you name.** If it does not, the deploy
fails with a message naming the ref.

**Dangling refs fail the deploy loudly.** Provider not bound, endpoint not
published, link not buildable — each returns an error naming the ref and telling
you to fix it and re-deploy. The cascade is idempotent, so re-deploying
re-attaches.

**At run time nothing throws.** Both patterns degrade: an honest skip, or an
empty block. That is deliberate — a missing capability should not lose the run.

:::caution Self-hosted only, today
`requires` attaches one agent to another over the per-agent MCP endpoint, and
that endpoint exists on self-hosted boxes only. A template with a `requires`
block cannot finish deploying on Zibby Cloud yet.
:::

## See also

- [`surfaces` and `entryPoints`](./surfaces.md) — what a provider must publish.
- [Composition](./composition.md) — the other kind of member; the two share one
  member list.
- [`remoteMcp`](./remote-mcp.md) — the same idea for a third-party server.
