@zackbart/connecta 0.32.0 → 0.33.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.
Files changed (137) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +15 -7
  3. package/bin/connecta.mjs +24 -6
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.d.ts +167 -23
  6. package/dist/access-tokens.js +676 -140
  7. package/dist/agent-identity.js +47 -0
  8. package/dist/auth/cloudflare-access.js +13 -5
  9. package/dist/call-admission-context.js +15 -0
  10. package/dist/call-admission.js +30 -43
  11. package/dist/call-recovery.js +6 -2
  12. package/dist/config-value-sources.js +11 -9
  13. package/dist/config.d.ts +37 -75
  14. package/dist/config.js +110 -36
  15. package/dist/connector-access.d.ts +4 -34
  16. package/dist/connector-access.js +15 -99
  17. package/dist/connector-caller.d.ts +7 -1
  18. package/dist/connector-caller.js +3 -2
  19. package/dist/connectors/api-connector.d.ts +27 -4
  20. package/dist/connectors/api-connector.js +2 -0
  21. package/dist/connectors/remote-mcp.js +2 -0
  22. package/dist/describe-config.d.ts +29 -7
  23. package/dist/describe-config.js +7 -17
  24. package/dist/index.d.ts +4 -3
  25. package/dist/index.js +29 -10
  26. package/dist/invocation.js +5 -3
  27. package/dist/membership.d.ts +51 -0
  28. package/dist/membership.js +121 -0
  29. package/dist/module-contracts.d.ts +23 -6
  30. package/dist/operator-record.d.ts +2 -2
  31. package/dist/operator-record.js +2 -4
  32. package/dist/operator-ui/agents.d.ts +74 -0
  33. package/dist/operator-ui/agents.js +104 -0
  34. package/dist/operator-ui/contract.d.ts +149 -28
  35. package/dist/operator-ui/endpoints.js +192 -0
  36. package/dist/operator-ui/fix-prompts.js +4 -4
  37. package/dist/operator-ui/generated.js +5 -4
  38. package/dist/operator-ui/member-page.js +112 -0
  39. package/dist/operator-ui/model.d.ts +14 -6
  40. package/dist/operator-ui/view.d.ts +84 -19
  41. package/dist/operator-ui/view.js +82 -21
  42. package/dist/page-styles.js +1 -1
  43. package/dist/provider.d.ts +61 -8
  44. package/dist/provider.js +46 -7
  45. package/dist/providers/_shared/google/workspace.d.ts +53 -16
  46. package/dist/providers/_shared/google/workspace.js +30 -8
  47. package/dist/providers/_shared/rest/tools.js +2 -2
  48. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  49. package/dist/providers/_shared/rest/value-safety.js +699 -7
  50. package/dist/providers/ccb/index.js +1 -1
  51. package/dist/providers/cloudflare/index.js +3 -3
  52. package/dist/providers/cloudflare/rest.js +25 -3
  53. package/dist/providers/cloudflare/skill.generated.js +1 -1
  54. package/dist/providers/cloudflare/value-safety.js +32 -0
  55. package/dist/providers/docs/index.js +3 -3
  56. package/dist/providers/docs/skill.generated.js +2 -2
  57. package/dist/providers/drive/index.js +3 -3
  58. package/dist/providers/drive/skill.generated.js +2 -2
  59. package/dist/providers/forms/index.js +3 -3
  60. package/dist/providers/forms/skill.generated.js +2 -2
  61. package/dist/providers/gmail/index.js +10 -8
  62. package/dist/providers/gmail/skill.generated.js +2 -2
  63. package/dist/providers/mixpanel/index.js +1 -1
  64. package/dist/providers/notion/index.d.ts +7 -0
  65. package/dist/providers/notion/index.js +100 -11
  66. package/dist/providers/notion/rest.js +45 -5
  67. package/dist/providers/notion/skill.generated.js +2 -2
  68. package/dist/providers/notion/value-safety.js +30 -5
  69. package/dist/providers/overflow/index.js +1 -1
  70. package/dist/providers/planning-center/index.js +1 -1
  71. package/dist/providers/sheets/index.d.ts +3 -0
  72. package/dist/providers/sheets/index.js +6 -3
  73. package/dist/providers/sheets/skill.generated.js +2 -2
  74. package/dist/providers/slides/index.js +3 -3
  75. package/dist/providers/slides/skill.generated.js +2 -2
  76. package/dist/providers/stripe/rest.js +9 -2
  77. package/dist/providers/stripe/skill.generated.js +1 -1
  78. package/dist/providers/stripe/value-safety.js +22 -0
  79. package/dist/providers/vercel/rest.js +20 -2
  80. package/dist/providers/vercel/skill.generated.js +1 -1
  81. package/dist/providers/vercel/value-safety.js +22 -0
  82. package/dist/registry.d.ts +2 -3
  83. package/dist/registry.js +8 -9
  84. package/dist/routes/access-tokens.js +82 -75
  85. package/dist/routes/activity-disclosure.js +17 -18
  86. package/dist/routes/activity.js +34 -54
  87. package/dist/routes/actor-labels.js +33 -4
  88. package/dist/routes/agent-path.js +43 -0
  89. package/dist/routes/connect.js +16 -12
  90. package/dist/routes/credentials.js +16 -40
  91. package/dist/routes/mcp.js +145 -95
  92. package/dist/routes/oauth-management.js +11 -5
  93. package/dist/routes/oauth.js +3 -9
  94. package/dist/routes/operator.js +24 -23
  95. package/dist/routes/shared.d.ts +60 -20
  96. package/dist/routes/shared.js +187 -96
  97. package/dist/routes/ui-config.js +437 -97
  98. package/dist/routes/ui-data.js +2 -2
  99. package/dist/routes/ui.js +9 -16
  100. package/dist/runtime/call-admission.js +9 -0
  101. package/dist/server.js +5 -0
  102. package/dist/storage/keys.js +9 -1
  103. package/dist/types.d.ts +11 -0
  104. package/dist/ui.d.ts +1 -1
  105. package/dist/ui.js +16 -14
  106. package/dist/version.d.ts +1 -1
  107. package/dist/version.js +1 -1
  108. package/dist/workspace-address.js +9 -0
  109. package/dist/workspace-subject.js +23 -0
  110. package/documentation/README.md +46 -22
  111. package/documentation/architecture.md +355 -50
  112. package/documentation/auth.md +775 -243
  113. package/documentation/code-mode.md +14 -11
  114. package/documentation/deploying.md +121 -24
  115. package/documentation/integrating.md +92 -21
  116. package/documentation/meta-tools.md +14 -9
  117. package/documentation/migration-0.33.md +344 -0
  118. package/documentation/operating.md +3 -1
  119. package/documentation/operator-tests.md +115 -28
  120. package/documentation/operator-ui.md +326 -61
  121. package/documentation/provider-auth-migration.md +37 -23
  122. package/documentation/provider-migration-0.29.md +2 -2
  123. package/examples/worker/AGENTS.md +30 -8
  124. package/examples/worker/README.md +92 -53
  125. package/examples/worker/src/connecta.config.ts +36 -20
  126. package/examples/worker/wrangler.jsonc +3 -0
  127. package/package.json +3 -2
  128. package/templates/node/.env.example +3 -0
  129. package/templates/node/AGENTS.md +27 -6
  130. package/templates/node/Dockerfile +2 -2
  131. package/templates/node/README.md +67 -42
  132. package/templates/node/docker-compose.yml +3 -2
  133. package/templates/node/package.json +4 -3
  134. package/templates/node/src/adopt-token.ts +23 -0
  135. package/templates/node/src/connecta.config.ts +28 -12
  136. package/templates/node/src/rotate-token.ts +17 -0
  137. package/templates/node/src/provision-token.ts +0 -17
