@zibby/skills 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/artifact.js +7 -7
  2. package/dist/chat-notify.js +4 -4
  3. package/dist/chatProgress.js +4 -4
  4. package/dist/datasetStore.js +2 -2
  5. package/dist/discord.js +3 -3
  6. package/dist/figma.js +2 -2
  7. package/dist/git-write.js +9 -9
  8. package/dist/github.js +5 -5
  9. package/dist/gitlab.js +5 -5
  10. package/dist/googleDocs.js +9 -9
  11. package/dist/hubspot.js +1 -1
  12. package/dist/index.js +179 -181
  13. package/dist/jira.d.ts +36 -1
  14. package/dist/jira.js +22 -20
  15. package/dist/kvMemory.js +2 -2
  16. package/dist/lark.js +2 -2
  17. package/dist/larkAttendance.js +1 -1
  18. package/dist/larkDocs.js +3 -3
  19. package/dist/lib/http-deadline.d.ts +117 -0
  20. package/dist/lib/http-deadline.js +1 -0
  21. package/dist/lib/markup.d.ts +158 -0
  22. package/dist/lib/markup.js +22 -0
  23. package/dist/linear.js +15 -15
  24. package/dist/linkedin.js +2 -2
  25. package/dist/llm-billing.js +1 -1
  26. package/dist/notion.js +7 -7
  27. package/dist/opendesign.js +2 -2
  28. package/dist/package.json +6 -5
  29. package/dist/report.d.ts +42 -42
  30. package/dist/review.js +4 -4
  31. package/dist/reviewMemoryIo.js +1 -1
  32. package/dist/sentry.js +2 -2
  33. package/dist/skill-installer.js +3 -3
  34. package/dist/slack.js +2 -2
  35. package/dist/trackers/github-adapter.js +5 -5
  36. package/dist/trackers/index.js +46 -44
  37. package/dist/trackers/jira-adapter.js +24 -22
  38. package/dist/trackers/linear-adapter.js +19 -19
  39. package/dist/trackers/plane-adapter.js +1 -1
  40. package/dist/triggerAgent.js +1 -1
  41. package/dist/vikunja.d.ts +8 -1
  42. package/dist/vikunja.js +22 -7
  43. package/docs/templates/catalog-metadata.md +101 -0
  44. package/docs/templates/composition.md +97 -0
  45. package/docs/templates/deploy-time-config.md +228 -0
  46. package/docs/templates/index.md +138 -0
  47. package/docs/templates/models.md +157 -0
  48. package/docs/templates/node-declarations.md +149 -0
  49. package/docs/templates/remote-mcp.md +105 -0
  50. package/docs/templates/requires.md +183 -0
  51. package/docs/templates/sidecars.md +163 -0
  52. package/docs/templates/stores.md +113 -0
  53. package/docs/templates/surfaces.md +184 -0
  54. package/docs/templates/update-behavior.md +108 -0
  55. package/package.json +6 -5
