@zackbart/connecta 0.22.0 → 0.22.2

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/dist/version.d.ts CHANGED
@@ -4,4 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export declare const CONNECTA_VERSION = "0.22.0";
7
+ export declare const CONNECTA_VERSION = "0.22.2";
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export const CONNECTA_VERSION = "0.22.0";
7
+ export const CONNECTA_VERSION = "0.22.2";
@@ -1,20 +1,20 @@
1
1
  # Cloudflare prebuilt connection
2
2
 
3
3
  Import `cloudflare()` independently from
4
- `@zackbart/connecta/providers/cloudflare`. It is a deliberate, hand-written
5
- surface over Cloudflare's v4 REST API. Fifty-one tools combine ergonomic,
6
- fully described operations for common work with three guarded escape hatches
7
- for the rest of Cloudflare's fast-moving control plane. Reads, JSON mutations,
8
- and raw/multipart uploads remain separate so safety routing does not depend on
9
- an agent-supplied HTTP method. The connection keeps lean projections, typed
10
- failures, and a rate-limit budget matching the documented one. It adds no
11
- provider dependency, imports nothing outside Connecta, and is not reachable
12
- from Connecta's root entry.
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.
13
12
 
14
13
  ```ts
15
14
  import { cloudflare } from "@zackbart/connecta/providers/cloudflare";
16
15
 
17
16
  const edge = cloudflare("cloudflare_prod", {
17
+ surface: "api", // optional; this is the backward-compatible default
18
18
  title: "Production edge",
19
19
  purpose: "DNS and cache administration for the production estate",
20
20
  zoneId: "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
@@ -23,6 +23,15 @@ const edge = cloudflare("cloudflare_prod", {
23
23
  });
24
24
  ```
25
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
+
26
35
  Use the legacy user-scoped Global API Key when an existing deployment needs it:
27
36
 
28
37
  ```ts
@@ -41,6 +50,29 @@ between a production and a staging instance needs to know which one answers the
41
50
  question. Account `instructions` are appended to the maintained guide and
42
51
  cannot change the connector's safety classification.
43
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
+
44
76
  ## No SDK, on purpose
45
77
 
46
78
  Cloudflare publishes an official `cloudflare` npm SDK, and this connection does
@@ -61,7 +93,7 @@ claim: the `cloudflare` package must not appear in `dependencies`,
61
93
  `peerDependencies`, or `devDependencies`, and every import in the provider
62
94
  must be relative.
63
95
 
64
- ## Credentials
96
+ ## API credentials
65
97
 
66
98
  The default credential is a scoped Cloudflare API token, sent as
67
99
  `Authorization: Bearer <token>`. Create it under My Profile → API Tokens →
@@ -133,7 +165,7 @@ an empty `accountId` would fall back to the default again. A deployment that
133
165
  wants zones from one account passes `accountId` explicitly, and the property
134
166
  says so.
135
167
 
136
- ## Tools
168
+ ## API tools
137
169
 
138
170
  The named surface covers workflows that benefit most from concise schemas and
139
171
  projections:
@@ -423,6 +455,14 @@ not by Connecta. `maxConcurrency` is the bound that actually protects a shared
423
455
  credential, because a single `execute_code` program can fan out far faster than
424
456
  the window notices.
425
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
+
426
466
  ## Conventions
427
467
 
428
468
  This connection is audited against
@@ -136,11 +136,11 @@ few things both shapes fully own:
136
136
 
137
137
  - [Hand-written HTTP providers](./provider-conventions.md#hand-written-http-providers)
138
138
  (H1–H14) — `api()` surfaces where Connecta owns every name, schema,
139
- projection, and error. Cloudflare and Notion.
139
+ projection, and error. Cloudflare, Notion, and Vercel.
140
140
  - [Hosted-MCP proxies](./provider-conventions.md#hosted-mcp-proxies) (P1–P13) —
141
141
  `remoteMcp()` wrappers where the downstream owns the catalog and Connecta
142
142
  owns the endpoint, credential, classification, guide, and budget. Linear,
143
- Stripe, and Mixpanel.
143
+ Stripe, Mixpanel, and RevenueCat.
144
144
 
145
145
  Both sets are judged by one measure: what the convention saves the model that
146
146
  interacts with connecta, priced in discovery tokens, wrong-tool selection,
@@ -54,11 +54,29 @@ const analytics = mixpanel("product_analytics", {
54
54
  });
55
55
  ```