@@ -80,7 +80,7 @@ and cleanup at budget exhaustion, cancellation, and deadlines; the wrapper only
80
80
 
81
81
  ## What an executor must implement
82
82
 
83
- The Promise provider/result seam lives in `src/types.ts`. Custom implementations
83
+ The Promise provider/result seam is declared in the installed `dist/types.d.ts`. Custom implementations
84
84
  also need the lifecycle wrapper above; structural compatibility alone is insufficient.
85
85
 
86
86
  ```ts
@@ -481,9 +481,9 @@ argument to the configured endpoint. It never fetches that
481
481
  URI or creates a connector from it.
482
482
 
483
483
  Resources are protocol reads on read-only and writable endpoints alike. A read
484
- requires a grant to the whole connector through the endpoint/pool and identity
485
- intersection. Exact-tool grants, including guarded grants, authorize no
486
- resources. An absent, hidden, tool-restricted or resource-incapable connector
484
+ requires the whole connector on the endpoint: plain `/mcp`, or a pool whose
485
+ `tools` list the connector id. A pool listing only exact `connector.tool`
486
+ addresses authorizes no resources. An absent, hidden, tool-restricted or resource-incapable connector
487
487
  fails with `unknown_address`. The URI and extra arguments cannot select a
488
488
  different credential owner or downstream subject. Personal credentials come
489
489
  from the admitted principal, just as they do for tool calls.
@@ -737,9 +737,6 @@ and nonzero pad bits are rejected before collection. Size budgets still apply.
737
737
  MCP-native output a return value cannot carry: base64 is not projectable, so a
738
738
  block that fits the inline call limit (`S5`, `L6`) can use the separate
739
739
  emission transport budget instead of the `R2` return guard.
740
- The earlier alternatives are in [decision history](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md)
741
- ([#267](https://github.com/zackbart/connecta/issues/267),
742
- [#270](https://github.com/zackbart/connecta/issues/270)).
743
740
 
744
741
  **M1.** `connecta.emit(result)` accepts a rich result from this program's
745
742
  `connecta.call` and forwards its original native content blocks, including
@@ -861,7 +858,7 @@ Programs and direct calls use the same invocation deadline and stages. A call's
861
858
  `timeoutMs` wins, then `calls.defaultTimeoutMs`, then `execute.hostCallTimeoutMs`
862
859
  (default 15,000). A guest may request a longer individual call, but the sandbox
863
860
  and request ceilings still bound the whole run. Timeout details are agent-facing;
864
- INV-6 operator records retain only checked classifications and numeric facts.
861
+ operator records retain only checked classifications and numeric facts.
865
862
  A dispatched write timeout becomes `write_outcome_unknown` and retains its
866
863
  deadline details even when the program catches the failure. The final program
867
864
  error includes the first timeout in `details`; multiple write timeouts also
@@ -1018,7 +1015,7 @@ createConnecta({
1018
1015
  connectors, executor,
1019
1016
  readOnly: false, // required: false writes, true refuses every write call
1020
1017
  pools: {
1021
- reporting: { tools: ["posthog"], readOnly: true, grant },
1018
+ reporting: { tools: ["posthog"], readOnly: true, members: analysts },
1022
1019
  },
1023
1020
  classification: {
1024
1021
  posthog: { exec: "read" }, // only for a deployment with a read-only exec contract
@@ -1028,7 +1025,7 @@ createConnecta({
1028
1025
  ```