@@ -0,0 +1,183 @@
1
+ ---
2
+ sidebar_position: 4
3
+ title: requires
4
+ ---
5
+
6
+ # `requires`
7
+
8
+ ## What it is
9
+
10
+ One agent needs a capability another agent publishes — a browser, a knowledge
11
+ base, a bridge to an internal API. `requires` declares that need by **name**.
12
+
13
+ At deploy the platform installs the provider as one of **this agent's own
14
+ members** — the same cascade, the same binding record and the same delete
15
+ cascade a sub-graph child gets — and then attaches the provider's endpoint to
16
+ your agent as a managed MCP server.
17
+
18
+ Nothing concrete is written in the template: no id, no URL, no credential.
19
+
20
+ ## There is no field to put a token in
21
+
22
+ This is the part people look for and do not find, so it is worth stating plainly:
23
+
24
+ - **A consumer declares WHO it uses.** Board Autopilot declares
25
+ `requires: [{ ref: 'endpoint:gbrain-kb/mcp', as: 'kb' }]` — and nothing else.
26
+ - **A provider declares WHAT IT NEEDS.** The knowledge base declares its own
27
+ embedding model, in its own template.
28
+ - **The credential that lets one call the other is in neither declaration.** The
29
+ platform mints it, encrypts it into the consumer's own environment, and never
30
+ returns it, logs it, or shows it to a model.
31
+
32
+ So there is no token field, no shared secret to distribute and nothing for you
33
+ to rotate. If you find yourself wanting to write a credential into a template,
34
+ the design has gone wrong somewhere — a declaration is public data that travels
35
+ with the card.
36
+
37
+ :::info Your agent installs its own members
38
+ A consumer never borrows a provider that happens to already exist in the
39
+ project. It installs its own, always. The heavy engine underneath is shared per
40
+ box anyway, so the extra agent row is just configuration.
41
+ :::
42
+
43
+ ## Syntax
44
+
45
+ ```js
46
+ spec: {
47
+ requires: [{ ref: 'endpoint:browser/mcp', as: 'browser' }],
48
+ }
49
+ ```
50
+
51
+ A bare string works too — `as` then defaults to the provider's slug:
52
+
53
+ ```js
54
+ requires: ['endpoint:browser/mcp'],
55
+ ```
56
+
57
+ ## Properties
58
+
59
+ | Name | Type | Required | Allowed values | Default | Update behavior |
60
+ |---|---|---|---|---|---|
61
+ | `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 |
62
+ | `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 |
63
+
64
+ `<entry-name>` is a surface the provider **publishes** — `mcp` for an agent that
65
+ serves an MCP endpoint.
66
+
67
+ ## What deploy does
68
+
69
+ 1. Adds the provider to this agent's member list, alongside any sub-graph
70
+ children.
71
+ 2. Installs it (recursively — its own members come too).
72
+ 3. Records the binding on your agent.
73
+ 4. Attaches the bound provider's endpoint as a managed MCP server, tagged with
74
+ your alias.
75
+
76
+ Re-deploying is safe. An existing link is kept as-is; a stale one is replaced;
77
+ a link you added by hand to the same provider is adopted rather than duplicated.
78
+
79
+ ## The two ways to consume it
80
+
81
+ This is the part worth reading twice. Both shipped examples are mechanically
82
+ identical to the platform — the attached endpoint is reachable from every node's
83
+ model either way. **What differs is the choice your template makes.**
84
+
85
+ | | **A. Model-driven** | **B. Code-driven** |
86
+ |---|---|---|
87
+ | Who calls the endpoint | the model, by calling its tools | your node's own code |
88
+ | What the node does to resolve it | nothing | looks the link up by its alias |
89
+ | What the model sees | tools in its tool list | nothing — just text in the prompt |
90
+ | When it is missing | the model is told to skip and say why | one log line, an empty block |
91
+ | Reach for it when | the work is open-ended | the retrieval must happen every run |
92
+
93
+ ### A. Model-driven — the model calls the tools
94
+
95
+ The **Frontend Specialist** agent requires a browser. Its QA node does not
96
+ resolve anything: it simply prompts, and the model discovers `browser_navigate`,
97
+ `browser_take_screenshot` and the rest in its tool list.
98
+
99
+ ```js
100
+ // the declaration
101
+ requires: [{ ref: 'endpoint:browser/mcp', as: 'browser' }],
102
+
103
+ // the node — no MCP wiring at all
104
+ const out = await invokeAgent(prompt, { state });
105
+ ```
106
+
107
+ The prompt is written to degrade honestly:
108
+
109
+ > If you do **not** have browser tools, or the preview is unreachable: do not
110
+ > fabricate anything and do not mark checks passed. Set `skippedReason`
111
+ > explaining exactly what was missing.
112
+
113
+ So with no browser attached the node returns `qaRan: false` plus an honest
114
+ reason, and the run still completes.
115
+
116
+ **Choose this** when the work is open-ended and the model should decide how many
117
+ calls to make.
118
+
119
+ ### B. Code-driven — your code calls it, the model never sees a tool
120
+
121
+ The **Board Autopilot** manager requires a knowledge base. Its `observe` node
122
+ retrieves the top few relevant records *in code* and splices the text into the
123
+ briefing. The model is never told a KB exists.
124
+
125
+ ```js
126
+ const KB_REQUIRE_AS = 'kb'; // must equal the `as` above
127
+ const KB_MANAGED_BY = `requires:${KB_REQUIRE_AS}`;
128
+
129
+ // 1. read this agent's own row
130
+ // GET {api}/projects/{PROJECT_ID}/workflows/{WORKFLOW_TYPE}
131
+ // 2. find the link the platform attached under our alias
132
+ const entry = (row.customMcp || [])
133
+ .find((e) => e && e.managedBy === KB_MANAGED_BY && e.id && e.url);
134
+
135
+ // 3. call it through the broker
136
+ // POST {api}/mcp/broker/{WORKFLOW_UUID}/{entry.id} → tools/call
137
+ ```
138
+
139
+ If the link is missing, the node logs once, returns an empty block, and the tick
140
+ carries on.
141
+
142
+ **Choose this** when the retrieval is deterministic, must happen on every run,
143
+ and the model must not be able to skip it or spend a turn deciding to.
144
+
145
+ :::tip Pin the alias with a test
146
+ A code-driven consumer looks the link up by the literal
147
+ `requires:<alias>`. Assert in a test that the constant in your node and the `as`
148
+ in your `spec` block are the same string — nothing else checks it.
149
+ :::
150
+
151
+ ## Gotchas
152
+
153
+ **A malformed `ref` is dropped silently.** A wrong kind, a capital letter or an
154
+ underscore inside a segment makes the entry vanish at normalisation, with no
155
+ diagnostic anywhere — the agent deploys without its capability. Copy a working
156
+ ref; do not retype one.
157
+
158
+ **A bad `as` degrades to the provider slug** rather than failing. That then
159
+ breaks a code-driven consumer looking for a different alias.
160
+
161
+ **The provider has to publish the entry you name.** If it does not, the deploy
162
+ fails with a message naming the ref.
163
+
164
+ **Dangling refs fail the deploy loudly.** Provider not bound, endpoint not
165
+ published, link not buildable — each returns an error naming the ref and telling
166
+ you to fix it and re-deploy. The cascade is idempotent, so re-deploying
167
+ re-attaches.
168
+
169
+ **At run time nothing throws.** Both patterns degrade: an honest skip, or an
170
+ empty block. That is deliberate — a missing capability should not lose the run.
171
+
172
+ :::caution Self-hosted only, today
173
+ `requires` attaches one agent to another over the per-agent MCP endpoint, and
174
+ that endpoint exists on self-hosted boxes only. A template with a `requires`
175
+ block cannot finish deploying on Zibby Cloud yet.
176
+ :::
177
+
178
+ ## See also
179
+
180
+ - [`surfaces` and `entryPoints`](./surfaces.md) — what a provider must publish.
181
+ - [Composition](./composition.md) — the other kind of member; the two share one
182
+ member list.
183
+ - [`remoteMcp`](./remote-mcp.md) — the same idea for a third-party server.
@@ -0,0 +1,163 @@
1
+ ---
2
+ sidebar_position: 7
3
+ title: sidecars & sidecarSpecs
4
+ ---
5
+
6
+ # `sidecarSpecs` and `sidecars`
7
+
8
+ ## What it is
9
+
10
+ Some agents need a long-lived service running beside them: a browser, a vector
11
+ database, a bridge that turns a REST API into tools. Those are **sidecars**.
12
+
13
+ `sidecarSpecs` carries the **whole specification** — name, a checksum-pinned
14
+ image on Zibby's own CDN, the port, the per-tenant config it reads — rather than
15
+ a name the platform must already know. That is what makes a brand-new
16
+ sidecar-backed agent *a template and nothing else*.
17
+
18
+ `sidecars` is the short form: a name for a sidecar the platform already ships.
19
+
20
+ At deploy the control plane fetches the image, **verifies its SHA-256**, loads it
21
+ and starts it on the internal network. The agent's run container never pulls
22
+ anything and never dials it directly.
23
+
24
+ :::caution Self-hosted only
25
+ Sidecar hosting is not built on Zibby Cloud yet. A template that declares one is
26
+ marked self-hosted-only and the cloud catalog will not deploy it. There is one
27
+ exception, [below](#the-one-cloud-exception).
28
+ :::
29
+
30
+ ## Syntax
31
+
32
+ ```js
33
+ spec: {
34
+ selfHostOnly: true,
35
+ sidecarSpecs: [{
36
+ name: 'openapi-mcp',
37
+ version: '0.2.3',
38
+ s3Url: 'https://dl.zibby.app/sidecars/openapi-mcp/0.2.3.tar.gz',
39
+ sha256: 'caa4ebfc04acf725f3afd0afa8d0ddf942a600eea364ec821351e3f19af53b4f',
40
+ bytes: 2500336, // informational — shown in the dialog
41
+ arch: {
42
+ arm64: {
43
+ s3Url: 'https://dl.zibby.app/sidecars/openapi-mcp/0.2.3-arm64.tar.gz',
44
+ sha256: '38fe9ca08961eed8c2768e3d036da6deaf4ca2384543ea243deb9685a77ca566',
45
+ bytes: 2305415,
46
+ },
47
+ },
48
+ port: 8080,
49
+ healthPath: '/health',
50
+ warm: true,
51
+ requestConfigKeys: ['OPENAPI_APIS', 'API_ROOT', 'API_TOKEN'],
52
+ }],
53
+ // NO `sidecars: ['openapi-mcp']` — a full spec implies its own name.
54
+ }
55
+ ```
56
+
57
+ ## Properties
58
+
59
+ | Name | Type | Required | Allowed values | Default | Update behavior |
60
+ |---|---|---|---|---|---|
61
+ | `name` | string | **Yes** | lowercase, starts with a letter, letters/digits/hyphens, 2–31 chars. May not shadow a built-in name | — | Re-applied |
62
+ | `port` | integer | **Yes** | 1–65535 | — | Re-applied |
63
+ | `sha256` | string | **Yes** | 64 hex characters | — | Re-applied — a new checksum pulls a new image |
64
+ | `s3Url` | string | **Yes** | an allow-listed host | — | Re-applied |
65
+ | `version` | string | No | a version string you own | none | Re-applied; shown in the Update dialog |
66
+ | `arch.<arch>` | object | No | `{ s3Url, sha256, bytes }`, validated identically | none | Re-applied |
67
+ | `healthPath` | string | No | a path | `/health` | Re-applied |
68
+ | `warm` | boolean | No | | `false` | Re-applied |
69
+ | `bytes` | integer | No | **informational only** — never checked against the download | none | Card only |
70
+ | `dataPath` | string | No | an absolute path — and it must be `/data` | none | Re-applied |
71
+ | `dataPurpose` | string | No | `data`, `credentials` | `data` | Re-applied — decides what uninstall deletes |
72
+ | `requestConfigKeys` | string[] | No | Env keys read **per request** from the declaring agent's own encrypted bag | `[]` | Re-applied |
73
+ | `envKeys` | string[] | No | container-wide environment | `[]` | Re-applied |
74
+ | `publicPaths` | string[] | No | prefixes exposed **anonymously** on your box's public origin — refused by default | none | Re-applied |
75
+ | `storeTypes` | string[] | No | store types this sidecar serves | `[]` | Re-applied |
76
+ | `instancePerAgent` | boolean | No | | `false` | Re-applied |
77
+ | `memoryBytes` / `pidsLimit` | integer | No | 64 MiB – 8 GiB / 16 – 4096 | platform defaults | Re-applied |
78
+
79
+ `sidecars` is simply `string[]`. A name nobody knows is skipped with a warning,
80
+ not an error.
81
+
82
+ ## One shared service, many isolated tenants
83
+
84
+ A sidecar is **one process hosting N isolated tenants**, not one container per
85
+ agent. Everything that differs per agent — data *and* configuration — is
86
+ isolated:
87
+
88
+ - **Data** is keyed by a server-derived tenant key, so different tenants run in
89
+ parallel and the same tenant serialises.
90
+ - **Configuration and secrets** come from `requestConfigKeys`, injected **per
91
+ request** from the declaring agent's encrypted Env bag — never as
92
+ container-wide environment.
93
+
94
+ That last point is the one people get wrong. Putting an OAuth client id and
95
+ secret in `envKeys` means two projects with different apps collide, and the fix
96
+ is not "error on conflict" — it is per-request injection.
97
+
98
+ ## Gotchas
99
+
100
+ :::danger A full spec implies its name
101
+ If you declare `sidecarSpecs`, do **not** also list that name in `sidecars`.
102
+ The name is taken from the spec. Repeating it is a second place to keep in sync.
103
+ :::
104
+
105
+ :::danger Durable state lives at `/data` — one path, every sidecar
106
+ The `dataPath` you declare and the path your app actually writes must be the
107
+ **same string**. Nothing cross-checks them, so a mismatch is silent: the data
108
+ lands in the container's throwaway layer and dies with it, while the volume you
109
+ declared sits empty. A live OAuth token store was lost exactly that way. Do not
110
+ "fix" a mismatch by injecting a path variable — change one of them.
111
+ :::
112
+
113
+ **A checksum pins bytes, not a CPU.** There is deliberately no fallback between
114
+ architectures: an x86 image verifies perfectly on an ARM box and then dies with
115
+ `exec format error`. Publish and pin each architecture, or that architecture
116
+ fails loudly.
117
+
118
+ **A URL with no checksum fails closed.** The image is never loaded unverified.
119
+
120
+ **A template-carried sidecar does not auto-update.** Its version belongs to you:
121
+ it moves when your `version`/`sha256`/`s3Url` move and the user takes the
122
+ Update. Platform-shipped sidecars follow a CDN channel instead.
123
+
124
+ :::danger A sidecar with no authentication of its own must not declare `publicPaths`
125
+ `publicPaths` exposes those prefixes **anonymously** on your box's public
126
+ origin, with no platform authentication added. Agents and the chat assistant
127
+ reach a sidecar over the internal network and do not need it.
128
+
129
+ Declare an [`entryPoints.mcp`](./surfaces.md) with `auth: 'bearer-pat'` instead,
130
+ and let the platform's own authenticated endpoint forward to you — it adds the
131
+ token check, the ownership check, and per-request tenant configuration.
132
+
133
+ It also costs you cloud deployability. See below.
134
+ :::
135
+
136
+ **`warm: true` is sometimes required, not an optimisation.** A sidecar is
137
+ started by the boot pre-warmer, by a public-path proxy, or by the store router.
138
+ An agent that is dialled directly as an MCP server matches none of those, so
139
+ without `warm` it would never be running.
140
+
141
+ **Share a spec, do not copy it.** If several templates use the same sidecar,
142
+ import the spec from one place. Two inlined copies at different versions will
143
+ fight, and the last deploy wins — which can silently downgrade the shared engine.
144
+
145
+ ## The one cloud exception
146
+
147
+ A self-hosted-only template **is** deployable on Zibby Cloud when it is
148
+ browser-host-servable — judged from the declaration, never from the name:
149
+
150
+ - every carried spec is fully pinned (`name`, `version`, `port`, `s3Url`,
151
+ `sha256`), **and**
152
+ - no spec declares `publicPaths`, **and**
153
+ - the template declares an `entryPoints.mcp` pointing at one of its sidecars.
154
+
155
+ One `publicPaths` entry costs the template its cloud deployability.
156
+
157
+ ## See also
158
+
159
+ - [`surfaces` and `entryPoints`](./surfaces.md)
160
+ - [`stores`](./stores.md) — a store type brings its own sidecar; do not name it
161
+ twice.
162
+ - [Bring your own sidecar](../self-host/custom-sidecars.md) — the operator route,
163
+ for a service that is not a catalog agent.
@@ -0,0 +1,113 @@
1
+ ---
2
+ sidebar_position: 3
3
+ title: stores
4
+ ---
5
+
6
+ # `stores`
7
+
8
+ ## What it is
9
+
10
+ Durable storage the agent needs. Declared **on a node**, not in the
11
+ `spec` block.
12
+
13
+ At deploy the platform creates (or re-uses) one store per declaration and writes
14
+ its id into the agent's encrypted Env bag under the name you chose. Your code
15
+ never sees an id in the template — it asks for the name and gets the resource.
16
+
17
+ This is the reference implementation of the naming rule: declare a name, the
18
+ platform binds it, the consumer resolves it late.
19
+
20
+ ## Syntax
21
+
22
+ ```js
23
+ // nodes/ingest-node.js
24
+ import { SKILLS } from '@zibby/core';
25
+
26
+ export const ingestNode = {
27
+ name: 'ingest',
28
+ skills: [SKILLS.DATASET_STORE], // required — see gotchas
29
+ stores: [
30
+ { name: 'knowledge_docs', type: 'docs', description: 'Verbatim markdown archive' },
31
+ { name: 'knowledge_meta', type: 'sqlite', description: 'Per-document metadata',
32
+ schema: {
33
+ tables: {
34
+ knowledge_meta: {
35
+ ddl: 'CREATE TABLE IF NOT EXISTS knowledge_meta (id TEXT PRIMARY KEY, title TEXT)',
36
+ },
37
+ },
38
+ },
39
+ },
40
+ ],
41
+ };
42
+ ```
43
+
44
+ The minimal form is just a name and a description:
45
+
46
+ ```js
47
+ stores: [{ name: 'scorecards', description: 'Per-commit engineering scorecard' }],
48
+ ```
49
+
50
+ ## Properties
51
+
52
+ | Name | Type | Required | Allowed values | Default | Update behavior |
53
+ |---|---|---|---|---|---|
54
+ | `name` | string | **Yes** | lowercase, starts with a letter, letters/digits/underscore, ≤ 41 chars. **Unique across the whole agent**, not just the node | — | Re-applied (idempotent). A **new** name creates a **new** store; renaming leaves the old one behind |
55
+ | `type` | string | No | `dataset`, `sqlite`, `file`, `docs`, `postgres` (self-hosted only) | `dataset` | Re-applied. An unknown type fails the deploy loudly |
56
+ | `description` | string | No | any — it is what the agent's store catalogue shows the model | none | Re-applied |
57
+ | `schema` | object | No | `{ tables: { <name>: { ddl } } }` — **`sqlite` only**. Each `ddl` must begin `CREATE TABLE IF NOT EXISTS` and the table it creates must match the key | none | Re-applied, but an existing table is never altered |
58
+
59
+ ## Store types
60
+
61
+ | Type | Use it for |
62
+ |---|---|
63
+ | `dataset` | append-and-query records — the default |
64
+ | `sqlite` | relational data with a schema you declare |
65
+ | `file` | opaque blobs |
66
+ | `docs` | a verbatim document archive |
67
+ | `postgres` | a vector knowledge base (self-hosted only) |
68
+
69
+ ## What deploy does
70
+
71
+ 1. Computes a stable key from the agent, the node and the name, so re-deploying
72
+ re-uses the same store instead of creating another one.
73
+ 2. Creates the store if it does not exist.
74
+ 3. Writes `ZIBBY_STORE__<name> = <store id>` into the encrypted Env bag.
75
+ 4. Creates any declared SQLite tables.
76
+ 5. Seeds the node's store catalogue so the model can see what it has.
77
+
78
+ At run time the store skill turns those environment entries back into names, and
79
+ sub-graph children inherit them.
80
+
81
+ ## Gotchas
82
+
83
+ :::danger A `stores` block on a node with no store skill is silently ignored
84
+ The same node must also declare a store skill (`dataset-store`, or `gbrain` for
85
+ a vector KB) in its **`skills`** array. Putting it in `optionalSkills` instead
86
+ provisions nothing — no store, no error, no warning.
87
+ :::
88
+
89
+ **Names are unique per agent, not per node.** Two nodes cannot each declare a
90
+ store called `results`.
91
+
92
+ **If you re-point a store by hand, that wins forever.** Setting
93
+ `ZIBBY_STORE__<name>` yourself to something other than the provisioned id tells
94
+ the platform this is deliberate: it stops re-binding the name and stops applying
95
+ the declared schema.
96
+
97
+ **A schema on a non-SQLite store is a hard error at deploy**, not a warning.
98
+
99
+ **Declared tables are created, never migrated.** If a table already exists it is
100
+ left exactly as it is, even when your `ddl` has changed. Adding a column is your
101
+ migration to write.
102
+
103
+ **A store type brings its own service.** `postgres` pulls the vector-KB engine
104
+ by itself — do not also name it under [`sidecars`](./sidecars.md).
105
+
106
+ **`postgres` cannot be deployed on Zibby Cloud today.** Templates that need it
107
+ are marked self-hosted-only.
108
+
109
+ ## See also
110
+
111
+ - [Node declarations](./node-declarations.md) — the store skill is a
112
+ precondition, not a nicety.
113
+ - [Sidecars](./sidecars.md)
@@ -0,0 +1,184 @@
1
+ ---
2
+ sidebar_position: 6
3
+ title: surfaces & entryPoints
4
+ ---
5
+
6
+ # `surfaces` and `entryPoints`
7
+
8
+ Two blocks, one subject: **which channels your agent offers, and where each one
9
+ lives.**
10
+
11
+ - `surfaces` — *which* channels the agent's page should show.
12
+ - `entryPoints` — *where* a channel lives when it is not served by the platform
13
+ itself, and every word the card shows.
14
+
15
+ ---
16
+
17
+ ## `surfaces`
18
+
19
+ ### What it is
20
+
21
+ The deployed agent's page renders one section per interaction channel.
22
+ `surfaces` says which of them are meaningful, so a pure knowledge-base agent
23
+ does not show a Trigger button that does nothing.
24
+
25
+ ### Syntax
26
+
27
+ ```js
28
+ spec: { surfaces: ['mcp', 'connect'] }
29
+ ```
30
+
31
+ ### Properties
32
+
33
+ | Name | Type | Required | Allowed values | Default | Update behavior |
34
+ |---|---|---|---|---|---|
35
+ | `surfaces` | string[] | No | `cli`, `webhook`, `schedule`, `mcp`, `connect` | `['cli','webhook','schedule','mcp']` | Re-applied |
36
+
37
+ | Value | Meaning |
38
+ |---|---|
39
+ | `cli` | started from the command line |
40
+ | `webhook` | started by an inbound HTTP call |
41
+ | `schedule` | started by cron |
42
+ | `mcp` | a machine endpoint an MCP client attaches to |
43
+ | `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 |
44
+
45
+ `connect` is not a webhook: nothing third-party posts to it and it starts no run.
46
+
47
+ Unknown values are ignored. Declaring nothing gives you the four standard
48
+ channels — **not** all five, because `connect` is opt-in by design.
49
+
50
+ ### Use cases
51
+
52
+ | Agent shape | Declare |
53
+ |---|---|
54
+ | An ordinary triggerable workflow | nothing |
55
+ | A pure store — running it directly is a no-op, it is driven over MCP | `['mcp']` |
56
+ | A service a person signs into, then uses from their editor | `['mcp', 'connect']` |
57
+
58
+ ### Gotchas
59
+
60
+ Declaring `mcp` does not create an endpoint — it says the agent has one. The
61
+ endpoint still has to exist.
62
+
63
+ ---
64
+
65
+ ## `entryPoints`
66
+
67
+ ### What it is
68
+
69
+ For a channel that need not be served by the platform, `entryPoints` names which
70
+ service serves it and at which path. **The URL itself is derived when the page
71
+ is read** — from the service's resolved public mount and your installation's
72
+ origin. No template ever writes a URL down.
73
+
74
+ For `connect` it also carries every word on the card, because the renderer has
75
+ none of its own.
76
+
77
+ ### Syntax
78
+
79
+ An MCP endpoint the platform authenticates and forwards to a service:
80
+
81
+ ```js
82
+ spec: {
83
+ surfaces: ['mcp'],
84
+ entryPoints: {
85
+ mcp: {
86
+ sidecar: 'openapi-mcp',
87
+ path: '/mcp',
88
+ auth: 'bearer-pat',
89
+ label: 'MCP endpoint for the APIs this agent bridges',
90
+ },
91
+ },
92
+ }
93
+ ```
94
+
95
+ A sign-in the platform drives, on an agent with no service container at all:
96
+
97
+ ```js
98
+ spec: {
99
+ surfaces: ['mcp', 'connect'],
100
+ entryPoints: {
101
+ connect: {
102
+ kind: 'oauth',
103
+ server: 'figma', // a name from this agent's own remoteMcp block
104
+ label: 'Connect your Figma account',
105
+ button: 'Connect',
106
+ buttonDisconnect: 'Disconnect',
107
+ disconnectConfirm: 'Disconnect Figma? The stored sign-in is deleted.',
108
+ connectedLine: 'Connected to Figma as {account}.',
109
+ },
110
+ // NO `mcp` entry — the absence IS the declaration. See below.
111
+ },
112
+ }
113
+ ```
114
+
115
+ ### Properties
116
+
117
+ Only `mcp` and `connect` can be redirected. `cli`, `webhook` and `schedule` are
118
+ platform mechanics with no alternative home.
119
+
120
+ | Name | Type | Required | Allowed values | Default | Update behavior |
121
+ |---|---|---|---|---|---|
122
+ | `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 |
123
+ | `path` | string | **Yes**, same exception | must start with `/`; `..` is refused | — | Re-applied |
124
+ | `auth` | string | No | `bearer-pat`, `connect-bearer`, `none` | `connect-bearer` for `mcp`, `none` for `connect` | Re-applied |
125
+ | `label` | string | No | any | none | Card only |
126
+ | `kind` (connect) | string | No | `sidecar-page`, `oauth` | `sidecar-page` | Re-applied |
127
+ | `server` (connect + `oauth`) | string | **Yes** for that kind | a name from `remoteMcp` | — | Re-applied |
128
+ | copy (`button`, `buttonDisconnect`, `disconnectConfirm`, `disconnectedLine`, `waitingLine`, `connectedLine`, `note`) | string | No | `{account}` is substituted | none — a field you omit renders nothing | Card only |
129
+
130
+ ### `auth` names what the CALLER presents
131
+
132
+ | Value | The caller presents |
133
+ |---|---|
134
+ | `bearer-pat` | a Zibby access token — the platform authenticates and forwards |
135
+ | `connect-bearer` | the token this agent's own sign-in handed the user |
136
+ | `none` | nothing — the surface authenticates the caller itself |
137
+
138
+ :::warning A misspelt `auth` is silently corrected, not rejected
139
+ An unrecognised value falls back to the channel's default, so a typo quietly
140
+ describes an authentication your endpoint does not use. Spell it exactly.
141
+ :::
142
+
143
+ ### An absent `entryPoints.mcp` is itself a declaration
144
+
145
+ Leave it out and **the platform serves the surface itself**, at the agent's own
146
+ authenticated MCP address, with a Zibby bearer token.
147
+
148
+ That is the right choice whenever your service has no authentication of its own.
149
+ Naming a source when you did not need to is how an endpoint gets taken away from
150
+ the sign-in that feeds it: the declared service wins the address and answers
151
+ every call with its own "missing token".
152
+
153
+ :::tip One agent, one MCP address
154
+ A declared MCP service takes the **whole** surface — other MCP servers attached
155
+ to the same agent are not merged into it. If you need both, you need two agents.
156
+ :::
157
+
158
+ ### Use cases
159
+
160
+ | You want | Declare |
161
+ |---|---|
162
+ | Platform-served MCP with a Zibby token | `surfaces: ['mcp']` and nothing else |
163
+ | MCP forwarded to your own service, platform-authenticated | `entryPoints.mcp` with `auth: 'bearer-pat'` |
164
+ | A service that runs its own OAuth and issues its own bearer | `connect` with `auth: 'none'` **and** `mcp` with `auth: 'connect-bearer'` |
165
+ | A hosted third-party MCP the platform signs you into | `connect` with `kind: 'oauth'`, and **no** `mcp` entry |
166
+
167
+ ### Gotchas
168
+
169
+ **Both halves are required.** A channel must be in `surfaces` *and* have an
170
+ `entryPoints` entry before anything is derived.
171
+
172
+ **A path the proxy would not serve is never advertised.** If the mount does not
173
+ publish it, the entry is dropped rather than shown as a dead link.
174
+
175
+ **`connect-bearer` turns off the platform's own token check** on the agent's MCP
176
+ address. Only declare it when your service really does authenticate.
177
+
178
+ **Service-served entries need a self-hosted box.** A `connect` entry with
179
+ `kind: 'oauth'` works everywhere.
180
+
181
+ ## See also
182
+
183
+ - [`sidecars`](./sidecars.md), [`remoteMcp`](./remote-mcp.md),
184
+ [`requires`](./requires.md)