56
56
 
57
- The constructor may use `remoteMcp()` or `api()` internally. Callers should not
58
- need to care which transport gives the better agent-facing surface, and the
59
- choice does not grant the connection different runtime privileges. Two
60
- instances of the same provider are isolated in exactly the same way as two
61
- hand-written connectors with different ids.
57
+ The constructor may use `remoteMcp()` or `api()` internally. When a provider's
58
+ official MCP and HTTP API expose materially different capabilities or schema
59
+ ownership, the constructor may offer an explicit deployment-time surface
60
+ choice. It must document the difference, keep a backward-compatible default,
61
+ and never let an agent switch surfaces during a run. The choice grants no
62
+ different runtime privileges. Two instances of the same provider are isolated
63
+ in exactly the same way as two hand-written connectors with different ids.
64
+
65
+ That choice exists only when the two interfaces are genuinely different:
66
+
67
+ | Provider | Maintained interfaces | Why |
68
+ | --- | --- | --- |
69
+ | Cloudflare | API and MCP | The API interface has 48 projected named tools plus three safety-split hatches. The official MCP compresses more than 2,500 endpoints into `search` and approval-gated `execute`. |
70
+ | Notion | API and MCP | The API interface has stable lean projections. The official MCP adds Notion-owned live schemas, workspace search, files, views, agents, and sessions. |
71
+ | Vercel | API and MCP | The API interface has projected deployment operations. The official MCP owns a broader, independently changing catalog. |
72
+ | Linear | MCP | Vendoring its GraphQL API would create a second schema system rather than a distinct maintained interface. |
73
+ | Mixpanel | MCP | Its hosted service already joins several Mixpanel APIs; flattening those APIs would recreate the catalog problem. |
74
+ | RevenueCat | MCP | Its official server is generated from API v2, so a second wrapper would duplicate the same contract. |
75
+ | Stripe | MCP | Its official server already exposes both named workflows and supported API methods. A second raw API interface would duplicate it. |
76
+
77
+ This is not a requirement that every provider have two labels. A second choice
78
+ must change capability, result shape, or schema ownership enough to justify a
79
+ second contract. Otherwise it only gives agents two names for the same thing.
62
80
 
63
81
  A prebuilt connection's vetted annotations fill in downstream silence and
64
82
  otherwise preserve explicit annotations. This includes an explicit
@@ -137,6 +155,7 @@ Maintained provider guides:
137
155
  - [Notion](./notion.md)
138
156
  - [RevenueCat](./revenuecat.md)
139
157
  - [Stripe](./stripe.md)
158
+ - [Vercel](./vercel.md)
140
159
 
141
160
  ## The `api()` construction contract
142
161
 
@@ -226,12 +245,13 @@ into a result or a typed failure. That split is not fastidiousness. Notion's
226
245
  cannot fix it — while Cloudflare's means a token scope, and the two want
227
246
  opposite next moves. A helper that guessed would be wrong for one of them.
228
247
 
