@zackbart/connecta 0.30.0 → 0.32.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/CHANGELOG.md +66 -0
- package/README.md +21 -16
- package/bin/connecta.mjs +1 -0
- package/bin/version.mjs +1 -1
- package/dist/auth-elicitation.js +1 -1
- package/dist/call-recovery.js +25 -0
- package/dist/catalog-service.js +7 -2
- package/dist/config-schema.d.ts +14 -7
- package/dist/config-schema.js +29 -10
- package/dist/config-value-sources.js +6 -3
- package/dist/config.d.ts +26 -8
- package/dist/config.js +38 -16
- package/dist/connector-access.d.ts +2 -1
- package/dist/connectors/guarded-fetch.d.ts +18 -1
- package/dist/connectors/guarded-fetch.js +21 -2
- package/dist/describe-config.d.ts +6 -3
- package/dist/describe-config.js +3 -2
- package/dist/errors.d.ts +3 -1
- package/dist/errors.js +1 -0
- package/dist/execute.js +16 -14
- package/dist/index.js +4 -3
- package/dist/invocation.js +39 -4
- package/dist/meta-tools.js +17 -19
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +2 -0
- package/dist/operator-ui/contract.d.ts +5 -4
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +1 -1
- package/dist/operator-ui/view.d.ts +9 -0
- package/dist/operator-ui/view.js +12 -0
- package/dist/program-writes.js +4 -49
- package/dist/provider.d.ts +6 -5
- package/dist/provider.js +3 -4
- package/dist/providers/_shared/google/workspace.js +1 -1
- package/dist/providers/_shared/rest/dispatch.js +41 -0
- package/dist/providers/_shared/rest/operation-index.d.ts +133 -0
- package/dist/providers/_shared/rest/operation-index.js +527 -0
- package/dist/providers/_shared/rest/tools.d.ts +141 -0
- package/dist/providers/_shared/rest/tools.js +569 -0
- package/dist/providers/_shared/rest/value-safety.d.ts +151 -0
- package/dist/providers/_shared/rest/value-safety.js +580 -0
- package/dist/providers/breeze/skill.generated.js +2 -2
- package/dist/providers/cloudflare/graphql.js +385 -0
- package/dist/providers/cloudflare/index.d.ts +47 -51
- package/dist/providers/cloudflare/index.js +188 -1997
- package/dist/providers/cloudflare/openapi.generated.js +3347 -0
- package/dist/providers/cloudflare/rest.d.ts +37 -0
- package/dist/providers/cloudflare/rest.js +1056 -0
- package/dist/providers/cloudflare/skill.generated.js +7 -4
- package/dist/providers/cloudflare/value-safety.js +1247 -0
- package/dist/providers/docs/index.d.ts +1 -1
- package/dist/providers/docs/index.js +2 -2
- package/dist/providers/drive/index.d.ts +1 -1
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/forms/index.d.ts +1 -1
- package/dist/providers/forms/index.js +2 -2
- package/dist/providers/github/skill.generated.js +2 -2
- package/dist/providers/gmail/index.d.ts +1 -1
- package/dist/providers/gmail/index.js +274 -23
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/linear/index.js +1 -1
- package/dist/providers/mixpanel/index.d.ts +27 -12
- package/dist/providers/mixpanel/index.js +58 -6
- package/dist/providers/mixpanel/operations.js +20 -0
- package/dist/providers/mixpanel/rest.js +603 -0
- package/dist/providers/mixpanel/skill.generated.js +3 -2
- package/dist/providers/mixpanel/value-safety.js +16 -0
- package/dist/providers/notion/index.d.ts +35 -31
- package/dist/providers/notion/index.js +76 -1499
- package/dist/providers/notion/openapi.generated.js +75 -0
- package/dist/providers/notion/rest.d.ts +24 -0
- package/dist/providers/notion/rest.js +1186 -0
- package/dist/providers/notion/skill.generated.js +5 -4
- package/dist/providers/notion/value-safety.js +31 -0
- package/dist/providers/planning-center/index.d.ts +27 -0
- package/dist/providers/planning-center/index.js +286 -24
- package/dist/providers/planning-center/skill.generated.js +24 -4
- package/dist/providers/sheets/index.d.ts +1 -1
- package/dist/providers/sheets/index.js +3 -3
- package/dist/providers/slides/index.d.ts +1 -1
- package/dist/providers/slides/index.js +3 -3
- package/dist/providers/stripe/index.d.ts +26 -20
- package/dist/providers/stripe/index.js +103 -130
- package/dist/providers/stripe/openapi.generated.js +649 -0
- package/dist/providers/stripe/rest.d.ts +14 -0
- package/dist/providers/stripe/rest.js +395 -0
- package/dist/providers/stripe/skill.generated.js +7 -5
- package/dist/providers/stripe/value-safety.js +705 -0
- package/dist/providers/vercel/index.d.ts +31 -22
- package/dist/providers/vercel/index.js +96 -853
- package/dist/providers/vercel/openapi.generated.js +456 -0
- package/dist/providers/vercel/rest.d.ts +20 -0
- package/dist/providers/vercel/rest.js +1002 -0
- package/dist/providers/vercel/skill.generated.js +4 -5
- package/dist/providers/vercel/value-safety.js +328 -0
- package/dist/providers/wispr-flow/index.d.ts +14 -0
- package/dist/providers/wispr-flow/index.js +117 -0
- package/dist/providers/wispr-flow/skill.generated.js +8 -0
- package/dist/routes/mcp.js +30 -10
- package/dist/routes/operator.js +14 -7
- package/dist/routes/ui-config.js +2 -2
- package/dist/server.js +3 -0
- package/dist/skills.js +5 -4
- package/dist/tool-safety.js +13 -5
- package/dist/types.d.ts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +1 -1
- package/dist/usage-guide.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/write-outcome.js +47 -0
- package/documentation/README.md +1 -0
- package/documentation/architecture.md +155 -28
- package/documentation/auth.md +86 -27
- package/documentation/code-mode.md +81 -72
- package/documentation/deploying.md +10 -5
- package/documentation/integrating.md +72 -9
- package/documentation/meta-tools.md +52 -38
- package/documentation/operating.md +11 -8
- package/documentation/operator-tests.md +2 -3
- package/documentation/operator-ui.md +9 -3
- package/documentation/provider-auth-migration.md +308 -0
- package/documentation/provider-migration-0.29.md +18 -6
- package/examples/worker/AGENTS.md +5 -0
- package/examples/worker/README.md +2 -2
- package/examples/worker/src/connecta.config.ts +5 -0
- package/package.json +7 -2
- package/templates/node/AGENTS.md +6 -4
- package/templates/node/README.md +6 -4
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +5 -0
- package/dist/providers/vercel/mcp-ownership.js +0 -212
- package/dist/tool-safety.d.ts +0 -9
package/documentation/auth.md
CHANGED
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
and package maintenance. This guide owns identity, grants, and downstream
|
|
7
7
|
credential contracts; use [deployment setup](./deploying.md) for runnable steps.
|
|
8
8
|
|
|
9
|
-
| Task
|
|
10
|
-
|
|
|
11
|
-
| Connect a machine or migrate an old client
|
|
12
|
-
| Configure caller visibility,
|
|
13
|
-
| Set up human sign-in
|
|
14
|
-
| Enable credential or connection controls
|
|
15
|
-
| Diagnose consent or token refresh
|
|
16
|
-
| Configure a downstream OAuth client or HTTP handler
|
|
17
|
-
| Review browser or downstream URL boundaries
|
|
9
|
+
| Task | Sections |
|
|
10
|
+
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| Connect a machine or migrate an old client | [Managed tokens](#managed-client-tokens-and-upgrading-from-v023), [static bearer migration](#migrating-static-bearer-clients) |
|
|
12
|
+
| Configure caller visibility, read-only endpoints, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth) |
|
|
13
|
+
| Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [management](#human-authentication-management) |
|
|
14
|
+
| Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting) |
|
|
15
|
+
| Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say) |
|
|
16
|
+
| Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens) |
|
|
17
|
+
| Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant) |
|
|
18
18
|
|
|
19
19
|
[Credential recognition and admission](#credential-recognition-and-admission)
|
|
20
20
|
defines refusal precedence.
|
|
@@ -90,6 +90,7 @@ createConnecta({
|
|
|
90
90
|
principal.id === "your-operator-id",
|
|
91
91
|
},
|
|
92
92
|
connectors, executor,
|
|
93
|
+
readOnly: false, // required: true refuses every write call
|
|
93
94
|
});
|
|
94
95
|
```
|
|
95
96
|
|
|
@@ -101,7 +102,7 @@ the identity namespaces and connector/pool grant callbacks too; storage
|
|
|
101
102
|
compatibility translates no configuration and invents no grants. `access_token`
|
|
102
103
|
actor ids, the `connecta:access-tokens:v1` activity namespace, friendly names,
|
|
103
104
|
and stored principals carry over; a token without a principal stays unbound.
|
|
104
|
-
Every request evaluates current identity, tool, and
|
|
105
|
+
Every request evaluates current identity, tool, and endpoint grants, and a token
|
|
105
106
|
never becomes an interactive operator. Names are labels, never permissions. A
|
|
106
107
|
UI-issued token belongs to the issuing human's principal, and any explicitly
|
|
107
108
|
permitted token manager can list, rename, or revoke deployment tokens. Machine tokens manage neither tokens nor connection credentials; operators
|
|
@@ -163,6 +164,10 @@ deployment's anonymous admission. Otherwise, or on `undefined`, the call fails
|
|
|
163
164
|
`auth_required` before any request leaves. Arguments, headers, and programs
|
|
164
165
|
never reach it; a deployment's own connectors still cannot read the caller.
|
|
165
166
|
|
|
167
|
+
The deployment's top-level `grant` decides who may use plain `/mcp`; named
|
|
168
|
+
pools use their own grants. These endpoint decisions receive the admitted
|
|
169
|
+
identity below and never widen its connector access.
|
|
170
|
+
|
|
166
171
|
`identity.connectorAccess` returns `"all"` (the default) or grants, for
|
|
167
172
|
discovery and use alike: a connector id opens every tool on it, a
|
|
168
173
|
`connector.tool` address that tool alone, and
|
|
@@ -206,6 +211,7 @@ createConnecta({
|
|
|
206
211
|
},
|
|
207
212
|
},
|
|
208
213
|
connectors: [issues, campaigns, billing], executor,
|
|
214
|
+
readOnly: false, // required: true refuses every write call
|
|
209
215
|
});
|
|
210
216
|
```
|
|
211
217
|
|
|
@@ -215,12 +221,12 @@ grant both connectors in full. Unknown or unprincipalled identities get `[]`.
|
|
|
215
221
|
Use a stable authenticated principal or subject, never request headers or tool
|
|
216
222
|
arguments. A token's stored principal is vouched for by inbound auth. Pools narrow their endpoint, not plain `/mcp`.
|
|
217
223
|
|
|
218
|
-
| Catalog change | Guarded exact grant
|
|
219
|
-
| -------------------------------------------------------- |
|
|
220
|
-
| New name | Excluded until reviewed and added
|
|
221
|
-
| Removed or renamed name | Unreachable; warned when the scoped view reads the catalog
|
|
222
|
-
| Stored write verdict after overrides and provider review | Removed from discovery and every invocation path, including `call_destructive_tool` and
|
|
223
|
-
| Schema change alone | Does not revoke, unless it breaks a reviewed digest: then it is a write, as above
|
|
224
|
+
| Catalog change | Guarded exact grant | Unrestricted exact string grant |
|
|
225
|
+
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
|
|
226
|
+
| New name | Excluded until reviewed and added | Excluded until reviewed and added |
|
|
227
|
+
| Removed or renamed name | Unreachable; warned when the scoped view reads the catalog | Same |
|
|
228
|
+
| Stored write verdict after overrides and provider review | Removed from discovery and every invocation path, including `call_destructive_tool` and programs on a writable endpoint | Still granted; `call_tool` refuses writes and write paths take over |
|
|
229
|
+
| Schema change alone | Does not revoke, unless it breaks a reviewed digest: then it is a write, as above | Same as above |
|
|
224
230
|
|
|
225
231
|
The example assumes both exact tools were reviewed as read-only; names imply no safety.
|
|
226
232
|
Review schemas, annotations, and downstream behavior
|
|
@@ -236,7 +242,7 @@ Failed remote loads are errors, not empty catalogs. A transient first-page tool
|
|
|
236
242
|
listing failure may serve complete facts in the same auth partition for five
|
|
237
243
|
minutes after expiry. Failed pagination, permission and auth failures do not
|
|
238
244
|
use this fallback. The expired deadline cannot authorize auth recovery. Personal OAuth ownership and credential
|
|
239
|
-
administration are separate from visibility;
|
|
245
|
+
administration are separate from visibility; writes follow the endpoint's `readOnly` setting and client approval. `test/identity-scope.test.ts` exercises these boundaries.
|
|
240
246
|
|
|
241
247
|
## Migrating static bearer clients
|
|
242
248
|
|
|
@@ -284,6 +290,8 @@ createConnecta({
|
|
|
284
290
|
storage,
|
|
285
291
|
accessTokens: accessTokens(storage),
|
|
286
292
|
auth: clerkAuth({ publishableKey, secretKey }),
|
|
293
|
+
readOnly: false, // required; pools without their own readOnly inherit it
|
|
294
|
+
grant: ({ actor }) => actor.kind !== "access_token", // machines use their pools
|
|
287
295
|
pools: {
|
|
288
296
|
support: {
|
|
289
297
|
tools: ["linear", "notion.search_pages", "notion.fetch_page"],
|
|
@@ -293,6 +301,11 @@ createConnecta({
|
|
|
293
301
|
tools: ["calendar.create_event"],
|
|
294
302
|
grant: ({ actor }) => actor.kind === "access_token" && actor.id === calendarTokenId,
|
|
295
303
|
},
|
|
304
|
+
reporting: {
|
|
305
|
+
tools: ["linear", "notion"],
|
|
306
|
+
readOnly: true,
|
|
307
|
+
grant: ({ principal }) => analysts.has(principal?.id ?? ""),
|
|
308
|
+
},
|
|
296
309
|
},
|
|
297
310
|
identity: { connectorAccess },
|
|
298
311
|
connectors, executor,
|
|
@@ -302,14 +315,59 @@ createConnecta({
|
|
|
302
315
|
Without configured `auth`, an open deployment has one anonymous, non-interactive
|
|
303
316
|
identity; grants like these return false and every pool path 404s.
|
|
304
317
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
318
|
+
The optional top-level `grant: (identity) => boolean | Promise<boolean>`
|
|
319
|
+
controls plain `/mcp`. Omitted, it admits everyone who passes auth, including
|
|
320
|
+
anonymous callers on an open deployment. Configured, only literal `true`
|
|
321
|
+
admits; other returns and throws deny. A refusal has the same 404 status,
|
|
322
|
+
body bytes, and headers as a refused or undeclared pool, after auth. The
|
|
323
|
+
operator log records `default_grant_denied` or `default_grant_threw`, never
|
|
324
|
+
the callback's error text. Invalid config values throw at construction.
|
|
325
|
+
Named pools use only their own grants, so a caller refused at `/mcp` may
|
|
326
|
+
still use a pool that grants it.
|
|
327
|
+
|
|
328
|
+
Protected-resource metadata stays public at both default aliases,
|
|
329
|
+
`/.well-known/oauth-protected-resource` and
|
|
330
|
+
`/.well-known/oauth-protected-resource/mcp`, just as at named-pool aliases.
|
|
331
|
+
Discovery does not authenticate callers or evaluate endpoint grants, so fresh
|
|
332
|
+
OAuth clients can discover how to authenticate. The grant applies after auth
|
|
333
|
+
to MCP requests and result/page handles. Handles remain bound
|
|
334
|
+
to their originating endpoint; paging rechecks the current endpoint grant,
|
|
335
|
+
identity, and connector access. A granting pool cannot read a `/mcp` handle,
|
|
336
|
+
and `/mcp` cannot read that pool's handle.
|
|
337
|
+
|
|
338
|
+
`describeConfig().hasGrant` and the operator UI report whether plain `/mcp`
|
|
339
|
+
has a configured grant without exposing callback source. `describeConfig()`
|
|
340
|
+
does not evaluate it. Operator catalog, config, activity, and connector auth
|
|
341
|
+
routes require the same default endpoint grant after their own authentication.
|
|
342
|
+
A refused caller gets a private JSON 404; the UI provides no per-pool view.
|
|
343
|
+
The public HTML shell carries no catalog data.
|
|
344
|
+
|
|
345
|
+
Each endpoint is writable or read-only. The deployment's `readOnly` sets the
|
|
346
|
+
plain `/mcp` endpoint and is required: a deployment that omits it fails at
|
|
347
|
+
construction and must choose `true` or `false`. A pool inherits it unless the
|
|
348
|
+
pool sets its own `readOnly`, in either direction. A writable endpoint lets
|
|
349
|
+
`call_destructive_tool` and programs write, and the MCP client decides what to
|
|
350
|
+
approve. On a read-only endpoint, write tools are absent from `search_tools`
|
|
351
|
+
results, `connecta.search`, and `connecta.describe`, and `call_tool`,
|
|
352
|
+
`call_destructive_tool`, and program writes are refused with
|
|
353
|
+
`read_only_endpoint` before validation or dispatch. Reads, including reads
|
|
354
|
+
through `call_destructive_tool`, still run.
|
|
355
|
+
Guides are documentation, not discovery: provider `SKILL.md` and usage guides,
|
|
356
|
+
surfaced through `queryAnalysis.guideSummary`, `skills`, `connecta.skill`, and
|
|
357
|
+
skill `resources/read`, are the same on every endpoint and may name tools a
|
|
358
|
+
read-only endpoint refuses.
|
|
359
|
+
Resource reads, grants, and `connectorAccess`, including guarded
|
|
360
|
+
`requireReadOnly` grants, are the same on both. The removed `trust` option fails
|
|
361
|
+
construction with a message naming `readOnly`.
|
|
362
|
+
|
|
363
|
+
| Rule | Contract |
|
|
364
|
+
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
365
|
+
| Scope | `/mcp/<pool>` intersects pool tools with the identity's `connectorAccess`, never widening it. Plain `/mcp` serves the identity's `connectorAccess` after its optional top-level `grant` admits it. |
|
|
366
|
+
| Grant | Pool grants default to deny; only literal `true` admits. Other returns, throws, and undeclared names produce an identical 404 status, body, and headers, after auth. Names are not anonymously enumerable; valid credentials cannot distinguish these cases by content. |
|
|
367
|
+
| Timing | Declared names await grants, so timing is not hidden. Keep grants pure and fast and names unsecret; names grant no access. The operator log records the refusal reason. |
|
|
368
|
+
| Construction | Malformed names, unknown options or connectors, empty pools, a non-boolean `readOnly`, a retired `trust`, and addresses absent from an `api()` static catalog throw. Remote catalogs are checked at lazy load. |
|
|
369
|
+
| Discovery | Clerk's pool 401 names `/.well-known/oauth-protected-resource/mcp/<pool>` with the pool URL as `resource`, matching RFC 9728. Cloudflare Managed OAuth is application-level and needs no pool setup. |
|
|
370
|
+
| Drift | No wildcards; tool grants are exact names. Missing live tools are unreachable and warned once while in a bounded 1,024-entry FIFO. |
|
|
313
371
|
|
|
314
372
|
## Shared and personal auth
|
|
315
373
|
|
|
@@ -809,6 +867,7 @@ createConnecta({
|
|
|
809
867
|
activityAccess: ({ id }) => id === "owner-id",
|
|
810
868
|
},
|
|
811
869
|
connectors, executor,
|
|
870
|
+
readOnly: false, // required: true refuses every write call
|
|
812
871
|
});
|
|
813
872
|
```
|
|
814
873
|
|
|
@@ -821,7 +880,7 @@ Access trusts the identity validated at the Worker edge.
|
|
|
821
880
|
|
|
822
881
|
```ts
|
|
823
882
|
import { cloudflareAccessAuth } from "@zackbart/connecta/auth/cloudflare-access";
|
|
824
|
-
createConnecta({ auth: cloudflareAccessAuth(), connectors, executor });
|
|
883
|
+
createConnecta({ auth: cloudflareAccessAuth(), connectors, executor, readOnly: false });
|
|
825
884
|
```
|
|
826
885
|
|
|
827
886
|
The adapter trusts only `ctx.access`, which Cloudflare attaches after validating
|
|
@@ -1011,7 +1070,7 @@ fallbacks, including a failed listing followed by cached annotations, cannot
|
|
|
1011
1070
|
authorize a re-run. Normal calls retain their existing stale-fallback behavior.
|
|
1012
1071
|
Before an accepted retry restarts any call, it rechecks every entered read.
|
|
1013
1072
|
A write classification, changed classification digest, or stale catalog returns
|
|
1014
|
-
`auth_replay_refused` with reconciliation guidance, including in
|
|
1073
|
+
`auth_replay_refused` with reconciliation guidance, including in programs on a writable endpoint.
|
|
1015
1074
|
Pre-invocation credential-resolution eligibility is unchanged.
|
|
1016
1075
|
Custom vaults may implement the host-only `requestStateKey` method; without it, the
|
|
1017
1076
|
deployment retains the ordinary handoff. It must return a stable,
|
|
@@ -48,7 +48,7 @@ QuickJS subpath:
|
|
|
48
48
|
import { createConnecta } from "@zackbart/connecta";
|
|
49
49
|
import { quickJsExecutor } from "@zackbart/connecta/quickjs";
|
|
50
50
|
|
|
51
|
-
createConnecta({ executor: quickJsExecutor() /* connectors, auth, storage… */ });
|
|
51
|
+
createConnecta({ executor: quickJsExecutor(), readOnly: false /* connectors, auth, storage… */ });
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
`quickJsExecutor()` runs each program in a disposable child process, with CPU,
|
|
@@ -61,7 +61,7 @@ On Workers the Worker Loader is the sandbox; Dynamic Workers require the Paid pl
|
|
|
61
61
|
```ts
|
|
62
62
|
import { workerExecutor } from "@zackbart/connecta/worker";
|
|
63
63
|
|
|
64
|
-
createConnecta({ executor: workerExecutor({ loader: env.LOADER }) });
|
|
64
|
+
createConnecta({ executor: workerExecutor({ loader: env.LOADER }), readOnly: false /* … */ });
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
The adapter uses only `loader` and an optional `timeout`. Its admission defaults
|
|
@@ -283,7 +283,10 @@ signature is byte-identical to the one `search_tools` returns.
|
|
|
283
283
|
integration is ambiguous. `safety: "readOnly"` selects stored read verdicts;
|
|
284
284
|
`"approvalRequired"` selects stored write verdicts; omitted or `"all"` selects
|
|
285
285
|
both. Search and describe rows carry `classification`. These filters grant no
|
|
286
|
-
authority and change no
|
|
286
|
+
authority and change no call admission decision. On a read-only endpoint,
|
|
287
|
+
search and describe omit write tools (`W12`): a write is absent from every
|
|
288
|
+
filter, and describing one is `unknown_tool`. `connecta.skill` guides are not
|
|
289
|
+
filtered and may name such a write; calling it is still refused.
|
|
287
290
|
|
|
288
291
|
**S2.** A requested object schema carries `inputKeys`, `requiredInputKeys`
|
|
289
292
|
(declared properties only), and `outputKeys`: the names the rendered schema
|
|
@@ -394,10 +397,10 @@ and a 32 KiB bridge ceiling. `page` is zero-based and mutually exclusive with
|
|
|
394
397
|
are at least four bytes wide and align both edges to UTF-8 boundaries.
|
|
395
398
|
Follow `nextOffset`, reassemble inside the program, and return a reduced value.
|
|
396
399
|
Before returning a page, the host rechecks current authentication, connector/tool
|
|
397
|
-
grants, pool membership, and endpoint
|
|
398
|
-
tool classification. A read-only endpoint cannot page a
|
|
399
|
-
|
|
400
|
-
|
|
400
|
+
grants, pool membership, and whether the endpoint is read-only against both the
|
|
401
|
+
original and current tool classification. A read-only endpoint cannot page a
|
|
402
|
+
write result; it never runs a write, so any write result it is asked for came
|
|
403
|
+
from a writable endpoint. Binding
|
|
401
404
|
mismatches, revoked access, and old entries without bindings are `not_found`, as
|
|
402
405
|
are unknown or expired ids; a storage failure is `unavailable`. IDs are random UUIDs.
|
|
403
406
|
The `get_result` meta-tool is removed.
|
|
@@ -477,7 +480,7 @@ Connecta passes an admitted decoded URI as an opaque MCP `resources/read`
|
|
|
477
480
|
argument to the configured endpoint. It never fetches that
|
|
478
481
|
URI or creates a connector from it.
|
|
479
482
|
|
|
480
|
-
Resources are protocol reads
|
|
483
|
+
Resources are protocol reads on read-only and writable endpoints alike. A read
|
|
481
484
|
requires a grant to the whole connector through the endpoint/pool and identity
|
|
482
485
|
intersection. Exact-tool grants, including guarded grants, authorize no
|
|
483
486
|
resources. An absent, hidden, tool-restricted or resource-incapable connector
|
|
@@ -553,27 +556,27 @@ URL-elicitation hosts retain their existing flow.
|
|
|
553
556
|
may do. A provider maps each downstream failure to the code that tells the
|
|
554
557
|
caller what to do next, and never invents a cause it was not told.
|
|
555
558
|
|
|
556
|
-
| Code
|
|
557
|
-
|
|
|
558
|
-
| `unknown_address`
|
|
559
|
-
| `unknown_tool`
|
|
560
|
-
| `
|
|
561
|
-
| `auth_required`
|
|
562
|
-
| `invalid_args`
|
|
563
|
-
| `not_found`
|
|
564
|
-
| `conflict`
|
|
565
|
-
| `input_required_unsupported`
|
|
566
|
-
| `rate_limited`
|
|
567
|
-
| `unavailable`
|
|
568
|
-
| `timeout`
|
|
569
|
-
| `program_error`
|
|
570
|
-
| `cancelled`
|
|
571
|
-
| `connector_call_failed`
|
|
572
|
-
| `catalog_lookup_failed`
|
|
573
|
-
| `result_processing_failed`
|
|
574
|
-
| `result_too_large`
|
|
575
|
-
| `budget_exceeded`
|
|
576
|
-
| `write_outcome_unknown`
|
|
559
|
+
| Code | Raised when | `retryable` |
|
|
560
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
561
|
+
| `unknown_address` | no visible connector owns the tool address or grants the resource reader | false |
|
|
562
|
+
| `unknown_tool` | the connector has no such tool | false |
|
|
563
|
+
| `read_only_endpoint` | a program on a read-only endpoint attempted a write (`E4`) | false |
|
|
564
|
+
| `auth_required` | the credential is missing, expired, or rejected | false |
|
|
565
|
+
| `invalid_args` | arguments or discovery bounds were rejected | false |
|
|
566
|
+
| `not_found` | the downstream answered and the resource is not there — the one code that says skip this id rather than stop, raised only where the provider tells absence from a permission gap | false |
|
|
567
|
+
| `conflict` | the write named a base version someone else already moved past; nothing changed. `details.current` says where things stand (at most 20 whole-number entries) — re-read, reapply, retry with that base | false |
|
|
568
|
+
| `input_required_unsupported` | a downstream asked for input; use `nextAction` for the equivalent direct call | false |
|
|
569
|
+
| `rate_limited` | the downstream reported a rate limit | true |
|
|
570
|
+
| `unavailable` | the downstream is down or unreachable; optional sanitized `details.host` and `details.code` describe the transport failure without paths, queries, credentials, or provider prose | true |
|
|
571
|
+
| `timeout` | a call or sandbox deadline expired; details name operation, stage, elapsedMs, and deadlineMs | per operation |
|
|
572
|
+
| `program_error` | guest JavaScript failed; `details.name`, `line`, and a fixed repair hint describe it | false |
|
|
573
|
+
| `cancelled` | the run ended while this call was in flight (`E5`) | false |
|
|
574
|
+
| `connector_call_failed` | anything else the connector threw | per message |
|
|
575
|
+
| `catalog_lookup_failed` | the connector's catalog could not be loaded | per cause |
|
|
576
|
+
| `result_processing_failed` | the result could not be prepared | per message |
|
|
577
|
+
| `result_too_large` | a discovery response exceeded its byte bound | false |
|
|
578
|
+
| `budget_exceeded` | the run exhausted a host-call, write, or emitted-output budget | false |
|
|
579
|
+
| `write_outcome_unknown` | a program write was sent and no answer came back; it is never sent again (`W9`) | false |
|
|
577
580
|
|
|
578
581
|
**E3.** `auth_required` carries the same recovery envelope as `call_tool`:
|
|
579
582
|
`connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
|
|
@@ -581,16 +584,13 @@ caller what to do next, and never invents a cause it was not told.
|
|
|
581
584
|
sentence. A program cannot recover credentials — only an operator can — so stop
|
|
582
585
|
and let the failure reach the model.
|
|
583
586
|
|
|
584
|
-
**E4.** A tool classified as a write never runs
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
call is the approval, and the only one: generated code can neither mint the
|
|
592
|
-
capability nor approve its own write, and the model's short `reason` grants no
|
|
593
|
-
authority and never goes downstream.
|
|
587
|
+
**E4.** A tool classified as a write never runs on a read-only endpoint
|
|
588
|
+
(`W12`). A program's call is refused with `read_only_endpoint` before
|
|
589
|
+
validation and before anything is sent. The refusal carries no `nextAction` and
|
|
590
|
+
echoes no arguments: no route on that endpoint writes, so the program reports
|
|
591
|
+
it rather than retrying. Generated code can neither mint the capability nor
|
|
592
|
+
change the endpoint; on a writable endpoint, approval of the program as a write
|
|
593
|
+
belongs to the client.
|
|
594
594
|
|
|
595
595
|
**E5.** Failures of the _execution_, not of a call, never appear inside the
|
|
596
596
|
guest: admission rejection (`executor_overloaded`, retryable, with
|
|
@@ -631,7 +631,7 @@ Program diagnostic fields are bounded before transport, including escaped text;
|
|
|
631
631
|
a large custom error name cannot turn a failure into a truncated success.
|
|
632
632
|
|
|
633
633
|
**E7.** `retryable` for `unknown_address`, `unknown_tool`, and
|
|
634
|
-
`
|
|
634
|
+
`read_only_endpoint` is pinned false, never inferred from an
|
|
635
635
|
address containing `503`, `429`, or `temporar`. The first two carry
|
|
636
636
|
`nextAction: { function: "connecta.search", arguments: { query, connector?, includeSchemas: "compact" } }`
|
|
637
637
|
— scoped discovery keyed to the surface the caller has, since a program cannot
|
|
@@ -686,7 +686,7 @@ Caught call failures retain that connector visibility when catalog loading or
|
|
|
686
686
|
tool resolution fails. Recovery messages listing configured connectors or
|
|
687
687
|
local guides retain visibility of those contributors too.
|
|
688
688
|
Each page rechecks current auth,
|
|
689
|
-
grants, pool membership, and
|
|
689
|
+
grants, pool membership, and the endpoint's `readOnly`. Reading a stash inside a program carries
|
|
690
690
|
its dependencies forward to any new return stash. The page cap is the smallest
|
|
691
691
|
of the deployment cap and contributing connectors' effective `maxResultBytes`.
|
|
692
692
|
Read-only endpoints cannot page write results. A UUID is a handle, never a grant.
|
|
@@ -882,17 +882,17 @@ deadline past the ceiling means raising the ceiling too.
|
|
|
882
882
|
**L4.** Per-execution bounds that are contract, identical in both executors
|
|
883
883
|
because connecta enforces them above the sandbox:
|
|
884
884
|
|
|
885
|
-
| Bound
|
|
886
|
-
|
|
|
887
|
-
| Host calls per execution, shared by `search`, `describe`, `call`, and `read`
|
|
888
|
-
|
|
|
889
|
-
| Deadline per host call
|
|
890
|
-
| Discovery page
|
|
891
|
-
| `describe` addresses
|
|
892
|
-
| `describe` nearby suggestions
|
|
893
|
-
| Caller text echoed by `describe` recovery
|
|
894
|
-
| Result
|
|
895
|
-
| Logs presented to the model
|
|
885
|
+
| Bound | Value |
|
|
886
|
+
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
887
|
+
| Host calls per execution, shared by `search`, `describe`, `call`, and `read` | 20 by default, `execute.maxHostCalls` |
|
|
888
|
+
| Writes per run on a writable endpoint, on top of the host calls they also spend (`W10`) | 10 by default, `execute.maxWrites` |
|
|
889
|
+
| Deadline per host call | 15 s, `execute.hostCallTimeoutMs`; one deadline covers catalog resolution, admission, and the connector call |
|
|
890
|
+
| Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
|
|
891
|
+
| `describe` addresses | ≤ 100 |
|
|
892
|
+
| `describe` nearby suggestions | ≤ 3 canonical addresses per failed entry |
|
|
893
|
+
| Caller text echoed by `describe` recovery | ≤ 512 UTF-8 bytes per field, plus `…` |
|
|
894
|
+
| Result | 24,000 serialized characters |
|
|
895
|
+
| Logs presented to the model | 4,000 characters |
|
|
896
896
|
|
|
897
897
|
Every `call`, `read`, `search`, and `describe` spends one host call on entry, before
|
|
898
898
|
resolution, validation, or dispatch. Catching a local refusal refunds nothing.
|
|
@@ -901,7 +901,7 @@ The first call beyond the budget ends the host run with one non-retryable
|
|
|
901
901
|
`try/catch` cannot swallow it. Later host access, including `emit`, stops; pending
|
|
902
902
|
replies are withheld. The refusal reaches no connector, returns no partial result,
|
|
903
903
|
and discards accepted emits (`M4`). Calls still resolving or awaiting admission
|
|
904
|
-
are cancelled, including
|
|
904
|
+
are cancelled, including writes past their gate. Dispatched writes
|
|
905
905
|
drain under `W9`; unknown outcomes retain precedence. The lease then releases:
|
|
906
906
|
QuickJS terminates its child, and the Worker adapter disposes RPC and loader
|
|
907
907
|
handles independently of the guest deadline. Workers guest computation may
|
|
@@ -1003,22 +1003,22 @@ payload-free _by construction_ means the event has nowhere to put a payload.
|
|
|
1003
1003
|
distinctive output is the program source — exactly what a payload-free history
|
|
1004
1004
|
must never keep.
|
|
1005
1005
|
|
|
1006
|
-
**V5.** A
|
|
1006
|
+
**V5.** A write a program issues after it has already returned is not
|
|
1007
1007
|
sent and records nothing: it was never an attempt (`W9`).
|
|
1008
1008
|
|
|
1009
1009
|
## Writes
|
|
1010
1010
|
|
|
1011
|
-
|
|
1012
|
-
`
|
|
1013
|
-
|
|
1014
|
-
|
|
1011
|
+
Whether an endpoint writes belongs to deployment code. Every deployment sets
|
|
1012
|
+
`readOnly` for `/mcp` explicitly; construction fails without it. Each named pool inherits
|
|
1013
|
+
the deployment's `readOnly` unless it sets its own, in either direction. The
|
|
1014
|
+
client owns approval; Connecta answers one question per endpoint: can it write.
|
|
1015
1015
|
|
|
1016
1016
|
```ts
|
|
1017
1017
|
createConnecta({
|
|
1018
1018
|
connectors, executor,
|
|
1019
|
-
|
|
1019
|
+
readOnly: false, // required: false writes, true refuses every write call
|
|
1020
1020
|
pools: {
|
|
1021
|
-
|
|
1021
|
+
reporting: { tools: ["posthog"], readOnly: true, grant },
|
|
1022
1022
|
},
|
|
1023
1023
|
classification: {
|
|
1024
1024
|
posthog: { exec: "read" }, // only for a deployment with a read-only exec contract
|
|
@@ -1027,6 +1027,9 @@ createConnecta({
|
|
|
1027
1027
|
});
|
|
1028
1028
|
```
|
|
1029
1029
|
|
|
1030
|
+
The removed `trust` option, on the deployment or a pool, fails construction
|
|
1031
|
+
with a message naming `readOnly` (`INV-11`).
|
|
1032
|
+
|
|
1030
1033
|
The registry computes one `classifyTool` verdict on each request-scoped
|
|
1031
1034
|
published catalog entry. It caches and persists only raw downstream facts.
|
|
1032
1035
|
Every caller consumes that stored verdict. Config cannot classify an individual
|
|
@@ -1044,7 +1047,7 @@ fail construction; unknown remote tool keys fail publication of the whole
|
|
|
1044
1047
|
catalog. Overrides are exact tool names, including names containing dots.
|
|
1045
1048
|
They are never inferred from descriptions or arguments.
|
|
1046
1049
|
|
|
1047
|
-
**W9.** Programs
|
|
1050
|
+
**W9.** Programs on a writable endpoint may write. A dispatched write is awaited even
|
|
1048
1051
|
if the program does not await it, bounded by its host-call deadline. A write
|
|
1049
1052
|
that reaches accounting after the program settles is not sent. An unanswered
|
|
1050
1053
|
write makes the run fail `write_outcome_unknown` with
|
|
@@ -1054,7 +1057,7 @@ write is automatically retried, including an ambiguous timeout. A downstream
|
|
|
1054
1057
|
refusal or a tool's `isError` can establish failure; a timeout or an unanswered
|
|
1055
1058
|
transport failure leaves the outcome unknown.
|
|
1056
1059
|
|
|
1057
|
-
**W10.** `execute.maxWrites`, default 10, bounds
|
|
1060
|
+
**W10.** `execute.maxWrites`, default 10, bounds a program's writes in
|
|
1058
1061
|
addition to the host-call budget. It is checked after argument validation and
|
|
1059
1062
|
before admission, so an over-budget write spends no permit.
|
|
1060
1063
|
|
|
@@ -1067,14 +1070,20 @@ reconciliation. Any new call must use the original arguments when its echo is
|
|
|
1067
1070
|
partial or absent. A returned `writes` count cannot make a run safe to repeat.
|
|
1068
1071
|
See [argument echo rules](./meta-tools.md#echo-budgets) for supported schema shapes.
|
|
1069
1072
|
|
|
1070
|
-
**W12.**
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
`
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1073
|
+
**W12.** Writable endpoints annotate `execute_code` as a write
|
|
1074
|
+
(`readOnlyHint: false, destructiveHint: true`) and permit program writes and
|
|
1075
|
+
`call_destructive_tool`; `call_tool` still accepts only reads and refuses a write
|
|
1076
|
+
with `destructive_tool_requires_approval`. Read-only endpoints annotate
|
|
1077
|
+
`execute_code` as read-only (`readOnlyHint: true, destructiveHint: false`),
|
|
1078
|
+
omit write tools from `search_tools`, `connecta.search`, and
|
|
1079
|
+
`connecta.describe`, and refuse every write call through `call_tool`,
|
|
1080
|
+
`call_destructive_tool`, and programs with `read_only_endpoint` before
|
|
1081
|
+
validation or dispatch. Guides and skills are documentation and may still name
|
|
1082
|
+
those tools. `tools/list` is the same six tools on both. Approval belongs to the client.
|
|
1083
|
+
`readOnly` grants no tool or connector access and cannot be set by guest code,
|
|
1084
|
+
request arguments, or a downstream catalog. `execute.approval`,
|
|
1085
|
+
`Connector.approval`, per-tool approval exemptions, and pool `trust` are
|
|
1086
|
+
removed. `W1`–`W8` and `W11` remain retired.
|
|
1078
1087
|
|
|
1079
1088
|
## Executor exceptions
|
|
1080
1089
|
|
|
@@ -1225,8 +1234,8 @@ rejection, and branded adapter acceptance across module copies.
|
|
|
1225
1234
|
| `L5`, `L7`, `X2` | `test/quickjs-executor.node.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue) |
|
|
1226
1235
|
| `L6`, `X10` | `test/guest-api-contract-quickjs.node.test.ts` and `test/guest-api-contract.test.ts` (whole oversized reads and one-write pageable handles), `test/sqlite-storage.node.test.ts` and `test/d1-storage.node.test.ts` (shared stash transfers), `test/quickjs-executor.node.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message), `test/quickjs-child-stderr.node.test.ts` (outer reply serialization failure settles the call) |
|
|
1227
1236
|
| `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no event for the execution itself), `test/activity.test.ts` (the shared code → friction table, the identity clamp, the one-attempt floor), `test/operator-view.test.ts`, `test/sql-storage-contract.ts`, run by `test/d1-storage.node.test.ts` and `test/sqlite-storage.node.test.ts` (historical pause and approval rows still render and round-trip) |
|
|
1228
|
-
| `V5`, `W9` | `test/program-writes.test.ts` (an unawaited
|
|
1229
|
-
| `W10`, `W12` | `test/program-writes.test.ts` (a
|
|
1237
|
+
| `V5`, `W9` | `test/program-writes.test.ts` (an unawaited program write finished and recorded, its unknown outcome reported, counts on a failed program, the classification table), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
|
|
1238
|
+
| `W10`, `W12` | `test/program-writes.test.ts` (a writable endpoint's program write runs, a read-only endpoint omits writes from search and describe and refuses every write call on every path, including a write its guide names, a required deployment `readOnly`, the write budget, `readOnly` inheritance and overrides, `call_tool` still refusing, search and describe verdicts, construction refusals including retired `trust`), `test/operator-ui-model.test.ts`, `test/browser/operator-ui.spec.ts` (the badge) |
|
|
1230
1239
|
| `M1` | `test/guest-api-contract.test.ts` (invalid emits throw catchably, accept nothing), `test/execute-emit.test.ts` (rejected shapes, bounded native snapshots, paging before metadata traversal, and cyclic presentation refusal) |
|
|
1231
1240
|
| `M2`, `M3` | `test/guest-api-contract.test.ts` (delivery order, truncated return plus delivered blocks), `test/execute-emit.test.ts` (envelope, `structuredContent`, byte-for-byte no-emit path) |
|
|
1232
1241
|
| `M4` | `test/guest-api-contract.test.ts` (discard is visible), `test/execute-emit.test.ts` (structured and plain paths), `test/quickjs-executor.node.test.ts` (mid-run shutdown) |
|
|
@@ -64,7 +64,7 @@ through to `fetch`. The example owns those details and
|
|
|
64
64
|
|
|
65
65
|
## Compose capabilities and access
|
|
66
66
|
|
|
67
|
-
Both configurations use `defineConfig((env) => …)
|
|
67
|
+
Both configurations use `defineConfig((env) => …)`, a required executor, and a required `readOnly` choice.
|
|
68
68
|
[Integrating services](./integrating.md) explains the `connectors` array.
|
|
69
69
|
Configuration selects optional factories through explicit slots:
|
|
70
70
|
|
|
@@ -96,10 +96,15 @@ personal connection, token management, and activity access explicit.
|
|
|
96
96
|
[Clerk setup](./auth.md#clerk-oauth-tokens-and-operator-sessions) owns resource-bound
|
|
97
97
|
tokens and `aud_claim_enabled`. A person's operator session is not an MCP token.
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
Every deployment must set `readOnly` in its configuration; startup fails without
|
|
100
|
+
it, and the template and Worker example set `readOnly: false`. Each named pool
|
|
101
|
+
inherits that value unless it sets its own. A writable endpoint (`false`) allows
|
|
102
|
+
writes through `call_destructive_tool` and programs and marks `execute_code` as
|
|
103
|
+
a write; the client decides what to approve. A read-only endpoint (`true`) omits
|
|
104
|
+
write tools from search and describe and refuses every write call; provider
|
|
105
|
+
guides may still name those tools. Pool membership intersects
|
|
106
|
+
the caller's grants; it never widens them. See [pools](./auth.md#pools) before
|
|
107
|
+
changing `readOnly`.
|
|
103
108
|
|
|
104
109
|
## Verify and diagnose
|
|
105
110
|
|