@zackbart/connecta 0.24.2 → 0.24.4
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.
- package/AGENTS.md +18 -20
- package/CHANGELOG.md +205 -1
- package/README.md +5 -6
- package/dist/auth/bearer.js +2 -0
- package/dist/auth/downstream-oauth.d.ts +12 -1
- package/dist/auth/downstream-oauth.js +147 -35
- package/dist/branding.d.ts +31 -2
- package/dist/branding.js +116 -8
- package/dist/call-admission.d.ts +4 -0
- package/dist/call-admission.js +26 -0
- package/dist/catalog-drift.js +9 -4
- package/dist/catalog-service.d.ts +2 -0
- package/dist/catalog-service.js +25 -8
- package/dist/catalog.d.ts +2 -0
- package/dist/catalog.js +246 -121
- package/dist/connectors/api.d.ts +1 -1
- package/dist/connectors/api.js +21 -3
- package/dist/connectors/guarded-fetch.d.ts +6 -2
- package/dist/connectors/guarded-fetch.js +61 -24
- package/dist/connectors/remote-mcp.js +92 -57
- package/dist/errors.d.ts +28 -3
- package/dist/errors.js +60 -1
- package/dist/execute.js +85 -23
- package/dist/executor-result.js +3 -1
- package/dist/executors/quickjs-child.js +5 -1
- package/dist/executors/quickjs-protocol.d.ts +4 -0
- package/dist/executors/quickjs-runtime.d.ts +1 -1
- package/dist/executors/quickjs-runtime.js +38 -21
- package/dist/executors/quickjs.js +68 -27
- package/dist/index.d.ts +15 -1
- package/dist/index.js +36 -4
- package/dist/invocation.js +134 -93
- package/dist/mcp-result.js +3 -2
- package/dist/meta-tools.js +191 -36
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/view.d.ts +38 -1
- package/dist/operator-ui/view.js +71 -0
- package/dist/providers/cloudflare.d.ts +14 -2
- package/dist/providers/cloudflare.js +107 -16
- package/dist/providers/linear.d.ts +26 -4
- package/dist/providers/linear.js +19 -4
- package/dist/providers/mixpanel.d.ts +16 -3
- package/dist/providers/mixpanel.js +13 -2
- package/dist/providers/notion.d.ts +8 -1
- package/dist/providers/notion.js +83 -10
- package/dist/providers/revenuecat.d.ts +30 -4
- package/dist/providers/revenuecat.js +42 -4
- package/dist/providers/stripe.d.ts +7 -1
- package/dist/providers/stripe.js +30 -4
- package/dist/providers/vercel.js +11 -1
- package/dist/registry.d.ts +22 -2
- package/dist/registry.js +101 -13
- package/dist/routes/mcp.d.ts +4 -1
- package/dist/routes/mcp.js +84 -13
- package/dist/routes/oauth.js +4 -0
- package/dist/routes/shared.d.ts +1 -0
- package/dist/routes/shared.js +4 -4
- package/dist/server.js +15 -3
- package/dist/skills.js +6 -5
- package/dist/storage/file.d.ts +6 -2
- package/dist/storage/file.js +312 -34
- package/dist/storage/memory.js +12 -1
- package/dist/types.d.ts +37 -0
- package/dist/ui.js +18 -10
- package/dist/validate.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +193 -165
- package/documentation/auth.md +199 -145
- package/documentation/code-mode.md +433 -316
- package/documentation/meta-tools.md +363 -355
- package/examples/worker/AGENTS.md +2 -1
- package/examples/worker/README.md +12 -10
- package/examples/worker/src/index.ts +12 -15
- package/package.json +1 -2
- package/templates/node/.env.example +3 -3
- package/templates/node/AGENTS.md +5 -4
- package/templates/node/README.md +2 -1
- package/templates/node/package.json +1 -1
- package/templates/node/src/index.ts +23 -22
- package/documentation/call-admission.md +0 -142
- package/documentation/cloudflare.md +0 -471
- package/documentation/connector-guides.md +0 -176
- package/documentation/connectors.md +0 -389
- package/documentation/linear.md +0 -193
- package/documentation/mixpanel.md +0 -160
- package/documentation/notion.md +0 -308
- package/documentation/operations.md +0 -359
- package/documentation/operator-ui.md +0 -135
- package/documentation/optional-modules-upgrade.md +0 -243
- package/documentation/provider-conventions.md +0 -722
- package/documentation/request-admission.md +0 -170
- package/documentation/revenuecat.md +0 -305
- package/documentation/storage-and-credentials.md +0 -201
- package/documentation/stripe.md +0 -262
- package/documentation/upgrading.md +0 -754
- package/documentation/vercel.md +0 -241
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
# Cloudflare prebuilt connection
|
|
2
|
-
|
|
3
|
-
Import `cloudflare()` independently from
|
|
4
|
-
`@zackbart/connecta/providers/cloudflare`. The deployment chooses one of two
|
|
5
|
-
interfaces. The default is a deliberate, hand-written surface over
|
|
6
|
-
Cloudflare's v4 REST API. Its fifty-one tools combine ergonomic, fully
|
|
7
|
-
described operations for common work with three guarded escape hatches for the
|
|
8
|
-
rest of Cloudflare's fast-moving control plane. The other choice is
|
|
9
|
-
Cloudflare's official whole-API hosted MCP, which exposes `search` and
|
|
10
|
-
`execute` with live provider-owned schemas. Both are ordinary connectors and
|
|
11
|
-
neither is reachable from Connecta's root entry.
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { cloudflare } from "@zackbart/connecta/providers/cloudflare";
|
|
15
|
-
|
|
16
|
-
const edge = cloudflare("cloudflare_prod", {
|
|
17
|
-
surface: "api", // optional; this is the backward-compatible default
|
|
18
|
-
title: "Production edge",
|
|
19
|
-
purpose: "DNS and cache administration for the production estate",
|
|
20
|
-
zoneId: "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
|
|
21
|
-
accountId: "9f8e7d6c5b4a30291817263544332211",
|
|
22
|
-
instructions: "Never purge the whole zone during business hours.",
|
|
23
|
-
});
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Use Cloudflare's hosted code-mode interface instead:
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
const wholeApi = cloudflare("cloudflare_mcp", {
|
|
30
|
-
surface: "mcp",
|
|
31
|
-
purpose: "Cloudflare administration outside the curated REST workflows",
|
|
32
|
-
});
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Use the legacy user-scoped Global API Key when an existing deployment needs it:
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
const legacyEdge = cloudflare("cloudflare_legacy", {
|
|
39
|
-
purpose: "Legacy account administration",
|
|
40
|
-
authentication: "globalApiKey",
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
The operator UI then asks for the Cloudflare user email and Global API Key as
|
|
45
|
-
separate fields. The default remains a scoped API token.
|
|
46
|
-
|
|
47
|
-
The `id` owns the ordinary connector namespaces; use a different id for every
|
|
48
|
-
Cloudflare account or estate. `purpose` is required because an agent choosing
|
|
49
|
-
between a production and a staging instance needs to know which one answers the
|
|
50
|
-
question. Account `instructions` are appended to the maintained guide and
|
|
51
|
-
cannot change the connector's safety classification.
|
|
52
|
-
|
|
53
|
-
## Choosing an interface
|
|
54
|
-
|
|
55
|
-
Use the API interface when its projected named tools cover the work. Connecta
|
|
56
|
-
owns those schemas, projections, typed errors, pagination, and the split
|
|
57
|
-
between read-only and mutating escape hatches.
|
|
58
|
-
|
|
59
|
-
Use `surface: "mcp"` when broad product coverage matters more than projected
|
|
60
|
-
results. Cloudflare's official server covers more than 2,500 API endpoints
|
|
61
|
-
through two code-mode tools. `search` reads the OpenAPI document and is
|
|
62
|
-
read-only. `execute` can run a program containing any authorized HTTP method,
|
|
63
|
-
so Connecta always routes it through approval. A program that happens to use
|
|
64
|
-
only GET cannot be proven observational from the tool schema.
|
|
65
|
-
|
|
66
|
-
The MCP catalog and schemas come from the live server. The release manifest
|
|
67
|
-
classifies the two known names but does not replace their schemas. OAuth is the
|
|
68
|
-
default. A headless deployment may instead pass `auth` with a scoped API token.
|
|
69
|
-
The credential remains the provider-side permission boundary either way. The
|
|
70
|
-
MCP interface accepts `callAdmission` when the deployment has a concurrency or
|
|
71
|
-
call-rate requirement; it does not reuse the API interface's REST-wide budget.
|
|
72
|
-
|
|
73
|
-
The remaining sections document the hand-written API interface. MCP tool
|
|
74
|
-
arguments and results are intentionally read from the live server instead.
|
|
75
|
-
|
|
76
|
-
## No SDK, on purpose
|
|
77
|
-
|
|
78
|
-
Cloudflare publishes an official `cloudflare` npm SDK, and this connection does
|
|
79
|
-
not use it. The SDK's value is typed request wrappers and pagination helpers.
|
|
80
|
-
Both are things this connection replaces rather than consumes: an agent needs a
|
|
81
|
-
projected result and a `page.hasMore` boolean, not Cloudflare's full response
|
|
82
|
-
object, so the SDK's types would be re-projected away at the boundary. What the
|
|
83
|
-
dependency would cost is real — an optional peer with its own install step and
|
|
84
|
-
version skew, an import that never belongs in the root graph, and a second
|
|
85
|
-
opinion about what a Cloudflare call looks like.
|
|
86
|
-
|
|
87
|
-
The API itself does not need one. It is authenticated `fetch` with a uniform
|
|
88
|
-
`{ success, errors, messages, result, result_info }` response envelope. JSON,
|
|
89
|
-
raw bytes, and multipart request bodies all use Web APIs, which keeps the
|
|
90
|
-
provider Workers-clean and means `@zackbart/connecta/providers/cloudflare`
|
|
91
|
-
installs and runs with nothing extra. `test/package-surface.test.ts` pins the
|
|
92
|
-
claim: the `cloudflare` package must not appear in `dependencies`,
|
|
93
|
-
`peerDependencies`, or `devDependencies`, and every import in the provider
|
|
94
|
-
must be relative.
|
|
95
|
-
|
|
96
|
-
## API credentials
|
|
97
|
-
|
|
98
|
-
The default credential is a scoped Cloudflare API token, sent as
|
|
99
|
-
`Authorization: Bearer <token>`. Create it under My Profile → API Tokens →
|
|
100
|
-
Create Token.
|
|
101
|
-
|
|
102
|
-
Set `authentication: "globalApiKey"` to use the legacy user-scoped scheme. The
|
|
103
|
-
credential form stores two encrypted fields and sends them as `X-Auth-Email`
|
|
104
|
-
and `X-Auth-Key`. The Global API Key has the same access as its Cloudflare user
|
|
105
|
-
across every account and zone that user can reach. It cannot be scoped, only
|
|
106
|
-
one can exist per user, and it has no expiry or IP limits. Cloudflare recommends
|
|
107
|
-
API tokens for new use, but existing Global API Keys remain supported.
|
|
108
|
-
|
|
109
|
-
Grant only what the deployment needs:
|
|
110
|
-
|
|
111
|
-
| Tools | Token permission | Scope |
|
|
112
|
-
| --- | --- | --- |
|
|
113
|
-
| Zone discovery and settings reads | Zone Read | Zone |
|
|
114
|
-
| `update_zone_setting` | Zone Settings Write | Zone |
|
|
115
|
-
| Zone ruleset reads | Relevant Rules product Read permission, such as Transform Rules Read or Firewall Services Read | Zone |
|
|
116
|
-
| `list_dns_records`, `get_dns_record` | DNS Read | Zone |
|
|
117
|
-
| `create_dns_record`, `update_dns_record`, `delete_dns_record` | DNS Write | Zone |
|
|
118
|
-
| `purge_cache` | Cache Purge | Zone |
|
|
119
|
-
| Worker script/deployment reads | Workers Scripts Read | Account |
|
|
120
|
-
| Worker writes through named or raw tools | Workers Scripts Write | Account |
|
|
121
|
-
| KV reads | Workers KV Storage Read | Account |
|
|
122
|
-
| KV creates, renames, writes, and deletes | Workers KV Storage Write | Account |
|
|
123
|
-
| R2 reads | Workers R2 Storage Read | Account |
|
|
124
|
-
| R2 creates, changes, uploads, deletes, and configuration writes | Workers R2 Storage Write | Account |
|
|
125
|
-
| Pages reads | Cloudflare Pages Read | Account |
|
|
126
|
-
| Pages retries, rollbacks, domains, purges, uploads, and deletes | Cloudflare Pages Write | Account |
|
|
127
|
-
| Images, Stream, Email Routing, D1, Queues, and other raw calls | Matching product Read or Write permission | Account or Zone |
|
|
128
|
-
|
|
129
|
-
Most names above appear directly in the token editor. Ruleset access is split
|
|
130
|
-
by product and phase, so grant the narrow Rules permission for the phases the
|
|
131
|
-
agent must inspect rather than looking for one generic "Zone Rulesets Read"
|
|
132
|
-
scope. "Cache Purge" is a single permission with no Read/Write split, and
|
|
133
|
-
Cloudflare's own reference renders a few labels differently between its
|
|
134
|
-
Dashboard and API tabs.
|
|
135
|
-
|
|
136
|
-
`verify_api_token` needs no permission beyond the token existing. In legacy
|
|
137
|
-
mode, `verify_global_api_key` reads `/user` to confirm the email and key pair.
|
|
138
|
-
The connection UI Test action at `/` runs the matching check before storage.
|
|
139
|
-
|
|
140
|
-
Cloudflare rate-limits *authentication failures* aggressively and separately
|
|
141
|
-
from the global limit: a few requests with a bad token return HTTP 429 with
|
|
142
|
-
code `10502`, "Too many authentication failures". That surfaces here as
|
|
143
|
-
`rate_limited`, not `auth_required`, which is correct — the token may well be
|
|
144
|
-
fine by the time the window clears — but it means a broken token should be
|
|
145
|
-
diagnosed once with `verify_api_token` rather than by retrying real calls.
|
|
146
|
-
|
|
147
|
-
## Scoping
|
|
148
|
-
|
|
149
|
-
`zoneId` and `accountId` are deployment defaults, not restrictions. When a
|
|
150
|
-
default is set, the corresponding argument drops out of the tool's `required`
|
|
151
|
-
list and calls that omit it use the default; a call may still pass a different
|
|
152
|
-
id. When no default is set, the argument is required and the schema's own
|
|
153
|
-
description names the discovery tool that produces it — `list_zones` for a
|
|
154
|
-
zone, `list_accounts` for an account.
|
|
155
|
-
|
|
156
|
-
That is the discovery flow worth knowing: Cloudflare addresses almost
|
|
157
|
-
everything by an opaque 32-character id, and an agent that only knows a domain
|
|
158
|
-
name must call `list_zones` with `name: "example.com"` first. Configuring
|
|
159
|
-
`zoneId` removes that hop entirely for a single-zone deployment.
|
|
160
|
-
|
|
161
|
-
`list_zones` is the one tool a configured `accountId` deliberately does *not*
|
|
162
|
-
reach. It is the discovery step, and a default that quietly filtered it would
|
|
163
|
-
be a restriction in all but name — one with no argument that escapes it, since
|
|
164
|
-
an empty `accountId` would fall back to the default again. A deployment that
|
|
165
|
-
wants zones from one account passes `accountId` explicitly, and the property
|
|
166
|
-
says so.
|
|
167
|
-
|
|
168
|
-
## API tools
|
|
169
|
-
|
|
170
|
-
The named surface covers workflows that benefit most from concise schemas and
|
|
171
|
-
projections:
|
|
172
|
-
|
|
173
|
-
| Area | Reads | Writes |
|
|
174
|
-
| --- | --- | --- |
|
|
175
|
-
| Zones | discovery, details, one setting at a time, rulesets | update a setting |
|
|
176
|
-
| DNS/cache | list and get records | create, update, delete, targeted/full purge |
|
|
177
|
-
| Workers | scripts, settings, deployments | delete a script |
|
|
178
|
-
| KV | namespaces, keys, bulk values | create/rename/delete namespace, bulk write/delete |
|
|
179
|
-
| R2 | buckets, object metadata, CORS | create/update/delete bucket, delete object |
|
|
180
|
-
| Pages | projects, deployments, domains | retry/rollback/delete deployments, add/delete domains, purge build cache, delete project |
|
|
181
|
-
|
|
182
|
-
Every named tool carries a complete hand-written input schema: closed
|
|
183
|
-
(`additionalProperties: false`), with an accurate `required` list, an `enum` on
|
|
184
|
-
every constrained field, endpoint-specific pagination bounds, and a description
|
|
185
|
-
on every property. `test/cloudflare-provider.test.ts` walks the surface and
|
|
186
|
-
asserts those properties rather than leaving them as a claim.
|
|
187
|
-
|
|
188
|
-
The per-setting zone tools accept Cloudflare's current non-empty setting ids,
|
|
189
|
-
including `webmcp_enabled` and `webmcp_packs`. Cloudflare's
|
|
190
|
-
[rendered setting reference](https://developers.cloudflare.com/api/resources/zones/subresources/settings/)
|
|
191
|
-
and current OpenAPI now agree on both beta ids. Results preserve the value and
|
|
192
|
-
the actual `editable` flag Cloudflare returned; Connecta does not apply the
|
|
193
|
-
document's defaults locally.
|
|
194
|
-
|
|
195
|
-
KV namespace jurisdiction is creation-only. `create_kv_namespace` accepts
|
|
196
|
-
`eu`, `fedramp`, or `us`, and namespace reads preserve the returned value.
|
|
197
|
-
R2 uses a different enum: `default`, `eu`, `us`, or `fedramp`, sent through
|
|
198
|
-
`cf-r2-jurisdiction` by all eight named R2 operations.
|
|
199
|
-
|
|
200
|
-
### What the named surface deliberately leaves out
|
|
201
|
-
|
|
202
|
-
A named tool is a permanent line item in every deployment's catalog, so the
|
|
203
|
-
surface was measured against the escape hatches rather than assumed to beat
|
|
204
|
-
them ([#350](https://github.com/zackbart/connecta/issues/350), evidence in
|
|
205
|
-
[`eval/current-version/results/issue-350-evidence.md`](https://github.com/zackbart/connecta/blob/main/eval/current-version/results/issue-350-evidence.md)).
|
|
206
|
-
Two named tools lost that comparison, a third followed one of them off the
|
|
207
|
-
surface to keep a policy pair on one route, and a fourth left because
|
|
208
|
-
Cloudflare deprecated the only endpoint it could call:
|
|
209
|
-
|
|
210
|
-
- **R2 CORS writes.** `set_r2_cors` declared its rule list as free-form objects
|
|
211
|
-
— the untyped body this connection refuses everywhere else — so its schema
|
|
212
|
-
validated the bucket name and waved through the part of the call that
|
|
213
|
-
actually fails. It also returned Cloudflare's response unprojected and was
|
|
214
|
-
destructive either way, which left nothing for it to beat
|
|
215
|
-
`cloudflare_api_mutate` on. `delete_r2_cors` did not lose that comparison:
|
|
216
|
-
it returned a fixed `{deleted: true}` behind a closed output schema and
|
|
217
|
-
refused every malformed argument locally. It went anyway, because naming only
|
|
218
|
-
the delete would mean one CORS policy is set through the raw route and
|
|
219
|
-
cleared through a named tool. Read a policy with `get_r2_cors`; change one
|
|
220
|
-
with `cloudflare_api_mutate` at
|
|
221
|
-
`PUT`/`DELETE /accounts/{accountId}/r2/buckets/{bucketName}/cors`. This is the
|
|
222
|
-
same split the [DNS record types](#dns-record-types) already use: structured
|
|
223
|
-
bodies stay readable through named tools and writable through the
|
|
224
|
-
approval-gated raw route.
|
|
225
|
-
- **R2 account metrics.** `get_r2_metrics` took one account id, put it in a
|
|
226
|
-
path, and returned the response untouched — `cloudflare_api_get` at
|
|
227
|
-
`/accounts/{accountId}/r2/metrics` does the same thing without a permanent
|
|
228
|
-
catalog line.
|
|
229
|
-
- **The bulk zone-settings read.** `list_zone_settings` called
|
|
230
|
-
`GET /zones/{zoneId}/settings`, which Cloudflare's published document now
|
|
231
|
-
marks `deprecated: true` along with its `PATCH` sibling
|
|
232
|
-
([#361](https://github.com/zackbart/connecta/issues/361)). There is no bulk
|
|
233
|
-
replacement to repoint it at: the supported operations are the per-setting
|
|
234
|
-
`GET` and `PATCH /zones/{zoneId}/settings/{settingId}` that `get_zone_setting`
|
|
235
|
-
and `update_zone_setting` already call. Keeping the tool would have meant
|
|
236
|
-
maintaining a name over a contract Cloudflare has announced it intends to
|
|
237
|
-
stop honouring — and #350 had already measured this one as the sharpest
|
|
238
|
-
passthrough on the surface: it took a zone id, projected nothing, and *grew*
|
|
239
|
-
the payload 22.7% by wrapping Cloudflare's settings array in a page object an
|
|
240
|
-
unpaginated endpoint never filled. Read one setting with `get_zone_setting`.
|
|
241
|
-
Where the whole set is genuinely wanted — an audit, a config diff — an
|
|
242
|
-
operator can still ask for it explicitly through `cloudflare_api_get` at
|
|
243
|
-
`/zones/{zoneId}/settings`, which is the honest place for a call whose
|
|
244
|
-
endpoint is on its way out: named by the caller, at the caller's risk, rather
|
|
245
|
-
than promised by connecta's catalog.
|
|
246
|
-
|
|
247
|
-
Because `scripts/drift/cloudflare-endpoints.json` records the endpoints this
|
|
248
|
-
connection *calls*, dropping the tool drops the row — so
|
|
249
|
-
`npm run drift:check -- --specs` is quiet about zone settings by construction
|
|
250
|
-
rather than by a recorded exception. A path reached only through a hatch is
|
|
251
|
-
named by the caller, so it was never a touched endpoint.
|
|
252
|
-
|
|
253
|
-
The surviving 48 named tools all refuse malformed arguments locally, which is
|
|
254
|
-
the one thing no escape hatch can do: a hatch's path is an opaque string, so it
|
|
255
|
-
can only check that a path is a path.
|
|
256
|
-
|
|
257
|
-
### The whole-v4 escape hatch
|
|
258
|
-
|
|
259
|
-
Cloudflare adds products and endpoints faster than a curated connector should
|
|
260
|
-
grow tool names. Three provider-relative tools cover the rest without turning
|
|
261
|
-
method classification into user input:
|
|
262
|
-
|
|
263
|
-
- `cloudflare_api_get` accepts only GET and is explicitly read-only. JSON is the
|
|
264
|
-
default; `responseType: "text" | "base64"` retrieves scripts, logs, R2
|
|
265
|
-
objects, and media bodies without pretending they have a JSON envelope.
|
|
266
|
-
- `cloudflare_api_mutate` accepts JSON POST, PUT, PATCH, and DELETE. It is always
|
|
267
|
-
destructive, even when a particular POST is merely additive. It preserves
|
|
268
|
-
ordinary non-envelope JSON from endpoints such as `/graphql`.
|
|
269
|
-
- `cloudflare_api_upload` accepts POST or PUT plus exactly one of raw text,
|
|
270
|
-
base64 bytes, or multipart fields/files. It is always destructive and reads
|
|
271
|
-
no local files.
|
|
272
|
-
|
|
273
|
-
All three accept explicit endpoint-specific headers, which supports R2
|
|
274
|
-
jurisdictions, conditional requests, encryption controls, and object metadata.
|
|
275
|
-
`Authorization`, `Cookie`, `Host`, `Content-Length`, `Content-Type`, and
|
|
276
|
-
`Transfer-Encoding` remain connector-owned and are refused: authentication,
|
|
277
|
-
host selection, content type, and request framing are not the caller's to set.
|
|
278
|
-
That refused list lives in the usage guide rather than in each header property
|
|
279
|
-
description, because the compact renderer inlines a property description once
|
|
280
|
-
per tool that shares it, and repeating it three times pushed
|
|
281
|
-
`cloudflare_api_upload`'s compact input shape past the 1,024-byte discovery
|
|
282
|
-
budget.
|
|
283
|
-
|
|
284
|
-
Paths are relative to `/client/v4`. Absolute URLs, protocol-relative paths,
|
|
285
|
-
`..` traversal, fragments, and embedded query strings are refused locally;
|
|
286
|
-
query parameters are explicit name/value pairs. These tools reuse the same
|
|
287
|
-
credential, admission budget, abort signal, envelope parsing, and typed failure
|
|
288
|
-
mapping as named tools. They do not widen the configured credential's access.
|
|
289
|
-
|
|
290
|
-
This is intentionally not OpenAPI ingestion: it creates three stable tools,
|
|
291
|
-
not one tool per Cloudflare operation. For example, an agent can list Images at
|
|
292
|
-
`/accounts/{accountId}/images/v1`, manage Stream at
|
|
293
|
-
`/accounts/{accountId}/stream`, manage Email Routing at
|
|
294
|
-
`/zones/{zoneId}/email/routing/rules`, reach D1 at
|
|
295
|
-
`/accounts/{accountId}/d1/database`, and reach Queues at
|
|
296
|
-
`/accounts/{accountId}/queues`. The endpoint-specific query and body shape still
|
|
297
|
-
comes from Cloudflare's API reference.
|
|
298
|
-
|
|
299
|
-
### Where the `perPage` bounds come from
|
|
300
|
-
|
|
301
|
-
`api()` enforces every hand-written schema, so an out-of-range `perPage` is
|
|
302
|
-
refused locally before it reaches Cloudflare. That is only a favor when the bound is really
|
|
303
|
-
Cloudflare's, so the schemas record which ones are and the descriptions say so
|
|
304
|
-
out loud:
|
|
305
|
-
|
|
306
|
-
| Tool | `perPage` | Default | Whose bound |
|
|
307
|
-
| --- | --- | --- | --- |
|
|
308
|
-
| `list_accounts`, `list_zones` | 5–50 | 20 | Cloudflare's, as documented |
|
|
309
|
-
| `list_kv_namespaces` | 1–1000 | 20 | Cloudflare's, as documented |
|
|
310
|
-
| `list_dns_records` | 1–1000 | 100 | Cloudflare's minimum; the ceiling is ours |
|
|
311
|
-
| `list_pages_projects` | 1–100 | — | Ours entirely |
|
|
312
|
-
|
|
313
|
-
Two need the note. Cloudflare's schema documents `per_page` on
|
|
314
|
-
`/zones/{id}/dns_records` as 1 to **5,000,000** — a nominal ceiling no listing
|
|
315
|
-
will honor — so this connection caps it at 1,000, the same conservative-reading
|
|
316
|
-
move as the [one-variant purge rule](#cache-purging): a local cap an agent is
|
|
317
|
-
told about beats a page size that fails somewhere inside Cloudflare. And
|
|
318
|
-
`/accounts/{id}/pages/projects` documents no bounds and no default at all, so
|
|
319
|
-
1 to 100 is a choice made here and labeled as one.
|
|
320
|
-
|
|
321
|
-
### DNS record types
|
|
322
|
-
|
|
323
|
-
Cloudflare accepts 21 record types, exported as `CLOUDFLARE_DNS_RECORD_TYPES`.
|
|
324
|
-
Eight of them take a single `content` string; the other thirteen (CAA, CERT,
|
|
325
|
-
DNSKEY, DS, HTTPS, LOC, NAPTR, SMIMEA, SRV, SSHFP, SVCB, TLSA, URI) take a
|
|
326
|
-
per-type structured `data` object with its own field set.
|
|
327
|
-
|
|
328
|
-
`list_dns_records` filters on all 21. `create_dns_record` and
|
|
329
|
-
`update_dns_record` accept only the eight content-based types, exported as
|
|
330
|
-
`CLOUDFLARE_CONTENT_DNS_RECORD_TYPES`. Supporting the rest would mean either a
|
|
331
|
-
free-form `data` passthrough — the untyped `{}` this connection exists to
|
|
332
|
-
avoid — or thirteen more hand-written schemas for record types that are rare in
|
|
333
|
-
day-to-day zone administration. Structured-data records stay fully readable.
|
|
334
|
-
The named create/update tools omit them and the enum says so rather than letting
|
|
335
|
-
the call reach Cloudflare and 400; an operator who needs one can use the
|
|
336
|
-
approval-gated raw mutation tool with Cloudflare's documented per-type `data`
|
|
337
|
-
body.
|
|
338
|
-
|
|
339
|
-
### Cache purging
|
|
340
|
-
|
|
341
|
-
`purge_cache` takes exactly one variant per call: `everything: true`, or one of
|
|
342
|
-
`files`, `tags`, `hosts`, or `prefixes`. Cloudflare caps a purge at 100
|
|
343
|
-
operations per request (500 files on Enterprise), and all four targeted methods
|
|
344
|
-
are available on every plan — tag, host, and prefix purging is no longer
|
|
345
|
-
Enterprise-only.
|
|
346
|
-
|
|
347
|
-
The one-variant rule is this connection's contract, not a documented API
|
|
348
|
-
restriction. Cloudflare's schema models the body as `anyOf`, which does not
|
|
349
|
-
forbid combining, and the only explicit exclusivity statement in its
|
|
350
|
-
documentation is about the Workers cache binding rather than the REST endpoint.
|
|
351
|
-
Refusing a combined call locally is the conservative reading: an agent gets a
|
|
352
|
-
clear `invalid_args` naming the conflict instead of a purge whose actual scope
|
|
353
|
-
is ambiguous. If a future deployment needs combined tag-and-prefix purging,
|
|
354
|
-
that is a deliberate change to make here, not something to discover in
|
|
355
|
-
production.
|
|
356
|
-
|
|
357
|
-
## Results
|
|
358
|
-
|
|
359
|
-
Reads return Cloudflare's `result` unwrapped and projected: identity and
|
|
360
|
-
description fields kept, plan/permission/meta noise dropped, `snake_case`
|
|
361
|
-
renamed to `camelCase`. A zone comes back as `id`, `name`, `status`, `paused`,
|
|
362
|
-
`type`, `accountId`, `accountName`, `plan`, `nameServers`, and timestamps —
|
|
363
|
-
not the forty-field object Cloudflare sends.
|
|
364
|
-
|
|
365
|
-
Every named tool declares useful top-level output keys. Zone settings, Worker
|
|
366
|
-
settings and deployments, KV namespaces and bulk operations, rulesets, R2 CORS,
|
|
367
|
-
and Pages resources use maintained result schemas instead of an open
|
|
368
|
-
"Cloudflare object" declaration. The three raw escape hatches remain open by
|
|
369
|
-
design because their result fields depend on the caller-supplied endpoint.
|
|
370
|
-
|
|
371
|
-
Paginated lists add a `page` object derived from `result_info`:
|
|
372
|
-
`{ page, perPage, count, totalCount, totalPages, hasMore }`. `hasMore` is the
|
|
373
|
-
field to branch on.
|
|
374
|
-
|
|
375
|
-
Some endpoints do not work that way, and the schemas say so rather than leaving
|
|
376
|
-
an agent to discover it. `list_zone_rulesets`, `list_r2_buckets`,
|
|
377
|
-
`list_r2_objects`, and `list_kv_keys` paginate by cursor and return
|
|
378
|
-
`nextCursor` instead of `page`. On those four the `cursor` argument says the
|
|
379
|
-
endpoint pages by cursor rather than page number, and the `nextCursor` result
|
|
380
|
-
says it is the only signal and that no `page` object is coming — so the loop
|
|
381
|
-
condition is legible from either end of one tool, without fetching the guide.
|
|
382
|
-
`list_worker_scripts` reports no counters at all and omits `page` entirely.
|
|
383
|
-
|
|
384
|
-
Projected resource reads expose `raw: true` where the provider's larger object
|
|
385
|
-
is commonly useful. `cloudflare_api_get` is the universal unprojected escape
|
|
386
|
-
hatch. Raw shapes can hit a deployment's result cap, so programs should still
|
|
387
|
-
filter and project before returning them.
|
|
388
|
-
|
|
389
|
-
## Typed failures
|
|
390
|
-
|
|
391
|
-
Cloudflare's error envelope carries an array of `{ code, message }` entries and
|
|
392
|
-
sometimes a nested `error_chain`; the connection flattens the whole chain into
|
|
393
|
-
the failure message so the provider's own code number survives to the agent.
|
|
394
|
-
|
|
395
|
-
| Cloudflare | Connecta failure | Agent behavior |
|
|
396
|
-
| --- | --- | --- |
|
|
397
|
-
| 429 | `rate_limited`, retryable | Waits `retryAfterMs` — the `retry-after` header when present, otherwise the full five-minute window |
|
|
398
|
-
| 401 or 403 | `auth_required`, not retryable | Stops and reports which permission is missing |
|
|
399
|
-
| 400 with a credential-shaped code (1001, 6003, 6111, 9103, 9106, 9107) | `auth_required`, not retryable | Stops; the header or key is malformed, not the arguments |
|
|
400
|
-
| 400, 409, 422 | `invalid_args`, not retryable | Repairs the arguments |
|
|
401
|
-
| 404 | `not_found`, not retryable | Re-runs discovery for the id |
|
|
402
|
-
| 5xx or a transport error | `unavailable`, retryable | Retries |
|
|
403
|
-
|
|
404
|
-
The 404 row is the unambiguous half of the `not_found` rule
|
|
405
|
-
([H11](./provider-conventions.md#h11--errors-are-mapped-to-what-the-caller-does-next)).
|
|
406
|
-
Cloudflare refuses a token that may not touch a resource with 401 or 403, so a
|
|
407
|
-
404 is an absence rather than a permission gap wearing a miss, and an agent can
|
|
408
|
-
act on it: confirm the zone or account id with `list_zones` or `list_accounts`
|
|
409
|
-
and address the right one. Notion's 404, which proves neither, stays
|
|
410
|
-
`connector_call_failed`.
|
|
411
|
-
|
|
412
|
-
The six credential-shaped codes deserve a caveat: Cloudflare publishes no
|
|
413
|
-
official table mapping error codes to causes, so that set is assembled from
|
|
414
|
-
community reports and probing, not from documentation. The same goes for the
|
|
415
|
-
claim below that `10000` is overloaded — that is an observation about responses
|
|
416
|
-
seen in practice. Treat both as well-supported readings that Cloudflare could
|
|
417
|
-
invalidate without notice. Prefer `verify_api_token` or
|
|
418
|
-
`verify_global_api_key`, as configured, when a diagnosis matters.
|
|
419
|
-
|
|
420
|
-
Two ordering decisions are deliberate. The 429 branch is checked before the
|
|
421
|
-
authentication codes, because Cloudflare reuses the generic `10000` code on
|
|
422
|
-
throttled responses and reading a rate limit as an auth failure would tell an
|
|
423
|
-
agent to stop when it should wait. And `10000` is *not* itself treated as an
|
|
424
|
-
auth code: Cloudflare returns it for "Authentication error" but also for
|
|
425
|
-
ordinary validation failures like "Invalid pagination cursor" and
|
|
426
|
-
"domain_name is required", so routing on it would tell an agent its credential was
|
|
427
|
-
broken when its arguments were. Genuine `10000` auth failures arrive with 401
|
|
428
|
-
or 403 and are caught by status.
|
|
429
|
-
|
|
430
|
-
Because the connection declares an operator-managed credential rather than an
|
|
431
|
-
OAuth flow, an `auth_required` failure resolves to the `operator_config`
|
|
432
|
-
recovery mode — the fix is a human updating the credential, not an authorization
|
|
433
|
-
URL the agent can open. A missing credential fails that way before any request
|
|
434
|
-
is made.
|
|
435
|
-
|
|
436
|
-
Some failures never reach Cloudflare at all. A blank scope id, a `purge_cache`
|
|
437
|
-
call with no variant or two, and an `update_dns_record` with nothing to change
|
|
438
|
-
are all refused locally as `invalid_args` with a validation issue attached,
|
|
439
|
-
because a round trip that can only 400 is a wasted call and a worse
|
|
440
|
-
explanation.
|
|
441
|
-
|
|
442
|
-
## Rate limits
|
|
443
|
-
|
|
444
|
-
Cloudflare documents a global limit of
|
|
445
|
-
[1,200 requests per five minutes per user](https://developers.cloudflare.com/fundamentals/api/reference/limits/),
|
|
446
|
-
counted cumulatively across the dashboard, API keys, and API tokens. The
|
|
447
|
-
connection declares a matching rolling-window admission budget plus a
|
|
448
|
-
`maxConcurrency` of 6, overridable with the `maxConcurrency` option.
|
|
449
|
-
|
|
450
|
-
The budget is a best-effort approximation of the per-user limit, not an
|
|
451
|
-
enforcement of it. Each runtime keeps its own counter, so N Worker isolates or
|
|
452
|
-
Node processes serving one deployment can each admit up to 1,200 — and the
|
|
453
|
-
dashboard traffic of a human sharing the account is counted by Cloudflare but
|
|
454
|
-
not by Connecta. `maxConcurrency` is the bound that actually protects a shared
|
|
455
|
-
credential, because a single `execute_code` program can fan out far faster than
|
|
456
|
-
the window notices.
|
|
457
|
-
|
|
458
|
-
## Contract checks
|
|
459
|
-
|
|
460
|
-
`npm run providers:check` compares the 49 fixed REST endpoints with
|
|
461
|
-
Cloudflare's published OpenAPI document and the two MCP names, endpoint, and
|
|
462
|
-
OAuth support with Cloudflare's official MCP page. It needs no Cloudflare
|
|
463
|
-
credential. The MCP schemas are not vendored or reconstructed: the live
|
|
464
|
-
`tools/list` response remains the contract agents receive.
|
|
465
|
-
|
|
466
|
-
## Conventions
|
|
467
|
-
|
|
468
|
-
This connection is audited against
|
|
469
|
-
[the provider conventions](./provider-conventions.md). Its verdict per
|
|
470
|
-
convention, including every recorded exception, is the Cloudflare section of
|
|
471
|
-
[the provider audit](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md).
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
# Connector guides
|
|
2
|
-
|
|
3
|
-
Descriptions and schemas say *what* a connector's tools are. They do not say
|
|
4
|
-
which tool to prefer, which id format an address quirk expects, how the service
|
|
5
|
-
paginates, or how hard you may hammer it. A connector's optional `usageGuide`
|
|
6
|
-
is where that goes — deployment-owned configuration, served by the `skills`
|
|
7
|
-
meta-tool as `connector:<id>` and returned verbatim, with a bounded `summary`
|
|
8
|
-
surfaced in discovery so an agent learns the guide exists at the moment it
|
|
9
|
-
matters. See [meta-tools](./meta-tools.md#connector-guide-selection) for the
|
|
10
|
-
discovery contract and [connectors](./connectors.md#catalog-contract) for how a
|
|
11
|
-
guide is configured.
|
|
12
|
-
|
|
13
|
-
## The shape of a guide
|
|
14
|
-
|
|
15
|
-
`usageGuide` accepts a bare markdown string, which is still the shortest
|
|
16
|
-
correct configuration, or the structured form:
|
|
17
|
-
|
|
18
|
-
```ts
|
|
19
|
-
const notion = remoteMcp("notion", {
|
|
20
|
-
url: "https://mcp.notion.com/mcp",
|
|
21
|
-
description: "Notion — pages, databases, comments",
|
|
22
|
-
auth: { type: "oauth" },
|
|
23
|
-
usageGuide: {
|
|
24
|
-
content: `# Notion usage
|
|
25
|
-
|
|
26
|
-
Search before listing: \`notion.search\` covers pages and databases in one call.
|
|
27
|
-
|
|
28
|
-
- Page ids are dashed UUIDs. Strip the trailing slug from a pasted URL first.
|
|
29
|
-
- Paginate with \`start_cursor\`; \`page_size\` is capped at 100.
|
|
30
|
-
- Writes replace blocks wholesale — read the block, merge, then write.
|
|
31
|
-
`,
|
|
32
|
-
summary: "Search before listing; dashed-UUID page ids; cursor pagination",
|
|
33
|
-
},
|
|
34
|
-
});
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
The field is on the `Connector` interface, not on a factory, so it works
|
|
38
|
-
identically for `api()`, `remoteMcp()`, a prebuilt connection, and a
|
|
39
|
-
hand-written connector. It is deployment-owned configuration like everything
|
|
40
|
-
else here: an edit and a redeploy, never a runtime registration.
|
|
41
|
-
|
|
42
|
-
`content` is returned byte for byte by `skills({ name: "connector:<id>" })`.
|
|
43
|
-
`summary` is normalized and must fit 120 characters. A longer configured value
|
|
44
|
-
refuses construction instead of silently changing the operator's words. Omit
|
|
45
|
-
it and connecta derives the same bounded summary the skills listing uses: the
|
|
46
|
-
first meaningful body paragraph, joined across Markdown's physical line wraps,
|
|
47
|
-
with frontmatter, fences, rules, comments, and tables skipped. When the
|
|
48
|
-
paragraph does not fit, connecta keeps a useful complete sentence when one
|
|
49
|
-
fits, then prefers a clause or word boundary before adding an ellipsis. A heading is used
|
|
50
|
-
only when the guide has no body, and the connector's description is the last
|
|
51
|
-
resort. A derived summary is usually worse than a written one — it was written
|
|
52
|
-
to open a document, not to answer "is this guide relevant to what I am about
|
|
53
|
-
to do".
|
|
54
|
-
|
|
55
|
-
`connector:<id>` is the only address for a guide, and built-in skill names are
|
|
56
|
-
bare identifiers, so a guide can never shadow or be shadowed by `usage`: a
|
|
57
|
-
connector whose id is literally `usage` is listed as `connector:usage`, and
|
|
58
|
-
`skills({ name: "usage" })` still returns the built-in guide. Every miss —
|
|
59
|
-
unknown name, unknown connector, connector with no guide — is an explicit
|
|
60
|
-
error. Nothing silently falls back to the generic guide, because a generic
|
|
61
|
-
answer to a specific question is worse than no answer.
|
|
62
|
-
|
|
63
|
-
Discovery text is conditional on the deployment actually having a guide. Short
|
|
64
|
-
pointers in the `skills`, `search_tools`, `call_destructive_tool`, and
|
|
65
|
-
`execute_code` descriptions appear only when at least one visible connector
|
|
66
|
-
declares one. The detailed selection rules live only in the built-in `usage`
|
|
67
|
-
skill. That skill stays byte-identical across deployments, including its
|
|
68
|
-
per-connector-guides section, so an agent reads it at most once per task.
|
|
69
|
-
|
|
70
|
-
## What belongs in a guide
|
|
71
|
-
|
|
72
|
-
The test is not "is this true" but "can a schema carry it". If a schema can,
|
|
73
|
-
fix the schema instead — a constraint in the schema is enforced, is visible at
|
|
74
|
-
the moment of the call, and costs nothing to a caller who never fetches the
|
|
75
|
-
guide.
|
|
76
|
-
|
|
77
|
-
So a guide carries:
|
|
78
|
-
|
|
79
|
-
- **Tool preference.** Which of two plausible tools is the right one, and the
|
|
80
|
-
fact that decides it.
|
|
81
|
-
- **Sequence.** What must happen first — resolving a name to an id, listing a
|
|
82
|
-
parent before a child.
|
|
83
|
-
- **Identity and address quirks.** The id format an address expects, the
|
|
84
|
-
difference between the id in the URL and the id the API wants.
|
|
85
|
-
- **Pagination conventions.** The cursor field, the page cap, whether the
|
|
86
|
-
connector has more than one convention.
|
|
87
|
-
- **Units and aliases.** What a bare number means; what the service calls the
|
|
88
|
-
thing the caller calls something else.
|
|
89
|
-
- **Reduction advice.** Which fields matter, for a downstream whose results are
|
|
90
|
-
large and whose schemas you do not control.
|
|
91
|
-
- **Rate-limit etiquette**, with the number.
|
|
92
|
-
|
|
93
|
-
And a guide does not carry: anything readable off the schema, background on
|
|
94
|
-
what the service is, marketing, a tool list, or a second copy of the tool
|
|
95
|
-
descriptions. It is fetched into a live context window — every line that
|
|
96
|
-
repeats the schema is a line that displaced one that did not.
|
|
97
|
-
|
|
98
|
-
**Style.** Write for the agent, not the operator; the built-in `usage` skill
|
|
99
|
-
(`src/skills.ts`) is the model. Concise and imperative, leading with the
|
|
100
|
-
decision rather than the background ("Search before listing", not "Notion has a
|
|
101
|
-
search API"). Prefer short bullets to prose. Name exact tool addresses and
|
|
102
|
-
argument names. State a constraint with its number.
|
|
103
|
-
|
|
104
|
-
## The `required` rule
|
|
105
|
-
|
|
106
|
-
`required: true` on the structured form means: fetch this guide before every
|
|
107
|
-
operation on this connector. It surfaces as `guideRequired: true` with
|
|
108
|
-
`guideRequiredReasons: ["connector_required"]` on discovery results.
|
|
109
|
-
|
|
110
|
-
It is an instruction, not a gate. Nothing refuses the call — connecta tells the
|
|
111
|
-
agent to read the guide first and then believes it, because a server-side
|
|
112
|
-
refusal here would be a policy engine, which
|
|
113
|
-
[`ethos.md`](../ethos.md) refuses.
|
|
114
|
-
|
|
115
|
-
Reserve it for connectors whose correct arguments or sequence *cannot* be
|
|
116
|
-
expressed by the downstream tool schema at all: generic API wrappers whose one
|
|
117
|
-
broad tool name carries no endpoint vocabulary, and cross-operation conventions
|
|
118
|
-
no single schema can state. Two categories already produce the flag on their
|
|
119
|
-
own and must not be hand-declared for it — an unannotated or write-capable tool
|
|
120
|
-
(`approval_required`) and a compact schema that was capped
|
|
121
|
-
(`schema_truncated`). `connector_required` and `approval_required` survive
|
|
122
|
-
exact schema expansion; `schema_truncated` clears once describe returns the
|
|
123
|
-
exact shape.
|
|
124
|
-
|
|
125
|
-
The failure mode of over-declaring is quiet and expensive: an agent that must
|
|
126
|
-
fetch a guide before every call pays that fetch on the calls where the schema
|
|
127
|
-
was already complete and unambiguous. `required` earns its cost on connectors
|
|
128
|
-
where the alternative is a wrong call, not on connectors where it is a slightly
|
|
129
|
-
slower right one.
|
|
130
|
-
|
|
131
|
-
## Provider conventions
|
|
132
|
-
|
|
133
|
-
The maintained prebuilt connections come in two shapes, and each has its own
|
|
134
|
-
convention set — including the shape of its usage guide, which is one of the
|
|
135
|
-
few things both shapes fully own:
|
|
136
|
-
|
|
137
|
-
- [Hand-written HTTP providers](./provider-conventions.md#hand-written-http-providers)
|
|
138
|
-
(H1–H14) — `api()` surfaces where Connecta owns every name, schema,
|
|
139
|
-
projection, and error. Cloudflare, Notion, and Vercel.
|
|
140
|
-
- [Hosted-MCP proxies](./provider-conventions.md#hosted-mcp-proxies) (P1–P13) —
|
|
141
|
-
`remoteMcp()` wrappers where the downstream owns the catalog and Connecta
|
|
142
|
-
owns the endpoint, credential, classification, guide, and budget. Linear,
|
|
143
|
-
Stripe, Mixpanel, and RevenueCat.
|
|
144
|
-
|
|
145
|
-
Both sets are judged by one measure: what the convention saves the model that
|
|
146
|
-
interacts with connecta, priced in discovery tokens, wrong-tool selection,
|
|
147
|
-
argument retries, or result size. The same document defines
|
|
148
|
-
[what a provider audit checks](./provider-conventions.md#what-the-audit-checks),
|
|
149
|
-
so [#342](https://github.com/zackbart/connecta/issues/342) can run against it
|
|
150
|
-
convention by convention rather than by taste.
|
|
151
|
-
|
|
152
|
-
Two of those conventions decide how a guide is written, and they differ by
|
|
153
|
-
shape. A hand-written provider's guide carries only what a schema cannot,
|
|
154
|
-
because it owns the schemas and should fix them instead
|
|
155
|
-
([H13](./provider-conventions.md#h13--the-guide-carries-only-what-a-schema-cannot)).
|
|
156
|
-
A proxy's guide carries the reduction and identity-resolution advice its
|
|
157
|
-
schemas will never carry, because it cannot change them
|
|
158
|
-
([P7](./provider-conventions.md#p7--the-guide-carries-the-reduction-advice-the-schemas-cannot),
|
|
159
|
-
[P8](./provider-conventions.md#p8--identity-resolution-comes-before-action)).
|
|
160
|
-
|
|
161
|
-
## Tests that enforce this
|
|
162
|
-
|
|
163
|
-
`test/meta-tools.test.ts` owns the guide behavior end to end: the skills
|
|
164
|
-
listing carrying one entry per guided connector, summaries joining a
|
|
165
|
-
hard-wrapped opening paragraph and shortening at readable boundaries,
|
|
166
|
-
configured summaries refusing construction past the bound, heading and
|
|
167
|
-
description fallbacks, markup skipping, whitespace-only guides treated as no
|
|
168
|
-
guide, content returned verbatim including surrounding padding, identical
|
|
169
|
-
content in two deployments staying isolated, every miss erroring rather than
|
|
170
|
-
falling back to the generic guide with an identically labelled skills list on
|
|
171
|
-
each branch, the `guide` pointer in search output, and `guideRequired`
|
|
172
|
-
appearing for connector-required conventions, approval-bound tools, and
|
|
173
|
-
truncated schemas — and being absent from a search that asked for no schemas.
|
|
174
|
-
`test/server.test.ts` owns the conditional half: it compares a guide-free
|
|
175
|
-
deployment's four short pointers against a guided one's, and asserts the
|
|
176
|
-
complete `usage` skill is byte-identical between them.
|