@zackbart/connecta 0.22.3 → 0.24.0
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 +5 -0
- package/CHANGELOG.md +82 -0
- package/README.md +19 -11
- package/dist/activity-friction.d.ts +3 -0
- package/dist/activity-friction.js +19 -0
- package/dist/activity.d.ts +11 -2
- package/dist/activity.js +15 -19
- package/dist/auth/downstream-oauth.d.ts +2 -1
- package/dist/auth/downstream-oauth.js +10 -1
- package/dist/branding.d.ts +67 -0
- package/dist/branding.js +176 -0
- package/dist/catalog-service.d.ts +1 -6
- package/dist/catalog-service.js +3 -53
- package/dist/connectors/remote-mcp.js +3 -5
- package/dist/credential-contract.d.ts +24 -0
- package/dist/credential-contract.js +1 -0
- package/dist/credential-rules.d.ts +85 -0
- package/dist/credential-rules.js +107 -0
- package/dist/credentials.d.ts +4 -100
- package/dist/credentials.js +3 -107
- package/dist/errors.d.ts +1 -1
- package/dist/execute.d.ts +5 -52
- package/dist/execute.js +40 -347
- package/dist/executors/quickjs-protocol.d.ts +0 -7
- package/dist/executors/quickjs-protocol.js +2 -10
- package/dist/executors/quickjs.js +1 -1
- package/dist/index.d.ts +23 -56
- package/dist/index.js +30 -58
- package/dist/invocation.d.ts +0 -33
- package/dist/invocation.js +56 -124
- package/dist/meta-tools.d.ts +7 -6
- package/dist/meta-tools.js +14 -21
- package/dist/module-contracts.d.ts +19 -0
- package/dist/module-contracts.js +1 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +6 -3
- package/dist/operator-ui/view.d.ts +2 -18
- package/dist/operator-ui/view.js +3 -20
- package/dist/registry.d.ts +4 -1
- package/dist/registry.js +8 -6
- package/dist/routes/activity.js +1 -1
- package/dist/routes/credentials.js +5 -2
- package/dist/routes/mcp.js +7 -51
- package/dist/routes/oauth-management.d.ts +2 -0
- package/dist/routes/oauth-management.js +108 -0
- package/dist/routes/oauth.d.ts +0 -1
- package/dist/routes/oauth.js +21 -121
- package/dist/routes/shared.d.ts +19 -17
- package/dist/routes/shared.js +48 -44
- package/dist/routes/ui.js +36 -33
- package/dist/server.d.ts +1 -2
- package/dist/server.js +7 -45
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +55 -19
- package/dist/types.d.ts +3 -15
- package/dist/ui.d.ts +15 -70
- package/dist/ui.js +176 -317
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +31 -26
- package/documentation/auth.md +65 -111
- package/documentation/call-admission.md +10 -11
- package/documentation/cloudflare.md +1 -1
- package/documentation/code-mode.md +65 -260
- package/documentation/connectors.md +8 -6
- package/documentation/linear.md +1 -1
- package/documentation/meta-tools.md +37 -18
- package/documentation/mixpanel.md +1 -1
- package/documentation/notion.md +2 -2
- package/documentation/operations.md +19 -23
- package/documentation/operator-ui.md +82 -104
- package/documentation/optional-modules-upgrade.md +243 -0
- package/documentation/provider-conventions.md +7 -5
- package/documentation/revenuecat.md +1 -1
- package/documentation/storage-and-credentials.md +59 -40
- package/documentation/stripe.md +1 -1
- package/documentation/upgrading.md +83 -8
- package/ethos.md +36 -43
- package/examples/worker/AGENTS.md +3 -1
- package/examples/worker/README.md +68 -84
- package/examples/worker/src/d1-activity.ts +1 -1
- package/examples/worker/src/index.ts +11 -6
- package/package.json +18 -2
- package/templates/node/AGENTS.md +8 -6
- package/templates/node/README.md +56 -67
- package/templates/node/package.json +1 -1
- package/templates/node/src/file-activity.ts +1 -1
- package/templates/node/src/index.ts +11 -12
- package/dist/access-tokens.d.ts +0 -31
- package/dist/access-tokens.js +0 -236
- package/dist/apps-shell.d.ts +0 -37
- package/dist/apps-shell.js +0 -174
- package/dist/routes/access-tokens.d.ts +0 -6
- package/dist/routes/access-tokens.js +0 -83
|
@@ -8,9 +8,8 @@ annotations, and `get_result` pages bounded results.
|
|
|
8
8
|
|
|
9
9
|
Every deployment requires an executor and `tools/list` is exactly seven:
|
|
10
10
|
`execute_code`, `search_tools`, `call_tool`, `call_destructive_tool`,
|
|
11
|
-
`authorize_connector`, `get_result`, and `skills`. Discovery
|
|
12
|
-
|
|
13
|
-
inside a program ([#273](https://github.com/zackbart/connecta/issues/273)).
|
|
11
|
+
`authorize_connector`, `get_result`, and `skills`. Discovery uses `connecta.search` and `connecta.describe`; programs compose
|
|
12
|
+
calls with JavaScript promises ([#273](https://github.com/zackbart/connecta/issues/273)).
|
|
14
13
|
|
|
15
14
|
Code-first is what a model sees. Read-only work has two routes: `call_tool` for
|
|
16
15
|
one known address, and `execute_code` when discovery or any wider work is
|
|
@@ -21,8 +20,11 @@ The [guest API contract](./code-mode.md) is what a program is promised.
|
|
|
21
20
|
|
|
22
21
|
The route is chosen before discovery. An unknown address, a result that will be
|
|
23
22
|
reduced, a call whose arguments depend on an earlier result, or work with
|
|
24
|
-
multiple operations starts with
|
|
25
|
-
|
|
23
|
+
multiple operations starts with `execute_code` and keeps discovery, calls, and reduction inside it
|
|
24
|
+
when the schemas and result shapes suffice. An unfamiliar provider result may
|
|
25
|
+
return a small sample for inspection before continuing in another call. This
|
|
26
|
+
exception avoids repeated guesses at text formats or collection roots; it does
|
|
27
|
+
not restore a mandatory discovery-only round trip. Distinct operations get distinct short
|
|
26
28
|
`connecta.search` queries in that program. A known address needs only
|
|
27
29
|
`call_tool`.
|
|
28
30
|
|
|
@@ -41,11 +43,13 @@ The measurements never contain program source, arguments, values, addresses,
|
|
|
41
43
|
credentials, logs, or raw error text.
|
|
42
44
|
|
|
43
45
|
Nothing became unreachable. `connecta.describe` takes the same addresses and
|
|
44
|
-
formats as the internal catalog service,
|
|
45
|
-
|
|
46
|
+
formats as the internal catalog service, ordinary promises compose read-only
|
|
47
|
+
calls, and an unfiltered
|
|
46
48
|
`connecta.search({})` browses every catalog a program can reach. Live connector
|
|
47
49
|
probing is an operator concern: the operator pages and `/health` own it.
|
|
48
50
|
|
|
51
|
+
Program search includes a bounded `connectorTitle` on each tool when configured, so choosing an account or environment does not require a provider read. It is context, not a ranking input or proof of live access.
|
|
52
|
+
|
|
49
53
|
The three discovery routes use deliberately different envelopes. These are
|
|
50
54
|
their smallest successful one-tool shapes:
|
|
51
55
|
|
|
@@ -62,7 +66,7 @@ their smallest successful one-tool shapes:
|
|
|
62
66
|
|
|
63
67
|
The deployment-derived `execute_code` description includes a live connector
|
|
64
68
|
inventory before any catalog search. It preserves registry order and uses each
|
|
65
|
-
canonical id
|
|
69
|
+
canonical id and a distinct configured title without generating a second name for programs. Titles normalize whitespace and are capped at 48 UTF-8 bytes, so account and environment hints cannot consume the entire inventory.
|
|
66
70
|
The complete inventory line is capped at 256 UTF-8 bytes. Entries stay whole,
|
|
67
71
|
and a truncated line ends with the exact `+N more` count. This reads only the
|
|
68
72
|
configured registry: it loads no catalog, probes no credential, grants no
|
|
@@ -177,6 +181,22 @@ This split avoids two normative copies while preserving a valid first program
|
|
|
177
181
|
for clients that never fetch the skill. Deployments without connector guides
|
|
178
182
|
receive none of the short conditional guide pointers in their definitions.
|
|
179
183
|
|
|
184
|
+
## Task guidance
|
|
185
|
+
|
|
186
|
+
`skills({ name: "investigate" })` provides on-demand guidance for purchase
|
|
187
|
+
verification, experiment checks, and customer or deployment investigations.
|
|
188
|
+
The execute description points to it when planning is unclear; routine reads need no additional guide fetch. It explains how to
|
|
189
|
+
resolve app/account/environment, follow evidence across services, establish
|
|
190
|
+
capability limits, and stop with a clear answer or a specific gap. It is shared
|
|
191
|
+
guidance, not a saved workflow or a source of deployment-specific ids. Existing
|
|
192
|
+
connector titles, purposes, and guides still own those distinctions.
|
|
193
|
+
|
|
194
|
+
The usage skill keeps the executable mechanics. Its dependent-call example
|
|
195
|
+
searches each operation separately, uses the page's `tools` array and canonical
|
|
196
|
+
addresses, and reports unresolved evidence instead of inventing an address or
|
|
197
|
+
querying another account. Its source runs against local fixtures in the QuickJS
|
|
198
|
+
suite, including missing and approval-required evidence.
|
|
199
|
+
|
|
180
200
|
## Result representation
|
|
181
201
|
|
|
182
202
|
For object results, `structuredContent` is the canonical full-fidelity value.
|
|
@@ -296,19 +316,21 @@ Every typed `auth_required` call failure uses the same envelope:
|
|
|
296
316
|
handoff:
|
|
297
317
|
|
|
298
318
|
- `oauth`: an `authorizationUrl` and consent instructions;
|
|
299
|
-
- `operator_config`: an `operatorUrl`
|
|
319
|
+
- `operator_config`: an `operatorUrl` to the mounted connection UI, plus the
|
|
300
320
|
declared credential label and field names/guidance; or
|
|
301
321
|
- `unavailable`: an honest deployment/configuration message.
|
|
302
322
|
|
|
303
323
|
The class follows what the connector declares, not how it was authored: a
|
|
304
324
|
`remoteMcp()` connection using `auth: { type: "credential" }` declares a slot
|
|
305
|
-
and no OAuth flow, so it
|
|
306
|
-
|
|
325
|
+
and no OAuth flow, so it uses `operator_config` when both vault and UI are
|
|
326
|
+
configured. Without either it returns `unavailable`, never a dead UI link.
|
|
307
327
|
|
|
308
328
|
The tool accepts no secret. `force` applies only to OAuth and may discard its
|
|
309
329
|
stored grant before restarting consent. Static credential values are written
|
|
310
330
|
only through the same-origin interactive-user credential route, and only for a
|
|
311
|
-
connector visible to that user
|
|
331
|
+
connector visible to that user with the relevant shared or personal management
|
|
332
|
+
permission. OAuth start, including `force`, requires that permission too. Core
|
|
333
|
+
callbacks work without the UI for authorized interactive callers. After OAuth consent or a human update, retry
|
|
312
334
|
the original operation; a static update is read from the vault on the next call
|
|
313
335
|
and needs no redeploy.
|
|
314
336
|
|
|
@@ -338,8 +360,7 @@ That route echoes the caller's own arguments back only while they fit a
|
|
|
338
360
|
512-byte budget, and then whole — never clipped. An error envelope is not
|
|
339
361
|
size-guarded the way a result is, so an unbounded echo would let a large
|
|
340
362
|
argument object produce a refusal many times the deployment's result cap, on
|
|
341
|
-
both `call_tool` and
|
|
342
|
-
`connecta.batch`. Over budget, `args` is absent and the `purpose` says to
|
|
363
|
+
both `call_tool` and program calls through `connecta.call`. Over budget, `args` is absent and the `purpose` says to
|
|
343
364
|
re-send what was just sent: the agent already holds its own arguments, and half
|
|
344
365
|
of them would describe a call nobody made.
|
|
345
366
|
|
|
@@ -352,9 +373,7 @@ recovery query, each of which lands in both the text content and
|
|
|
352
373
|
the address is the thing being corrected, a clipped one still identifies the
|
|
353
374
|
mistake, and a short one — every real one — comes back exact and untagged.
|
|
354
375
|
|
|
355
|
-
|
|
356
|
-
address and points at `connecta.call`; the program or model must still choose
|
|
357
|
-
which one matches the user's intent. `call_destructive_tool` accepts an optional
|
|
376
|
+
`call_destructive_tool` accepts an optional
|
|
358
377
|
`reason` of at most 500 characters for the host's human approval view. It is
|
|
359
378
|
outer-call context only: Connecta neither treats it as authority nor passes it
|
|
360
379
|
to the downstream connector, and an empty or whitespace-only one is read as no
|
|
@@ -395,7 +414,7 @@ about the mistake as all 40,000 would.
|
|
|
395
414
|
A remote MCP tool's advertised `inputSchema` is checked in the shared
|
|
396
415
|
invocation path before admission and provider dispatch. A mismatch is the
|
|
397
416
|
non-retryable `invalid_args`, consistently across `call_tool`,
|
|
398
|
-
`call_destructive_tool`,
|
|
417
|
+
`call_destructive_tool`, generated-code failures, and rejected promises. The error
|
|
399
418
|
names the connector and operation and carries bounded `validation.issues`:
|
|
400
419
|
JSON Pointer `path`, schema-keyword `code`, and expected shape. Submitted
|
|
401
420
|
values are never copied into those findings.
|
|
@@ -50,7 +50,7 @@ password, not ordinary configuration. Mixpanel currently labels service-account
|
|
|
50
50
|
MCP authentication beta. Prefer OAuth unless the deployment is intentionally
|
|
51
51
|
headless.
|
|
52
52
|
|
|
53
|
-
The same service account can arrive from
|
|
53
|
+
The same service account can arrive from the connection UI at `/` instead, and there the
|
|
54
54
|
operator pastes the readable pair rather than an encoded blob:
|
|
55
55
|
|
|
56
56
|
```ts
|
package/documentation/notion.md
CHANGED
|
@@ -58,7 +58,7 @@ it does not assume the REST interface's endpoint budget describes MCP traffic.
|
|
|
58
58
|
|
|
59
59
|
One operator-managed credential: an internal integration token from
|
|
60
60
|
[notion.so/profile/integrations](https://www.notion.so/profile/integrations).
|
|
61
|
-
The deployment needs `
|
|
61
|
+
The deployment needs `vault: encryptedCredentialVault(storage, encryptionKey)` configured, or the token
|
|
62
62
|
cannot be stored and every call fails `auth_required` at use.
|
|
63
63
|
|
|
64
64
|
Two Notion-specific facts decide whether a working token is enough:
|
|
@@ -69,7 +69,7 @@ Two Notion-specific facts decide whether a working token is enough:
|
|
|
69
69
|
default. `list_comments` and `add_comment` fail with 403 until an operator
|
|
70
70
|
turns them on in Notion.
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
The connection UI at `/` offers a Test action, which calls `GET /v1/users/me` — the
|
|
73
73
|
cheapest call that proves a token is live — and reports the workspace it
|
|
74
74
|
authenticated into.
|
|
75
75
|
|
|
@@ -33,8 +33,7 @@ and `test/package-surface.test.ts` fails if the two ever drift apart.
|
|
|
33
33
|
There are exactly two deployment shapes.
|
|
34
34
|
[`templates/node/`](../templates/node/) is what `connecta init` copies — the
|
|
35
35
|
one standalone Node project, Docker-ready rather than Docker-only — and
|
|
36
|
-
[`examples/worker/`](../examples/worker/) is the Cloudflare shape. Both
|
|
37
|
-
the whole operator feature set; each README walks through its own enablement.
|
|
36
|
+
[`examples/worker/`](../examples/worker/) is the Cloudflare shape. Both show explicit optional modules; each README walks through enablement.
|
|
38
37
|
A third scaffold that is a diff away from either is the shape
|
|
39
38
|
[#344](https://github.com/zackbart/connecta/issues/344) deleted, so do not add
|
|
40
39
|
one.
|
|
@@ -84,22 +83,21 @@ optional.
|
|
|
84
83
|
| `connectors` | — (required) | the connector set ([connectors](./connectors.md)) |
|
|
85
84
|
| `executor` | — (required) | the sandbox `execute_code` runs in ([code mode](./code-mode.md#what-an-executor-must-implement)) |
|
|
86
85
|
| `auth?` | none ⇒ open (dev only) | one `InboundAuth` or an array; bearer providers are checked before interactive providers ([inbound auth](./auth.md)) |
|
|
87
|
-
| `identity?` | all
|
|
88
|
-
| `storage?` | `memoryStorage()` |
|
|
86
|
+
| `identity?` | all visible; auth management denied; interactive activity reads | `{ connectorAccess?, credentialAdministration?, personalConnection?, activityAccess? }` derives separate use and management permissions ([identity](./auth.md#principals-visibility-and-operators)) |
|
|
87
|
+
| `storage?` | `memoryStorage()` | connector state, catalogs, and result paging; pass storage explicitly to the optional vault ([storage](./storage-and-credentials.md)) |
|
|
89
88
|
| `publicUrl?` | per-request origin | public base URL; an HTTPS value also redirects inbound HTTP |
|
|
90
|
-
| `logger?` | `console`, prefixed `[connecta]` | `{ debug, info, warn, error }` |
|
|
91
|
-
| `
|
|
89
|
+
| `logger?` | `console`, prefixed `[connecta]` | `{ debug, info, warn, error }`, or `"silent"` to suppress diagnostic output; independent of activity history |
|
|
90
|
+
| `ui?` | unset | `operatorUi({ branding? })` from `/ui`; omitted means no browser UI routes or bundle imports |
|
|
92
91
|
| `serverInfo?` | `connecta` / package version | `{ name, version, title?, websiteUrl?, icons? }` per the MCP icons spec |
|
|
93
92
|
| `deploymentInfo?` | unset | arbitrary metadata exposed by `/health` |
|
|
94
|
-
| `activity?` | unset | `{ store, readGate?, deploymentId? }`
|
|
95
|
-
| `
|
|
96
|
-
| `accessTokens?` | unset | `{ maxActive? }` (default 100) for operator-issued MCP bearer tokens. Requires an interactive operator provider, or construction throws ([access tokens](./auth.md#operator-issued-access-tokens)) |
|
|
93
|
+
| `activity?` | unset | `activityHistory({ store, readGate?, deploymentId? })` from `/activity`; omitted means no event recording or history reads |
|
|
94
|
+
| `vault?` | unset | `CredentialVault`; `encryptedCredentialVault(storage, encryptionKey)` from `/credentials` supplies the built-in encrypted implementation |
|
|
97
95
|
| `discovery.concurrency?` | 4 | connector catalogs/status probes in flight at once |
|
|
98
96
|
| `discovery.catalogTtlSeconds?` | 300 | fresh TTL for cached tool lists |
|
|
99
97
|
| `discovery.persistCatalog?` | true | persist serializable catalogs as a manifest plus revision-addressed chunks |
|
|
100
98
|
| `discovery.staleCatalogSeconds?` | 3600 | how long a complete expired catalog stays usable for agent SWR and as a refresh-failure fallback |
|
|
101
99
|
| `discovery.probeTimeoutMs?` | 30_000 | per-connector deadline for catalog fan-out; a timed-out connector degrades alone. Not a tool-call deadline |
|
|
102
|
-
| `calls.defaultTimeoutMs?` | **unset (opt-in)** | deadline for calls that pass no `timeoutMs`.
|
|
100
|
+
| `calls.defaultTimeoutMs?` | **unset (opt-in)** | deadline for calls that pass no `timeoutMs`. Each admitted call makes one attempt |
|
|
103
101
|
| `calls.maxResultBytes?` | 50_000 | inline result cap before truncation and `get_result` paging; a connector may override it. Invalid values warn and fall back |
|
|
104
102
|
| `execute.maxEmittedBytes?` | 4_000_000 | aggregate `connecta.emit` bytes per run — a transport bound, not a context bound |
|
|
105
103
|
| `execute.maxEmittedBlocks?` | 32 | content blocks `connecta.emit` accepts per run |
|
|
@@ -229,7 +227,6 @@ in.
|
|
|
229
227
|
|
|
230
228
|
| Suite | Covers |
|
|
231
229
|
| --- | --- |
|
|
232
|
-
| `access-tokens.test.ts` | the `AccessTokenManager` — a one-time secret created, authenticated, renamed, and revoked, bounded names and active count, enumerable storage required, a deployment with no interactive operator refused — and the operator-only routes, down to historical activity still resolving a revoked token's name |
|
|
233
230
|
| `activity.test.ts` | payload-free delivery: a rejected async write attaches to `waitUntil` instead of throwing, approved destructive calls record under their real entry point, result-size friction records without retaining the result, and a hallucinated connector id or invented identity is clamped so the event still cannot carry a payload |
|
|
234
231
|
| `api-connector.test.ts` | `api()` — kind, description, tool defs, dispatch, default args, unknown tools, handler throws, argument validation, and the construction contract |
|
|
235
232
|
| `bearer.test.ts` | constant-time bearer compare, case-insensitive scheme, 401 challenges, and the retired audience options refusing rather than silently unbinding |
|
|
@@ -240,20 +237,19 @@ in.
|
|
|
240
237
|
| `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 |
|
|
241
238
|
| `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 |
|
|
242
239
|
| `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 |
|
|
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
|
|
240
|
+
| `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 no rendering instructions |
|
|
244
241
|
| `codemode-compat.test.ts` | the `Executor` seam staying structurally compatible with `@cloudflare/codemode`'s `DynamicWorkerExecutor`, enforced by `tsc` |
|
|
245
242
|
| `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 |
|
|
246
243
|
| `credentials.test.ts` | the pure stored-shape classifier (containment, not equality) and the AES-GCM vault: round-trip, ciphertext bound to its connector id, named field sets, masked metadata, wrong-key rejection, deletion, coexistence with OAuth keys |
|
|
247
244
|
| `d1-activity-example.test.ts` | the Worker example's deployment-owned D1 activity store: actor namespace round-trip, payload-free friction reconstructed from the persisted code, and agreement with the package's friction table |
|
|
248
245
|
| `downstream-oauth.test.ts` | `KvOAuthProvider` round-trips and generation races, runtime-local rotating-token refresh coordination across request scopes, refresh failure/retry, `auth_required` versus `error`, `startAuth`/`finishAuth`, callback refusal equality, bounded diagnostics, and HTML escaping |
|
|
249
246
|
| `errors.test.ts` | `ConnectorCallError` codes, retryable defaults and overrides, `retryAfterMs` round-trip, typed-over-heuristic classification, `AbortError` as a retryable timeout, and framing errors |
|
|
250
|
-
| `execute.test.ts` | the code-mode host bridge: identifier sanitization, MCP-result unwrapping, sandbox provider construction, authenticated thrown-failure framing, fail-closed filtering of destructive and unannotated tools, MCP/code-mode invocation parity, and payload-free describe diagnostics |
|
|
247
|
+
| `execute.test.ts` | the code-mode host bridge: identifier sanitization, account titles in program discovery, MCP-result unwrapping, sandbox provider construction, authenticated thrown-failure framing, fail-closed filtering of destructive and unannotated tools, MCP/code-mode invocation parity, and payload-free describe diagnostics |
|
|
251
248
|
| `execute-emit.test.ts` | `connecta.emit` (M1–M10) — block validation, budgets, the provider, delivery after the result envelope on success only, and the defaults |
|
|
252
|
-
| `execute-ui.test.ts` | display-only `connecta.ui` (U1–U13) — one-string validation, multiplicity and budget, the provider, `_meta` delivery, shell isolation, and the absence of a payload-to-host call path |
|
|
253
249
|
| `executor-admission.test.ts` | the portable bounded FIFO both pools use: active and queue ceilings, stable retryable overload, queue timeout, cancellation removal, idempotent release, shutdown |
|
|
254
250
|
| `guarded-fetch.test.ts` | the guarded transport — construction, request building, destination confinement, and response handling |
|
|
255
|
-
| `guest-api-contract.test.ts` | the shared guest contract on the Dynamic Worker, including caught call, typed inline describe recovery, discovery, utility,
|
|
256
|
-
| `identity-scope.test.ts` | identity-derived connector visibility, personal credential isolation, shared-auth
|
|
251
|
+
| `guest-api-contract.test.ts` | the shared guest contract on the Dynamic Worker, including caught call, typed inline describe recovery, discovery, utility, parallel-call, and budget failure codes; plus the real authority boundary — local `data:` fetch, denied egress, unresolved DNS, empty environment paths, unavailable filesystem/HTTP builtins, and present runtime globals |
|
|
252
|
+
| `identity-scope.test.ts` | identity-derived connector visibility, personal credential isolation, separate shared-auth and personal-auth management permissions, and personal OAuth callback ownership |
|
|
257
253
|
| `linear-provider.test.ts` | the Linear proxy's construction, guide, plan-aware catalog superset, and current workspace, template, and issue-sharing classifications |
|
|
258
254
|
| `meta-tools-call.test.ts` | registry-backed calls: structured errors, truncation and `get_result`, per-connector result bounds, JSON representation failures, MCP content bounds, and offset alignment |
|
|
259
255
|
| `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 |
|
|
@@ -262,17 +258,18 @@ in.
|
|
|
262
258
|
| `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 |
|
|
263
259
|
| `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 |
|
|
264
260
|
| `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 |
|
|
261
|
+
| `optional-modules.test.ts` | absent modules, UI-free OAuth, fast lists and independent detail deadlines, explicit auth-management grants, invalid-resolver refusal, and passive OAuth consent-state protection |
|
|
265
262
|
| `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) |
|
|
266
263
|
| `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 |
|
|
267
264
|
| `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 |
|
|
268
265
|
| `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 |
|
|
269
|
-
| `remote-mcp-credential.test.ts` | `remoteMcp()` drawing a static key from
|
|
266
|
+
| `remote-mcp-credential.test.ts` | `remoteMcp()` drawing a static key from the connection UI: 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 |
|
|
270
267
|
| `remote-mcp-pagination.test.ts` | the `tools/list` cursor chain in both directions — exact cursor handoff, first-wins dedup, a failed later page rejecting rather than returning its prefix, the runaway backstops, the tool-metadata re-prime across pages, and paginated catalogs reaching the discovery path |
|
|
271
268
|
| `request-admission.test.ts` | `/mcp` bounded before auth, the stable 503 and `Retry-After`, health and operator responsiveness under saturation, payload-free counters, queued cancellation, shutdown rejection while active work drains, and the separate fallback code pool |
|
|
272
269
|
| `result-shapes.test.ts` | passive output-shape learning: value-free bounded inference, merging, 256-entry LRU eviction, 24-hour expiry, runtime isolation, read-only admission, declared-schema precedence, definition-change invalidation, discovery provenance, and failure isolation |
|
|
273
270
|
| `revenuecat-provider.test.ts` | the RevenueCat proxy's per-project key scoping and account-wide OAuth guides, its purpose-bearing summary, the refund-preference read and argued borderline verdicts in its digest-free manifest, and the deliberately unclassified `render-paywall-screenshot` |
|
|
274
|
-
| `server.test.ts` | end-to-end `/mcp` (401 → compact initialize instructions → seven compact definitions with bounded connector inventory
|
|
275
|
-
| `server-route-contracts.test.ts` | the route contracts `server.ts` must keep byte-identical:
|
|
271
|
+
| `server.test.ts` | end-to-end `/mcp` (401 → compact initialize instructions → seven compact definitions with bounded connector inventory, account titles, on-demand investigation guidance, and no Apps metadata or resource capability → complete usage skill → `call_tool`), conditional guide pointers, open routes, Clerk `.well-known` metadata without network, code mode, removed connector HTTP hooks rejected at construction, and deferred catalog reads through both discovery surfaces |
|
|
272
|
+
| `server-route-contracts.test.ts` | the route contracts `server.ts` must keep byte-identical: built-in routes and unknown-path 404s inside the security wrapper, open data-free shells with framing denied, per-route auth and same-origin requirements with exact 401/403/405 bodies, and OAuth `verifyState`-before-`finishAuth` ordering |
|
|
276
273
|
| `startup-warnings.test.ts` | every construction-time `logger.warn` and, as importantly, the conditions that must *not* trigger one: open mode with a credential or OAuth connector, `publicUrl` unset beside OAuth, dropped branding and `uiAuth` URLs, a missing `verifyState`, a credential test-hook mismatch, and an unusable `calls.maxResultBytes` |
|
|
277
274
|
| `stripe-provider.test.ts` | the Stripe proxy's mixed-mode OAuth and fixed-mode header contracts, current eleven-tool classifications, admission, exact account selectors, and no-guess rule |
|
|
278
275
|
| `operator-view.test.ts` | the app's pure state rules from `view.ts`: filtering, page routing, capability states, activity summaries, drift display, and identity reset |
|
|
@@ -288,7 +285,7 @@ justification for *not* re-running it in workerd, so "it was easier" is not one.
|
|
|
288
285
|
|
|
289
286
|
| Suite | Covers | Why Node |
|
|
290
287
|
| --- | --- | --- |
|
|
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,
|
|
288
|
+
| `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, explicit optional modules 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 |
|
|
292
289
|
| `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 |
|
|
293
290
|
| `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 |
|
|
294
291
|
| `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 |
|
|
@@ -300,7 +297,7 @@ justification for *not* re-running it in workerd, so "it was easier" is not one.
|
|
|
300
297
|
| `purity.test.ts` | the import-graph guardrail ([architecture](./architecture.md#import-graph-purity)) — the core stays Workers-clean | walks the source import graph with Node filesystem APIs |
|
|
301
298
|
| `quickjs-child-entry.test.ts` | a missing QuickJS child entry failing before `fork()`, with the expected path and the bundler-externalization constraint | mocks Node child-process and filesystem APIs |
|
|
302
299
|
| `quickjs-child-stderr.test.ts` | the QuickJS child-process boundary: an explicitly empty environment despite parent secrets and `NODE_OPTIONS`, plus abnormal exits retaining only an 8 KiB stderr tail in the parent-side diagnostic | mocks Node child-process streams |
|
|
303
|
-
| `quickjs-executor.test.ts` | the child-process sandbox — code normalization,
|
|
300
|
+
| `quickjs-executor.test.ts` | executable usage example with dependent, missing, and approval-required evidence; the child-process sandbox — code normalization, provider bridges and canonical connector calls, bounded IPC, separate guest-CPU and wall budgets, saturation, cancellation and shutdown, crash and OOM recovery, host-call hangs, stalled-promise detection | runs the Node QuickJS child-process executor |
|
|
304
301
|
| `quickjs-log-limits.test.ts` | bounded `console.*` capture — per-entry cut, cumulative character and transport budgets, escape-heavy floods preserving the guest result | runs the Node QuickJS child-process executor |
|
|
305
302
|
| `suite-partition.test.ts` | this partition, including itself: every `*.test.ts` in exactly one list, stale entries and empty reasons refused | walks the test directory to guard the partition |
|
|
306
303
|
| `template-file-activity.test.ts` | the Node template's own activity store — persistence across restart, torn-line repair, newest-first paging, and compaction past the slack window | runs it against real files |
|
|
@@ -311,8 +308,7 @@ justification for *not* re-running it in workerd, so "it was easier" is not one.
|
|
|
311
308
|
|
|
312
309
|
| Suite | Covers |
|
|
313
310
|
| --- | --- |
|
|
314
|
-
| `browser/operator-ui.spec.ts` | the operator wiring in a real browser: Clerk loader order across its version redirect and a real load failure, the shell staying open until authentication, credential and
|
|
315
|
-
| `browser/program-ui.spec.ts` | the display-only Apps shell in a real browser: local payload JavaScript runs, `connecta` stays absent, and forged payload messages never become host tool calls |
|
|
311
|
+
| `browser/operator-ui.spec.ts` | the operator wiring in a real browser: Clerk loader order across its version redirect and a real load failure, the shell staying open until authentication, credential and OAuth flows end to end, drift shown without naming a tool, and every failure and empty state |
|
|
316
312
|
|
|
317
313
|
**The `_transportFactory` seam.** `RemoteMcpOptions._transportFactory` is
|
|
318
314
|
internal, not public API: when set, `remoteMcp()` uses that `Transport` instead
|
|
@@ -4,109 +4,87 @@ The browser surface a human uses to see what a deployment exposes and to manage
|
|
|
4
4
|
the authentication material behind it. It is a small Preact app compiled by the
|
|
5
5
|
repository's own esbuild step and inlined into a data-free server shell.
|
|
6
6
|
|
|
7
|
-
Read [`ethos.md`](../ethos.md) first.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
the one on screen. `resetIdentity` replaces all identity-scoped state at once
|
|
89
|
-
and bumps a generation that work already in flight compares itself against.
|
|
90
|
-
- **Escaping is structural.** Components return elements; nothing builds HTML
|
|
91
|
-
from strings. A value that could be a URL passes `safeHttpHref` before it may
|
|
92
|
-
become an `href`, mirroring the server-side gate in `src/ui.ts`.
|
|
93
|
-
- **Secrets are shown once.** A created access token lives in state only, and
|
|
94
|
-
leaving the page — by navigation or by `pagehide`, which covers the
|
|
95
|
-
back-forward cache — unmounts it.
|
|
96
|
-
- **Every flow has four states.** Loading, error, empty, and success, with no
|
|
97
|
-
dead end: a failed save keeps the form and its typed value, a failed list
|
|
98
|
-
offers a retry, and an empty collection says what would fill it. A mutation
|
|
99
|
-
that fails is still a resolved promise — `mutate` lands the failure in state
|
|
100
|
-
rather than rejecting — so a caller that clears a form must clear it on a
|
|
101
|
-
confirmed success, never on resolution. `createAccessToken` returns that
|
|
102
|
-
answer as a boolean for exactly this reason.
|
|
103
|
-
- **Drift is counts, and absence is its own answer.** The connector card reads
|
|
104
|
-
`catalogDrift` ([#343](https://github.com/zackbart/connecta/issues/343)) as
|
|
105
|
-
four category counts and a timestamp. There is no drill-down, because a tool
|
|
106
|
-
name or a schema here would make an operator page the payload surface the
|
|
107
|
-
drift model refuses to be. A connector with no report renders as *not
|
|
108
|
-
observed*, never as clean: this runtime having seen no refresh is not the
|
|
109
|
-
same claim as a refresh having found nothing.
|
|
7
|
+
Read [`ethos.md`](../ethos.md) first. Code declares capabilities and access;
|
|
8
|
+
the UI displays the current user's effective permissions and manages only
|
|
9
|
+
authentication material explicitly permitted by that code. It never edits the
|
|
10
|
+
connector set, tool catalog, annotations, OAuth scopes, or permission rules.
|
|
11
|
+
|
|
12
|
+
## Enable the UI
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { operatorUi } from "@zackbart/connecta/ui";
|
|
16
|
+
|
|
17
|
+
createConnecta({
|
|
18
|
+
connectors,
|
|
19
|
+
executor,
|
|
20
|
+
auth,
|
|
21
|
+
ui: operatorUi({ branding: { productName: "Team connections" } }),
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The UI module owns its browser bundle and routes. Omit `ui` to omit those
|
|
26
|
+
routes and runtime imports. OAuth callbacks remain in core; authorized
|
|
27
|
+
interactive MCP callers can complete consent without the UI. Branding belongs
|
|
28
|
+
to `operatorUi` options, with neutral callback branding when no UI is mounted.
|
|
29
|
+
|
|
30
|
+
## Connections and activity
|
|
31
|
+
|
|
32
|
+
Connections is the main page. Each connection combines its status, effective
|
|
33
|
+
permissions, credential metadata, and permitted OAuth or credential actions.
|
|
34
|
+
There is no separate Credentials or Tokens tab. A user may see and invoke a
|
|
35
|
+
shared connector without permission to replace the grant everyone uses.
|
|
36
|
+
`identity.credentialAdministration` and `identity.personalConnection` select
|
|
37
|
+
shared and personal management rights, and both default to none.
|
|
38
|
+
|
|
39
|
+
Activity appears only when the optional history module has a readable store
|
|
40
|
+
and the caller passes `identity.activityAccess` and any additional read gate.
|
|
41
|
+
It is a global history, so permission to use one connector does not imply
|
|
42
|
+
permission to inspect that history. There is no member roster or policy editor.
|
|
43
|
+
|
|
44
|
+
The Node and Worker deployment READMEs show how to enable the modules and grant
|
|
45
|
+
the intended identities access. The configured bearer in the Node template can
|
|
46
|
+
read connection status but never mutate credentials as an interactive human.
|
|
47
|
+
|
|
48
|
+
## Loading and request lifetime
|
|
49
|
+
|
|
50
|
+
The server shell contains no connector or credential data. Authenticated
|
|
51
|
+
`/ui/data` returns the configured visible connection list without waiting for
|
|
52
|
+
provider status or tool discovery. Details load through `GET /ui/connectors/<id>`, independently,
|
|
53
|
+
under a bounded request lifetime. Unknown and loading states stay explicit;
|
|
54
|
+
a provider failure leaves the other connections usable.
|
|
55
|
+
|
|
56
|
+
A status read does not start OAuth or create authorization handoffs. Connect is
|
|
57
|
+
an explicit authorized POST. Successful save, reconnect, and disconnect actions
|
|
58
|
+
show their result without waiting for an unrelated full-catalog reload. Server
|
|
59
|
+
mutations still await catalog invalidation before replying, so another request
|
|
60
|
+
cannot consume a persisted catalog from before a credential change.
|
|
61
|
+
|
|
62
|
+
Each details request owns and closes its downstream connector scope. Never
|
|
63
|
+
cache a transport, request signal, or awaited promise in the UI module.
|
|
64
|
+
|
|
65
|
+
## Browser identity and security
|
|
66
|
+
|
|
67
|
+
Cloudflare Access is ambient browser auth. When the Worker invocation has
|
|
68
|
+
`ctx.access`, the shell emits no Clerk loader or browser-readable token.
|
|
69
|
+
Same-origin fetch carries the HttpOnly Access cookie, and the server uses the
|
|
70
|
+
trusted runtime identity. Sign out navigates to `/cdn-cgi/access/logout`.
|
|
71
|
+
Clerk deployments use their configured interactive provider.
|
|
72
|
+
|
|
73
|
+
Mutation requires exact same-origin `Origin`, an interactive identity,
|
|
74
|
+
connector visibility, and the relevant management permission. Personal actions
|
|
75
|
+
resolve only to the current principal's partition. Credential reads return
|
|
76
|
+
metadata, never saved values or masked fragments. Mutation cannot change any
|
|
77
|
+
declared capability. `test/operator-boundary.test.ts` checks that boundary.
|
|
78
|
+
|
|
79
|
+
The browser store fences responses by identity generation. Switching identity
|
|
80
|
+
clears the prior identity's state and discards its outstanding responses.
|
|
81
|
+
Components render elements, not HTML strings; links pass the shared URL gate.
|
|
82
|
+
Loading, failure, empty, and success states must all provide a useful next
|
|
83
|
+
step. A failed mutation preserves form input and does not masquerade as success.
|
|
84
|
+
|
|
85
|
+
Catalog drift remains counts and a timestamp. A missing observation means
|
|
86
|
+
"not observed", not that the downstream catalog is unchanged. The UI does not
|
|
87
|
+
expose tool schemas or raw payloads as diagnostics.
|
|
110
88
|
|
|
111
89
|
## Working on it
|
|
112
90
|
|
|
@@ -127,7 +105,7 @@ well as Node and there is no DOM in either:
|
|
|
127
105
|
DOM-lib program (`tsconfig.operator-ui.json`) because it imports the store.
|
|
128
106
|
- `test/browser/operator-ui.spec.ts` — the wiring, in a real browser:
|
|
129
107
|
Clerk loader order across its version redirect and a real load failure, plus
|
|
130
|
-
credential
|
|
108
|
+
credential and OAuth flows end to end, including their failure and
|
|
131
109
|
empty states. Run it with `npm run test:browser`
|
|
132
110
|
(`npm run test:browser:install` once, for Chromium). It is not part of
|
|
133
111
|
`npm run check`.
|