229
- Cloudflare and Notion both run on it. Their existing suites carried over
230
- unchanged, which proves the migration kept the behavior those suites cover
231
- not that nothing changed. Three things did, and the changelog names them: a
232
- 3xx is refused where both providers used to follow it, both now fail past
233
- their byte ceiling, and `cloudflare()`'s `baseUrl` is validated at
234
- construction. Each suite gained one test for the ceiling, because the one
248
+ Cloudflare, Notion, and Vercel run on it. The first two existing suites carried
249
+ over unchanged when the helper was extracted, which proves that migration kept
250
+ the behavior those suites cover — not that nothing changed. Three things did,
251
+ and the changelog names them: a 3xx is refused where both providers used to
252
+ follow it, both now fail past their byte ceiling, and `cloudflare()`'s
253
+ `baseUrl` is validated at construction. Each suite gained one test for the
254
+ ceiling, because the one
235
255
  guard the helper was written to add is the one a provider's own mapper can
236
256
  most easily disarm: a bare `catch` around `response.json()` swallows the
237
257
  transport's refusal along with a parse error, and turns a response nobody was
@@ -176,6 +176,15 @@ A budget-only rule needs no queue. If you add `maxConcurrency` you are asking
176
176
  for a queue, and the admission controller then requires the rest of the queue
177
177
  settings at construction.
178
178
 
179
+ ## Public contract check
180
+
181
+ `npm run drift:check -- --docs --provider linear` checks Linear's official MCP
182
+ setup page for the read-write endpoint and OAuth support without using a
183
+ credential. Linear does not publish an exact tool inventory there, so the
184
+ command says `setup metadata only` and makes no claim about names or schemas.
185
+ At runtime the live `tools/list` response remains the schema authority and is
186
+ passed through without a vendored replacement.
187
+
179
188
  ## Conventions
180
189
 
181
190
  This connection is audited against
@@ -85,9 +85,11 @@ A read-only live audit on 2026-08-13 confirmed all three refusals against the
85
85
  US hosted endpoint. They are reported upstream as