1029
1026
 
1030
1027
  The removed `trust` option, on the deployment or a pool, fails construction
1031
- with a message naming `readOnly` (`INV-11`).
1028
+ with a message naming `readOnly`.
1032
1029
 
1033
1030
  The registry computes one `classifyTool` verdict on each request-scoped
1034
1031
  published catalog entry. It caches and persists only raw downstream facts.
@@ -1194,7 +1191,13 @@ brand check and reads only own string data descriptors for diagnostics; guest
1194
1191
  accessors, serialization hooks, and Proxy traps do not run. Other thrown objects
1195
1192
  receive a fixed description.
1196
1193
 
1197
- ## Verification
1194
+ ## Contributor reference
1195
+
1196
+ ### Verification
1197
+
1198
+ The earlier alternatives are in [decision history](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md)
1199
+ ([#267](https://github.com/zackbart/connecta/issues/267),
1200
+ [#270](https://github.com/zackbart/connecta/issues/270)).
1198
1201
 
1199
1202
  `test/guest-contract-cases.ts` runs on QuickJS in `test/guest-api-contract-quickjs.node.test.ts`
1200
1203
  and on the Worker adapter with a real Miniflare Loader in `test/guest-api-contract.test.ts`.
@@ -27,20 +27,25 @@ path and pins the generated project's Connecta dependency to the CLI version:
27
27
  npx @zackbart/connecta init my-connecta
28
28
  cd my-connecta
29
29
  npm install
30
- npm run --silent provision-token -- "local-machine"
30
+ npm run --silent rotate-token -- local-machine
31
31
  # Save the returned cta_ token privately. It is shown only once.
32
32
  npm start
33
33
  ```
34
34
 
35
- The client connects to `http://localhost:8787/mcp` with
36
- `Authorization: Bearer <returned-cta-token>`. The generated project always
37
- installs `accessTokens(storage)`. An empty database admits no MCP caller even
38
- though `/health` works. `CONNECTA_TOKEN` is only a client/doctor variable;
35
+ The agent connects to its pool's agent endpoint,
36
+ `http://localhost:8787/agent/local-machine`, with
37
+ `Authorization: Bearer <returned-cta-token>`. People use `/mcp` and
38
+ `/mcp/<pool>`, which refuse `cta_` tokens. Machine clients are the agents
39
+ declared in `agents` in `src/connecta.config.ts`; the generated project declares
40
+ `local-machine` and always installs `accessTokens(storage)`. An agent with no
41
+ issued token admits no MCP caller even though `/health` works. Rotating with the
42
+ same command replaces the token immediately. `CONNECTA_TOKEN` is only a client/doctor variable;
39
43
  it never configures server authentication. The local commands read the process
40
44
  environment and do not load `.env`; Docker Compose does.
41
45
 
42
- Edit `src/connecta.config.ts` for connectors, public URL, auth, identity rules,
43
- and optional modules. `src/index.ts` only constructs and starts the deployment.
46
+ Edit `src/connecta.config.ts` for connectors, public URL, auth, maintainers,
47
+ agents, pools, and optional modules. Lay out each agent's pools by
48
+ [Organizing an agent's pools](./auth.md#organizing-an-agents-pools). `src/index.ts` only constructs and starts the deployment.
44
49
  Follow the [template README](../templates/node/README.md) for storage paths,
45
50
  backups, Docker provisioning, secrets, and optional modules. Run the deployment's
46
51
  `npm run typecheck` after configuration changes.
@@ -56,12 +61,74 @@ must be installed for the Worker executor.
56
61
 
57
62
  Attach Cloudflare Access to the Worker destination. Interactive MCP clients
58
63
  need Managed OAuth, Dynamic Client Registration, and the documented redirect
59
- allowlist. Machines need Access service headers at the edge plus a stored
60
- `cta_` bearer inside Connecta; service identity alone is refused. Keep
64
+ allowlist. Machines are declared agents: they use `/agent/<pool>` with the
65
+ agent's `cta_` bearer as their only credential, once the
66
+ [agent bypass application](#agent-endpoints-behind-cloudflare-access) exists;
67
+ Access service identity alone is refused. Keep
61
68
  `enable_request_signal`, construct Connecta once per isolate, and pass `ctx`
62
69
  through to `fetch`. The example owns those details and
63
70
  [the machine-token procedure](../examples/worker/README.md#machine-tokens).
64
71
 
72
+ ## Agent endpoints behind Cloudflare Access
73
+
74
+ Agents use `/agent/<pool>`, which connecta authenticates with the `cta_` token
75
+ alone and which ignores Access context entirely
76
+ ([agent endpoints](./auth.md#agent-endpoints)). Behind Access, the edge must
77
+ let those requests through without an Access login or service token. The P0
78
+ probe ([observations](./architecture.md#production-access-bypass-for-agent-endpoints))
79
+ verified this shape on 2026-10-15:
80
+
81
+ 1. Keep the Worker-level application, whose destination is
82
+ `{ "type": "worker", "worker_id": "<the Worker script tag>" }`, exactly as
83
+ [Cloudflare Access on Workers](./auth.md#cloudflare-access-on-workers)
84
+ describes. It still protects every other path and supplies `ctx.access` to
85
+ human routes.
86
+ 2. For each hostname agents use (the `workers.dev` hostname, each Custom
87
+ Domain, each route host), add one more **self-hosted** Access application.
88
+ In the dashboard, set the domain to that hostname and the path to
89
+ `agent/*`. Give it a single policy whose action is **Bypass** and whose
90
+ include rule is **Everyone**. Through the API:
91
+
92
+ ```json
93
+ {
94
+ "type": "self_hosted",
95
+ "name": "connecta agents",
96
+ "destinations": [{ "type": "public", "uri": "connecta.example.com/agent/*" }],
97
+ "policies": [{ "name": "agents bypass", "decision": "bypass", "include": [{ "everyone": {} }] }]
98
+ }
99
+ ```
100
+
101
+ The more specific hostname-and-path application takes precedence over the
102
+ Worker-level one on matching paths only. Do not use a Worker destination
103
+ for it: a Worker destination belongs to one application, and a second one
104
+ is refused with 409 `destination belongs to another application`. For the
105
+ same reason a second hostname without Access is not an alternative.
106
+
107
+ 3. Point each agent at `https://<host>/agent/<pool>` with
108
+ `Authorization: Bearer cta_…` and drop its `CF-Access-Client-Id` and
109
+ `CF-Access-Client-Secret` headers. The pool must list `"agent:<name>"`.
110
+ 4. Verify after the change has settled. Access changes took 15–19 seconds to
111
+ settle in the probe, and while they propagate the edge can answer with
112
+ transient 500 (`error code: 1104`) or 404 (`error code: 1042` or `1043`)
113
+ responses, and in one round a 403 on a bypassed path after more than 30
114
+ seconds. Do not rely on a fixed wait: repeat these checks until they agree
115
+ for several consecutive rounds. Without credentials, `/agent/<pool>`
116
+ reaches connecta (its 401 carries `WWW-Authenticate: Bearer`, not Access's
117
+ 403 page), while `/mcp` and `/` still get Access's refusal. Then run
118
+ `connecta doctor --pool <pool>` with the agent's token.
119
+
120
+ The bypass makes `/agent/*` reachable from the internet without Access.
121
+ Connecta answers there only an issued token of an agent listed in the pool,
122
+ and gives every other spelling of the agent namespace a 404 before auth,
123
+ because Access matches `/agent/*` after resolving dot segments, folding case,
124
+ decoding unreserved escapes, and collapsing a leading `//`, while the Worker
125
+ sees the raw path. Two cases were not tested: a human's `CF_Authorization`
126
+ cookie on a bypassed path, whose `ctx.access` identity is unknown (connecta
127
+ never reads it at `/agent/*`), and Custom Domains, which use the same
128
+ documented hostname-and-path form but were not observed; only `workers.dev`
129
+ was probed. Pinning the Worker-level application's AUD on human routes is
130
+ recommended hardening that connecta does not yet do.
131
+
65
132
  ## Compose capabilities and access
66
133
 
67
134
  Both configurations use `defineConfig((env) => …)`, a required executor, and a required `readOnly` choice.
@@ -90,9 +157,13 @@ outside the database and source control, and preserve it across upgrades.
90
157
  existing-record migration.
91
158
 
92
159
  One deployment serves one tenant and may admit several people. Node uses Clerk
93
- for human auth; Access is Workers-only. Keep credential administration,
94
- personal connection, token management, and activity access explicit.
95
- [Auth](./auth.md#principals-visibility-and-operators) owns identity and grants;
160
+ for human auth; Access is Workers-only. With inbound auth, `maintainers` is
161
+ required: only they reach plain `/mcp` and the operator UI. Everyone else
162
+ reaches a pool that lists them in `members`: people at `/mcp/<pool>`, agents
163
+ at `/agent/<pool>`. Maintainers alone administer shared credentials, manage
164
+ agent tokens, and read activity; members connect their own accounts on
165
+ personal connectors in their pools.
166
+ [Auth](./auth.md#principals-visibility-and-operators) owns maintainers and members;
96
167
  [Clerk setup](./auth.md#clerk-oauth-tokens-and-operator-sessions) owns resource-bound
97
168
  tokens and `aud_claim_enabled`. A person's operator session is not an MCP token.
98
169
 
@@ -102,44 +173,70 @@ inherits that value unless it sets its own. A writable endpoint (`false`) allows
102
173
  writes through `call_destructive_tool` and programs and marks `execute_code` as
103
174
  a write; the client decides what to approve. A read-only endpoint (`true`) omits
104
175
  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`.
176
+ guides may still name those tools. A pool serves exactly its `tools` to its
177
+ `members`. See [pools](./auth.md#pools) before changing `readOnly`.
108
178
 
109
179
  ## Verify and diagnose
110
180
 
111
- With the deployment running, replace the placeholder with its saved client
112
- token and run doctor:
181
+ With the deployment running, replace the placeholder with its saved agent
182
+ token and run doctor against a pool that lists the agent:
183
+
184
+ ```sh
185
+ CONNECTA_TOKEN='<stored-cta-token>' npx connecta doctor --url http://localhost:8787 --pool local-machine
186
+ ```
187
+
188
+ Doctor checks health, the required executor, and the current six-tool set. A
189
+ `cta_` token is checked at `/agent/<pool>` and needs `--pool`.
190
+ For a Worker protected by Access, `/health` still needs edge admission even
191
+ though `/agent/*` bypasses Access. Use an Access service token admitted by the
192
+ Worker-level policy for that check, alongside the agent token:
113
193
 
114
194
  ```sh
115
- CONNECTA_TOKEN='<stored-cta-token>' npx connecta doctor --url http://localhost:8787
195
+ CF_ACCESS_CLIENT_ID='<access-service-id>' CF_ACCESS_CLIENT_SECRET='<access-service-secret>' CONNECTA_TOKEN='<stored-cta-token>' npx connecta doctor --url https://connecta.example.com --pool assistant
116
196
  ```
117
197
 
118
- Doctor checks health, the required executor, and the current six-tool set.
119
- For a Worker protected by Access, follow the example's doctor credentials
120
- and service-header setup. If the operator UI is enabled, authenticated
198
+ The service headers admit the health probe at the edge; only the `cta_` token
199
+ authenticates the agent endpoint inside Connecta. If the operator UI is enabled, maintainer-authenticated
121
200
  `connecta doctor --config --url <public-url>` prints the caller-scoped
122
201
  configuration snapshot as JSON. It runs no diagnostic program and prints no
123
202
  raw HTTP failure body. [Operator UI](./operator-ui.md) describes the returned
124
- configuration, live facts, and permission rules.
203
+ configuration, live facts, and permission rules. The config route is a human
204
+ endpoint: an agent token cannot authenticate it.
125
205
 
126
206
  Health success alone does not prove a client can authenticate or reach an
127
207
  integration. Check client connection and a bounded read through a chosen
128
208
  connector; resolve credentials using [authorization recovery](./meta-tools.md#authorization-recovery).
129
209
 
210
+ Use the failed check to choose the next step:
211
+
212
+ | Symptom | Action |
213
+ | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
214
+ | Connection refused or timeout | Check the public URL, listening process/port, proxy, and Worker deployment. |
215
+ | `/health` fails behind Access | Check the Worker-level Access policy and the two service headers above. Do not broaden the agent bypass. |
216
+ | Executor check fails | Retain `quickJsExecutor()` and its peer on Node, or `workerExecutor({ loader })`, the codemode peer, and `LOADER` on Workers. |
217
+ | MCP 401 | Check the saved token, declared agent, and slot state; rotated/revoked tokens refuse. Use `/agent/<pool>` with `--pool`. |
218
+ | MCP 404 | Check the pool name and `members: ["agent:assistant"]`; an existing but inaccessible pool is deliberately indistinguishable from a missing one. |
219
+ | Doctor passes but a connector fails | Discover its tools in that pool, check credential status/account ownership, and perform a bounded read. Doctor does not prove downstream access. |
220
+
221
+ After a fix, repeat doctor with the same client credentials and pool. Never
222
+ paste a token or raw downstream failure body into a committed diagnostic log.
223
+
130
224
  ## Upgrade an existing deployment
131
225
 
132
226
  Bump the exact package pin, read the [changelog](../CHANGELOG.md) for every
133
227
  intervening release, and apply the relevant migration before startup. `init`
134
228
  does not merge changes into an existing deployment.
135
229
 
136
- - [Managed tokens from v0.23](./auth.md#managed-client-tokens-and-upgrading-from-v023) keep their secrets when storage, namespaces, and grants are retained. Replace the old boolean config with `accessTokens(storage)`; machine tokens gain no operator authority.
230
+ - [Migration to 0.33](./migration-0.33.md) maps every removed access option to `maintainers`, pool `members`, and `agents`, moves agents to `/agent/<pool>`, and covers pool owners, token revisions, operator contract v2 with its operator route changes, and the pre-auth listen fix.
231
+ - [Tokens issued before agents](./auth.md#agent-tokens-and-upgrading-from-earlier-tokens) stop admitting until each machine's token is bound once to a declared agent with `npm run adopt-token -- <token-id> <agent>` (Node) or `AccessTokenManager.adopt`, which keeps its secret. Human-bound tokens stay refused; issue those agents new tokens. Pool members name `"agent:<name>"` instead of matching token record ids.
137
232
  - [Node storage from 0.28](../templates/node/README.md#storage) uses `connecta migrate-state` to copy the JSON state into SQLite. Activity JSONL is not imported.
138
233
  - [Worker storage from 0.28](../examples/worker/README.md#upgrading-from-028) copies KV into D1 with a one-shot migration.
139
234
  - [Notion, Vercel, and Cloudflare for 0.29](./provider-migration-0.29.md) change defaults and retained API tool names.
140
235
  - [Executor wiring](./code-mode.md#deploy-time-capability) requires branded shipped executors or a self-managed custom executor.
141
236
 
142
- ## Implementation and evidence
237
+ ## Contributor reference
238
+
239
+ ### Implementation and evidence
143
240
 
144
241
  Check the [CLI](https://github.com/zackbart/connecta/blob/main/bin/connecta.mjs),
145
242
  [Node configuration](../templates/node/src/connecta.config.ts), and
@@ -1,16 +1,62 @@
1
1
  # Integrating services
2
2
 
3
- Use this guide to add a connector to deployment configuration. To implement a
4
- new maintained provider in Connecta itself, follow the repository's
5
- [add-provider skill](https://github.com/zackbart/connecta/blob/main/.claude/skills/add-provider/SKILL.md)
6
- and [AGENTS.md](https://github.com/zackbart/connecta/blob/main/AGENTS.md).
3
+ Use this guide to change connectors in deployment configuration.
7
4
  [All agent routes](./README.md) are in the index.
8
5
 
6
+ ## Add or remove a connector
7
+
8
+ Connectors are code-owned. Edit the deployment's `src/connecta.config.ts`;
9
+ there is no install button or runtime registration API.
10
+
11
+ To add one:
12
+
13
+ 1. Choose a maintained provider, `remoteMcp()`, or `api()` using the sections
14
+ below. Give it a unique, stable id such as `crm`; renaming an id selects
15
+ different stored credentials and changes every tool address.
16
+ 2. Put the constructed connector in `connectors`. Keep secrets in environment
17
+ variables or the encrypted vault. Choose shared or personal auth explicitly
18
+ for the intended audience; agents cannot see personal-auth connectors.
19
+ 3. Add the id (all its tools) or exact `id.tool` addresses to the intended
20
+ pools' `tools`. Maintainers at plain `/mcp` already see the whole catalog.
21
+ Follow [the agent pool layout](./auth.md#organizing-an-agents-pools) for an
22
+ agent: shared connectors go in its base constant, per-user connectors only
23
+ in that user's pool.
24
+ 4. Run the deployment's `npm run typecheck` (Worker copies: `tsc --noEmit`),
25
+ restart Node or redeploy the Worker, and
26
+ [connect its credential](./auth.md#connect-a-shared-credential) if needed.
27
+ Run [doctor](./deploying.md#verify-and-diagnose), discover its tools from
28
+ the intended pool, and perform a bounded read. Check an excluded pool too.
29
+
30
+ For example, merge this fragment into the existing configuration, retaining
31
+ its storage, executor, auth, and `readOnly` settings:
32
+
33
+ ```ts
34
+ import { remoteMcp } from "@zackbart/connecta";
35
+
36
+ const crm = remoteMcp("crm", {
37
+ url: "https://crm.example.com/mcp",
38
+ authScope: "shared",
39
+ auth: { type: "credential", credential: { label: "CRM API token" } },
40
+ });
41
+ const assistant = { tools: ["crm", "tickets"], members: ["agent:assistant"] };
42
+ // connectors: [crm, tickets], agents: ["assistant"], pools: { assistant }
43
+ ```
44
+
45
+ To remove `crm`, first disconnect its OAuth grant or delete its saved
46
+ credential while the connector still exists, using a signed-in maintainer
47
+ for shared auth. Each person disconnects their own personal account. Remove
48
+ `crm` and all `crm.*` addresses from every pool (including base constants),
49
+ then remove its entry from `connectors`, unused imports, and unused secret
50
+ configuration. Remove any pools left with no tools (empty `tools` fails
51
+ construction), and update or retire clients pointing at those pools. Typecheck, restart/redeploy, run doctor, and confirm discovery
52
+ and calls no longer find it. Removing configuration alone does not revoke a
53
+ credential at the downstream service; revoke unused credentials there too.
54
+
9
55
  ## Choose a connector
10
56
 
11
57
  | Need | Configuration path | Check before using |
12
58
  | -------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------- |
13
- | A maintained integration | Its `@zackbart/connecta/providers/<name>` factory | Provider `SKILL.md`, options, auth ownership, supported tools |
59
+ | A maintained integration | Its `@zackbart/connecta/providers/<name>` factory | Published factory declarations, auth ownership, supported tools |
14
60
  | Another hosted remote MCP server | `remoteMcp()` from the root package | Endpoint, authentication, current catalog, and read/write annotations |
15
61
  | A selected HTTP API capability | `api()` from the root package | Input/output schemas, explicit read/write hints, bounded handler, credential handling |
16
62
 
@@ -19,6 +65,14 @@ registry enforcement, caller grants, result bounds, and activity paths. There
19
65
  is no runtime installation or registration. Keep connector IDs unique and
20
66
  stable because they prefix canonical `<connectorId>.<toolName>` addresses.
21
67
 
68
+ `callAdmission` is optional on `remoteMcp()`, `api()`, custom connectors, and
69
+ provider variants that expose an override. Only omission or `undefined` uses
70
+ the connector's documented default, which may be no admission. Every defined
71
+ value is validated at construction. Malformed policies, including `false`,
72
+ `null`, `0`, and `""`, throw before serving requests; they never disable
73
+ admission or select a default. Custom connector policies are validated when
74
+ the deployment constructs its registry.
75
+
22
76
  ## Maintained integrations
23
77
 
24
78
  Import a provider individually. For example, Linear requires an explicit
@@ -35,25 +89,26 @@ const tracker = linear("tracker", {
35
89
  ```
36
90
 
37
91
  Linear defaults to OAuth; its read-only endpoint requests only the downstream
38
- read scope. That is separate from a Connecta `readOnly` endpoint. Read the
39
- [Linear skill](https://github.com/zackbart/connecta/blob/main/src/providers/linear/SKILL.md)
40
- and [factory options](https://github.com/zackbart/connecta/blob/main/src/providers/linear/index.ts)
41
- for API-key framing, workspace instructions, and admission choices.
92
+ read scope. That is separate from a Connecta `readOnly` endpoint. Read the installed `dist/providers/linear/index.d.ts` for API-key framing,
93
+ workspace instructions, and admission choices.
42
94
 
43
95
  The [human README](../README.md#what-you-can-do-with-it) lists available
44
- integrations. Each [provider folder](https://github.com/zackbart/connecta/tree/main/src/providers)
45
- contains its scoped guide, options, tests, fixtures, and review data. Prefer
46
- that guide to general assumptions about a service's API. Google Workspace
96
+ integrations. In the installed package, `dist/providers/<name>/index.d.ts`
97
+ and its imported declarations describe each factory's options. After adding
98
+ a connector, use `search_tools` to discover its current tools and `connecta.skill("connector:<id>")` in `execute_code`
99
+ to read its runtime guide; provider source skills are not installed files.
100
+ Google Workspace
47
101
  providers require administrator-managed domain-wide delegation and a deployment
48
- mapping from admitted identity to the downstream account; a request must not
49
- choose its own account. Gmail never sends mail, and Drive never permanently
102
+ mapping from admitted identity to the downstream account; an agent acts as its
103
+ pool's `owner` instead ([auth guide](auth.md#pool-owners-for-google-workspace)).
104
+ A request must not choose its own account. Gmail never sends mail, and Drive never permanently
50
105
  deletes files.
51
106
 
52
107
  Wispr Flow uses OAuth only through `wisprFlow()` from
53
108
  `@zackbart/connecta/providers/wispr-flow`. It reaches the authorized account's
54
109
  Notetaker content, with personal and work accounts holding separate data.
55
- Its [provider guide](https://github.com/zackbart/connecta/blob/main/src/providers/wispr-flow/SKILL.md)
56
- owns account access, search limits, transcript permissions and paging. The
110
+ Its runtime guide, available through `connecta.skill("connector:<id>")` in `execute_code`, describes account access,
111
+ search limits, transcript permissions and paging. The
57
112
  reviewed classification includes task writes listed in Claude's directory even
58
113
  though Wispr's public help describes read-only access; discover the account's
59
114
  actual tools before assuming those writes exist.
@@ -75,6 +130,16 @@ MCP clients to its hosted server, an operational gate outside Connecta. See the
75
130
  To use both implementations of one provider, configure two connector ids with
76
131
  independent credentials.
77
132
 
133
+ Both Notion auth variants accept optional `callAdmission`. For a token
134
+ connector it replaces the whole per-runtime default of 180 calls per minute
135
+ and 3 concurrent. Notion's [request limits](https://developers.notion.com/reference/request-limits)
136
+ allow 600 requests per minute per connection on Business and Enterprise and
137
+ 180 on other plans. A separate workspace-wide limit is shared by all
138
+ connections. Size an override for other runtime traffic and tools that make
139
+ multiple HTTP requests. Only an omitted or `undefined` policy selects the
140
+ default; malformed policies, including `false`, `null`, `0`, and `""`, throw
141
+ at construction on both auth variants.
142
+
78
143
  Planning Center's `excludeApps` removes whole products from a connection, for
79
144
  example `planningCenter("pco", { purpose, excludeApps: ["giving"] })` for a
80
145
  deployment that keeps agents away from donor and financial data. Excluded
@@ -130,8 +195,8 @@ const service = remoteMcp("service", {
130
195
  description: "Our team's hosted service",
131
196
  auth: { type: "credential" },
132
197
  });
133
- // Add service to connectors. Configure the vault and permitted operators
134
- // before supplying its credential through the operator UI.
198
+ // Add service to connectors. Configure the vault; a signed-in maintainer
199
+ // supplies its credential through the operator UI.
135
200
  ```
136
201
 
137
202
  Credential auth defaults to an `Authorization: Bearer` header. Choose framing
@@ -198,10 +263,10 @@ schemas when known; runtime observations cannot replace them.
198
263
 
199
264
  `authScope: "shared"` uses deployment-owned downstream auth; `"personal"` uses
200
265
  the admitted principal's partition. Visibility is a different decision,
201
- controlled by identity and pool grants. A personal connector must not close
266
+ controlled by maintainers and pool membership. A personal connector must not close
202
267
  over a shared secret or derive its owner from arguments. Remote MCP refuses
203
268
  literal header credentials with personal scope. Review
204
- [principals and grants](./auth.md#principals-visibility-and-operators) and
269
+ [maintainers and members](./auth.md#principals-visibility-and-operators) and
205
270
  [management permissions](./auth.md#management-permissions) before exposing a
206
271
  new integration or its authentication controls.
207
272
 
@@ -210,7 +275,13 @@ perform a bounded read. Add negative checks for target restrictions and writes
210
275
  when authoring a handler or provider. Follow repository verification policy
211
276
  when changing package code.
212
277
 
213
- ## Implementation and evidence
278
+ ## Contributor reference
279
+
280
+ ### Implementation and evidence
281
+
282
+ To implement a maintained provider in Connecta itself, follow the repository's
283
+ [add-provider skill](https://github.com/zackbart/connecta/blob/main/.claude/skills/add-provider/SKILL.md)
284
+ and [AGENTS.md](https://github.com/zackbart/connecta/blob/main/AGENTS.md).
214
285
 
215
286
  Factory contracts live in [remote MCP](https://github.com/zackbart/connecta/blob/main/src/connectors/remote-mcp.ts),
216
287
  [API connector](https://github.com/zackbart/connecta/blob/main/src/connectors/api-connector.ts),
@@ -194,8 +194,7 @@ discovery made the same bet).
194
194
  ```
195
195
 
196
196
  It is something to read, not something that runs: programs stay JavaScript,
197
- and `execute_code` accepts no erasable TypeScript
198
- ([decision history](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md)). The output half is the provider's declared
197
+ and `execute_code` accepts no erasable TypeScript. The output half is the provider's declared
199
198
  schema, else an `S9` observation that opens with the
200
199
  `/* observed, not declared */` marker and keeps the `outputSchemaSource`
201
200
  field, else `unknown` — runtime evidence never reads as a contract, even when
@@ -282,7 +281,7 @@ is its slug; other IDs use `connector-` followed by a stable SHA-256 prefix.
282
281
  The suffix makes each
283
282
  document a valid Agent Skills file; the roots `skill://connecta/usage` and
284
283
  `skill://connecta/connectors/<connector-id>` also resolve. Provider guides are
285
- built from the generated `src/providers/*/SKILL.md` content with the existing
284
+ built from maintained provider guides with the existing
286
285
  connection context and deployment instructions. Local manifests contain a
287
286
  SHA-256 digest and UTF-8 byte size of the complete document. `usage` and
288
287
  `investigate` remain compatibility names; `connector:<id>` remains a
@@ -598,8 +597,8 @@ same when no visible connector ID or title names that service. This service-name
598
597
  vocabulary is query interpretation, not an inventory of configured providers.
599
598
  For other service names, use the explicit `connector` scope. An unavailable
600
599
  catalog is a catalog failure, never an absence. Absence records and catalog
601
- errors use only the endpoint/pool and grant intersection; a connector hidden by
602
- that intersection is indistinguishable from an unconfigured one.
600
+ errors use only the endpoint's tools; a connector the endpoint does not serve
601
+ is indistinguishable from an unconfigured one.
603
602
 
604
603
  If no tool covers every search term, wider any-term fallback remains available
605
604
  for ordinary action/object searches and the page carries `matchMode: "partial"`.
@@ -933,9 +932,13 @@ list connectors from another pool or identity's grants.
933
932
  A write that times out after dispatch returns `write_outcome_unknown` with
934
933
  `retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted?, recovery? }`.
935
934
  No write is automatically replayed. `recovery` carries facts the connector
936
- recorded before dispatch (`src/call-recovery.ts`), such as the `idempotencyKey`
937
- a REST connector generated; a deliberate retry that reuses it with the exact
938
- original arguments cannot repeat the write while the vendor retains the key
935
+ recorded before dispatch, such as a REST connector's
936
+ generated or caller-supplied `idempotencyKey`. Recovery accepts keys up to 255
937
+ characters, matching the REST write schema. Other recovery values remain capped
938
+ at 128 characters, with at most four facts per call and camelCase names of at
939
+ most 32 characters. Values outside these bounds are dropped whole. A deliberate
940
+ retry that reuses the key with the exact original arguments cannot repeat the
941
+ write while the vendor retains the key
939
942
  (Stripe may prune v1 keys after 24 hours). After that, look the object up before
940
943
  retrying. When a connector recorded such facts, any other dispatched write whose
941
944
  outcome is unknown (a lost response body, a 5xx) also carries `uncertainCall`
@@ -954,7 +957,9 @@ SDK timeout classes, and runtime network codes. Untyped prose is non-retryable:
954
957
  name, or URL never changes the verdict. JSON-RPC `ProtocolError.data` is dropped
955
958
  at the downstream boundary, even when the agent may see the error's message.
956
959
 
957
- ## Source and tests
960
+ ## Contributor reference
961
+
962
+ ### Source and tests
958
963
 
959
964
  Handlers and schemas live in [meta-tools.ts](https://github.com/zackbart/connecta/blob/main/src/meta-tools.ts).
960
965
  [Catalog service](https://github.com/zackbart/connecta/blob/main/src/catalog-service.ts)