86
86
  [`mixpanel/mixpanel-headless#202`](https://github.com/mixpanel/mixpanel-headless/issues/202).
87
87
  The vetted catalog records current schema digests for all 64 tools,
88
- so a later schema correction or regression appears by tool name in the
89
- maintainer drift check. The guide can then shrink when the downstream schema
90
- becomes complete; Connecta does not absorb the defect permanently.
88
+ so a later schema correction or regression increments runtime drift when an
89
+ ordinary catalog refresh observes it. The live definition is still served
90
+ unchanged. The credential-free provider check does not depend on those digests.
91
+ The guide can shrink when the downstream schema becomes complete; Connecta does
92
+ not absorb the defect permanently.
91
93
 
92
94
  The wrapper classifies the documented observational tools as reads and the
93
95
  documented create, update, edit, merge, dismiss, duplicate, and delete tools as
@@ -142,6 +144,14 @@ for a queue, and the admission controller then requires the rest of the queue
142
144
  settings at construction. Discovery traffic is outside connector call admission
143
145
  either way and still needs restrained use.
144
146
 
147
+ ## Public contract check
148
+
149
+ `npm run drift:check -- --docs --provider mixpanel` compares Mixpanel's
150
+ official Available Tools table with the vetted manifest and checks all three
151
+ regional endpoints plus OAuth support. The current table lists 63 tools. It
152
+ omits `Fill-Event-Metadata`, which remains classified from the last
153
+ authenticated review and is reported as `not documented`, not silently removed.
154
+
145
155
  ## Conventions
146
156
 
147
157
  This connection is audited against
@@ -1,39 +1,60 @@
1
1
  # Notion prebuilt connection
2
2
 
3
3
  Import `notion()` independently from `@zackbart/connecta/providers/notion`. It
4
- is a hand-written `api()` surface over Notion's public REST API — fifteen
5
- deliberate tools, lean projections of Notion's famously bloated payloads, typed
6
- failures, a rate budget matched to the documented limit, and a required usage
7
- guide. It adds no provider dependency, imports no `node:` builtin, and is not
4
+ offers two deployment-time interfaces. The default is a hand-written `api()`
5
+ surface over Notion's public REST API: fifteen deliberate tools, lean
6
+ projections of Notion's famously bloated payloads, typed failures, a rate
7
+ budget matched to the documented limit, and a required usage guide. The other
8
+ choice is Notion's official hosted MCP with live provider-owned schemas and a
9
+ broader workspace, files, views, agents, and sessions catalog. Neither is
8
10
  reachable from Connecta's root entry.
9
11
 
10
12
  ```ts
11
13
  import { notion } from "@zackbart/connecta/providers/notion";
12
14
 
13
15
  const wiki = notion("engineering_wiki", {
16
+ surface: "api", // optional; this is the backward-compatible default
14
17
  title: "Engineering wiki",
15
18
  purpose: "Runbooks, specs, and on-call notes for the platform team",
16
19
  instructions: "Prefer the Runbooks database; specs live under Projects.",
17
20
  });
18
21
  ```
19
22
 
23
+ Use Notion's hosted MCP instead:
24
+
25
+ ```ts
26
+ const workspace = notion("notion_mcp", {
27
+ surface: "mcp",
28
+ purpose: "Workspace search, files, views, and asynchronous agent sessions",
29
+ });
30
+ ```
31
+
20
32
  The `id` owns the ordinary connector namespaces; use a different id for every
21
33
  Notion workspace. `purpose` is required because an agent choosing between two
22
34
  instances needs to know which workspace answers the question. Workspace
23
35
  `instructions` are appended to the maintained guide and cannot change the
24
36
  connector's safety classification.
25
37
 
26
- ## Why this one is `api()` and not `remoteMcp()`
38
+ ## Choosing an interface
27
39
 
28
- Notion publishes an MCP server, but the interesting problem here is not
29
- transport — it is shape. A single Notion page returns every property as a
40
+ Use the API interface for its compact, stable projections. A single Notion
41
+ page returns every property as a
30
42
  discriminated wrapper object, every string as an array of rich-text runs each
31
43
  carrying its own annotations block, and every user reference as a nested
32
44
  object. A twenty-five row database query is tens of kilobytes of structure
33
45
  around a few hundred bytes of meaning. Hand-writing the surface is what makes
34
46
  the projections possible, and the projections are the point.
35
47
 
36
- ## Authentication
48
+ Use `surface: "mcp"` for Notion's wider official capabilities, including
49
+ connected-source search, attachments, saved views, Notion Skills, agents, and
50
+ asynchronous sessions. Tool names and schemas come from the live server.
51
+ Connecta preserves them and only fills in release-reviewed safety annotations
52
+ when Notion is silent. OAuth is the hosted server's authentication contract.
53
+ Do not apply the hand-written REST schemas to similarly named MCP tools. The
54
+ MCP interface accepts `callAdmission` for an operator-supplied runtime policy;
55
+ it does not assume the REST interface's endpoint budget describes MCP traffic.
56
+
57
+ ## API authentication
37
58
 
38
59
  One operator-managed credential: an internal integration token from
39
60
  [notion.so/profile/integrations](https://www.notion.so/profile/integrations).
@@ -52,6 +73,10 @@ Two Notion-specific facts decide whether a working token is enough:
52
73
  cheapest call that proves a token is live — and reports the workspace it
53
74
  authenticated into.
54
75
 
76
+ The MCP interface uses Notion OAuth instead of the integration-token form. An
77
+ `auth_required` failure means the grant is absent or expired and must be
78
+ completed again through `authorize_connector`.
79
+
55
80
  ## The pinned API version
56
81
 
57
82
  The connection pins `Notion-Version: 2026-03-11` and offers no override. That
@@ -69,7 +94,10 @@ type's payload rather than switching exhaustively. A property type that ships
69
94
  after this release degrades to its raw value, and a block type that does keeps
70
95
  its payload under `raw`; neither vanishes.
71
96
 
72
- ## Tools
97
+ The remaining sections document the hand-written API interface. MCP tool
98
+ arguments and results are intentionally read from the live server instead.
99
+
100
+ ## API tools
73
101
 
74
102
  Ten reads, all annotated `readOnlyHint: true`:
75
103
 
@@ -229,13 +257,14 @@ Cursors are opaque. Notion's own versioning page is explicit that they may
229
257
  change in length, format, and structure at any time and must be passed back
230
258
  verbatim — never parsed, validated, or constructed.
231
259
 
232
- ## What this connection does not do
260
+ ## What the API interface does not do
233
261
 
234
262
  No file uploads, no database or data-source creation, no schema editing, no
235
263
  block updates or deletes, no page moves. Those are all real Notion endpoints
236
264
  and all deliberately absent: this is a deliberate tool surface, not a mirror of
237
- the API. Anything missing is reachable through a custom `api()` connector
238
- beside this one, which remains a first-class path.
265
+ the API. Some are present on Notion's hosted MCP interface. Anything still
266
+ missing is reachable through a custom `api()` connector beside this one, which
267
+ remains a first-class path.
239
268
 
240
269
  The 2026-03-11 contract also offers more fields on create and update. They were
241
270
  reviewed after the 0.17.0 drift check and remain deliberately absent:
@@ -263,6 +292,14 @@ The usage guide says it too, because an agent that assumes a hatch exists
263
292
  spends a search proving it does not: absent from the tool list means absent
264
293
  from this connection, not hidden behind a generic call.
265
294
 
295
+ ## Contract checks
296
+
297
+ `npm run providers:check` compares the 14 fixed REST endpoints with Notion's
298
+ published OpenAPI document and the 34 MCP names, endpoint, and OAuth support
299
+ with Notion's official pages. It needs no Notion credential. The MCP schemas
300
+ are not vendored or reconstructed: the live `tools/list` response remains the
301
+ contract agents receive.
302
+
266
303
  ## Conventions
267
304
 
268
305
  This connection is audited against
@@ -194,10 +194,15 @@ Two more runners are deliberately outside `check`:
194
194
  - `npm run test:browser` — Playwright against a real headless Chromium
195
195
  (`npm run test:browser:install` once). It covers the embedded bundle without
196
196
  adding a browser download to the CI release check.
197
- - `npm run drift:check` — the maintainer-run provider drift check, with local
198
- provider credentials exported. No credential goes near CI and nothing files
199
- itself; findings are read by a human and become issues
197
+ - `npm run drift:check` — the lower-level maintainer provider contract check.
198
+ It reads public MCP references and OpenAPI documents only. No provider
199
+ credential is read and nothing files itself; findings are read by a human
200
+ and become issues
200
201
  ([provider conventions](./provider-conventions.md#the-maintainer-run-drift-check)).
202
+ - `npm run providers:check` — the normal provider check across every maintained
203
+ provider: official MCP documentation plus the OpenAPI contracts for
204
+ hand-written HTTP connections. It uses the network, so it stays outside the
205
+ deterministic `check` chain.
201
206
  - `npm run load:admission` — the opt-in capacity matrix and soak
202
207
  ([request admission](./request-admission.md#measuring-capacity)).
203
208
 
@@ -234,7 +239,7 @@ in.
234
239
  | `catalog.test.ts` | lexical ranking and the compact schema renderer — `const`, `allOf` beside siblings, `$ref`, the depth limit, per-schema caching, and 2020-12 keyword compatibility |
235
240
  | `clerk.test.ts` | protected-resource metadata, the browser sign-in config, OAuth and session tokens, cached best-effort activity labels with their caps, the hand-applied `azp` rejection, and the `allowedDomains` allowlist including every lookalike that must not be repaired into a match |
236
241
  | `cloudflare-access-auth.test.ts` | trusted `ctx.access` human and service identities, absent/error fail-closed behavior, service-token MCP admission without operator mutation, human same-origin mutation, and the Clerk-to-ambient shell switch |
237
- | `cloudflare-provider.test.ts` | `cloudflare()` construction, tool surface, current R2 and KV jurisdictions, useful output declarations, request building, projections including additive provider fields, typed failures, and credential test |
242
+ | `cloudflare-provider.test.ts` | `cloudflare()` API and MCP construction, the code-mode safety manifest, API tool surface, current R2 and KV jurisdictions, useful output declarations, request building, projections including additive provider fields, typed failures, and credential test |
238
243
  | `code-first-surface.test.ts` | the seven-tool surface itself — an executor required, every removed option and top-level tool refused, compact always-loaded routing pinned below 1,000 characters, complete on-demand usage served, and `connecta.ui` findable before connector search |
239
244
  | `codemode-compat.test.ts` | the `Executor` seam staying structurally compatible with `@cloudflare/codemode`'s `DynamicWorkerExecutor`, enforced by `tsc` |
240
245
  | `config.test.ts` | the grouped `ConnectaConfig` boundary — each group forwarding to its internals, malformed admission bounds failing construction, and unknown own-properties rejected by their complete path before construction does work |
@@ -254,11 +259,11 @@ in.
254
259
  | `meta-tools-search.test.ts` | registry-backed discovery: bounded search with page and address maxima, compact and JSON schemas with constraints, typed describe recovery and suggestions, and structured-result compatibility |
255
260
  | `meta-tools.test.ts` | the remaining registry-backed meta-tools: the complete on-demand usage skill, connector-guide selection and summary bounds, stored-credential drift, catalog health, authorization, probe timeouts, and unavailable or unknown browse recovery |
256
261
  | `mixpanel-provider.test.ts` | the Mixpanel proxy, its conditional-input guide, destructive metadata fill, and complete 64-tool schema-digest manifest |
257
- | `notion-provider.test.ts` | Notion's deliberate tool surface, including declined expanded page inputs, request construction, lean projections, both pagination conventions, error mapping, and writes |
262
+ | `notion-provider.test.ts` | Notion's API and MCP construction, the hosted safety manifest and drift behavior, the deliberate REST surface including declined expanded page inputs, request construction, lean projections, both pagination conventions, error mapping, and writes |
258
263
  | `operator-boundary.test.ts` | the operator row of the decisions table, after every mutation route: authentication material managed without moving a declared structure, and the one honest exception — a credential write making a remote catalog appear, which is discovery arriving, not an operator editing the deployment |
259
264
  | `operator-store.test.ts` | `src/operator-ui/app/store.ts` against a fake browser: the Clerk listener, ambient Access requests without a browser-readable token, `gate()`, the generation fence, and the request path |
260
265
  | `provider-conventions.test.ts` | the conventions a test can hold: hand-written providers refusing schemas they cannot enforce (H5), their compact discovery schemas staying complete (H7), Cloudflare stating its second pagination convention in the schema (H10), and Notion saying it has no escape hatch (H14) |
261
- | `provider-registry.test.ts` | all six maintained providers inside real deployments: boot, description, address, catalog, storage, credential, admission, and activity isolation; plus provider-specific discovery and guide contracts |
266
+ | `provider-registry.test.ts` | all seven maintained providers inside real deployments: boot, description, address, catalog, storage, credential, admission, and activity isolation; plus provider-specific discovery and guide contracts |
262
267
  | `registry.test.ts` | construction and id validation, startup warnings, address resolution, version 2 catalog TTL/persistence/completeness, agent-only stale-while-revalidate with cross-request single-flight shared with blocking reads in both start orders, owned teardown, invalidation/fingerprint guards, blocking diagnostics, and broken-connector isolation |
263
268
  | `remote-mcp.test.ts` | `remoteMcp()` against an in-process server through the `_transportFactory` seam: passthrough, downstream `isError`, Workers-safe output-schema validation, request-scoped client reuse and at-most-once scope close; plus the real transport's manual redirect policy, destination guard, credential containment, and downstream session termination |
264
269
  | `remote-mcp-credential.test.ts` | `remoteMcp()` drawing a static key from `/credentials`: the declared slot and its refusal of named fields and bad header names, header framing (bearer, bare, and the two `Basic` forms) observed on the wire, an empty slot failing as `auth_required` rather than reaching the downstream, a value carrying a control character refused before framing and absent from every surface — `call_tool`, `status`, the Test result, the payload-free activity event, and the thrown error — rotation replacing the cached client and a connect already in flight while a wiped value fails the next call, the Test action's catalog probe and scope close, the cleartext-destination warning, and the vault and `authorize_connector` handoff end to end |
@@ -274,6 +279,7 @@ in.
274
279
  | `ui-credentials.test.ts` | credential-management routes: save, test, delete, validation, authentication, same-origin checks, and multi-field credential shapes |
275
280
  | `ui.test.ts` | the server shell and remaining `/ui/*` routes: gated `/ui/data` with broken-connector isolation and registry-owned catalog-observation containment, plus the URL safety gates |
276
281
  | `validate.test.ts` | `validateToolInput()` — a returned (not thrown) `invalid_args` naming the path, `additionalProperties: false` enforcement, per-schema validator caching, and an unusable schema passed through with one warning |
282
+ | `vercel-provider.test.ts` | `vercel()` API and MCP construction, MCP inventory classification, team scoping, project and deployment projections, finite build and runtime logs, value-safe environment variables, domains, lifecycle writes, REST hatches, typed failures, and credential test |
277
283
 
278
284
  ### Node-bound (`NODE_ONLY_SUITES`)
279
285
 
@@ -285,7 +291,7 @@ justification for *not* re-running it in workerd, so "it was easier" is not one.
285
291
  | `deployment-shapes.test.ts` | the Worker as the only example with a loader-only sandbox, its agent instructions and setup guide pinning Claude and both ChatGPT Managed OAuth callback forms, one Node template that is also its own container, the same source running locally and in the container, the Node template's pinned esbuild install-script approval, the full operator surface in both, a template that cannot start on its own `.env.example`, a Worker README naming every optional peer its entrypoint imports, and the initializer's `.gitignore` staying in step | walks the template and example trees with Node filesystem APIs |
286
292
  | `doc-links.test.ts` | the documentation checker itself — local file and fragment resolution, repository URLs resolved back to the checkout, duplicate heading slugs, fenced-code exclusion, and useful failures | spawns the Node checker against filesystem fixtures |
287
293
  | `doctor-cli.test.ts` | `connecta doctor`'s executor line and credentials end to end — the sandbox the deployment reports is the one named, an unidentifiable executor gets an executor-neutral line, a hostile name is bounded, and a complete Cloudflare Access service-token pair is accepted while a partial pair is refused | spawns the CLI against a Node HTTP deployment over real sockets |
288
- | `drift-check.test.ts` | the maintainer drift checker hosted-provider credential framing, recorded touched endpoints, a quiet revision bump, clear failures for an unavailable spec/manifest/credential, `$ref` traversal, and one well-formed row per endpoint | spawns the Node checker against filesystem fixtures |
294
+ | `drift-check.test.ts` | the credential-free maintainer drift checker: recorded touched endpoints, heading, table, and inline MCP inventories, setup-only providers, live-schema ownership, a quiet revision bump, clear failures for unavailable inputs, `$ref` traversal, and one well-formed row per endpoint | spawns the checker against filesystem fixtures |
289
295
  | `file-storage.test.ts` | `fileStorage()` across instances, logical TTL plus physical pruning without clobbering a newer value, and corrupt-file quarantine | exercises the Node filesystem storage adapter |
290
296
  | `guest-api-contract-quickjs.test.ts` | the shared guest-contract cases on the real QuickJS executor, including identical caught failure codes and inline describe recovery, its exact absent globals, and blocked runtime imports | runs the contract cases on the Node QuickJS executor |
291
297
  | `node.test.ts` | the `listen()` adapter propagating an HTTP client disconnect through the Web `Request` and the MCP handler into a program's connector call, releasing both admission permits | exercises the Node HTTP adapter over real TCP sockets |