@zackbart/connecta 0.32.0 → 0.34.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 (158) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +95 -116
  3. package/SECURITY.md +1 -1
  4. package/bin/connecta.mjs +24 -6
  5. package/bin/version.mjs +1 -1
  6. package/dist/access-tokens.d.ts +167 -23
  7. package/dist/access-tokens.js +676 -140
  8. package/dist/activity.d.ts +3 -0
  9. package/dist/agent-identity.js +47 -0
  10. package/dist/auth/cloudflare-access.js +13 -5
  11. package/dist/branding.d.ts +7 -0
  12. package/dist/branding.js +76 -9
  13. package/dist/call-admission-context.js +15 -0
  14. package/dist/call-admission.js +30 -43
  15. package/dist/call-recovery.js +6 -2
  16. package/dist/catalog-service.js +15 -1
  17. package/dist/config-value-sources.js +16 -9
  18. package/dist/config.d.ts +48 -75
  19. package/dist/config.js +144 -36
  20. package/dist/connector-access.d.ts +4 -34
  21. package/dist/connector-access.js +15 -99
  22. package/dist/connector-caller.d.ts +7 -1
  23. package/dist/connector-caller.js +4 -2
  24. package/dist/connectors/api-connector.d.ts +27 -4
  25. package/dist/connectors/api-connector.js +2 -0
  26. package/dist/connectors/remote-mcp.js +2 -0
  27. package/dist/describe-config.d.ts +31 -7
  28. package/dist/describe-config.js +8 -17
  29. package/dist/execute.js +1 -11
  30. package/dist/fix-prompt.js +2 -2
  31. package/dist/index.d.ts +4 -3
  32. package/dist/index.js +30 -10
  33. package/dist/invocation.js +5 -3
  34. package/dist/membership.d.ts +53 -0
  35. package/dist/membership.js +121 -0
  36. package/dist/module-contracts.d.ts +23 -6
  37. package/dist/oauth-callback-outcome.js +2 -2
  38. package/dist/operator-record.d.ts +2 -2
  39. package/dist/operator-record.js +2 -4
  40. package/dist/operator-ui/agents.d.ts +74 -0
  41. package/dist/operator-ui/agents.js +104 -0
  42. package/dist/operator-ui/config-summary.js +562 -0
  43. package/dist/operator-ui/connection-map.js +375 -0
  44. package/dist/operator-ui/contract.d.ts +149 -28
  45. package/dist/operator-ui/endpoints.js +221 -0
  46. package/dist/operator-ui/fix-prompts.js +5 -5
  47. package/dist/operator-ui/generated.js +5 -4
  48. package/dist/operator-ui/member-page.js +97 -0
  49. package/dist/operator-ui/model.d.ts +14 -6
  50. package/dist/operator-ui/overview.js +134 -0
  51. package/dist/operator-ui/view.d.ts +107 -37
  52. package/dist/operator-ui/view.js +163 -86
  53. package/dist/page-styles.js +5 -3
  54. package/dist/provider.d.ts +61 -8
  55. package/dist/provider.js +46 -7
  56. package/dist/providers/_shared/google/workspace.d.ts +56 -17
  57. package/dist/providers/_shared/google/workspace.js +34 -8
  58. package/dist/providers/_shared/rest/tools.js +2 -2
  59. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  60. package/dist/providers/_shared/rest/value-safety.js +699 -7
  61. package/dist/providers/ccb/index.js +2 -2
  62. package/dist/providers/cloudflare/index.js +3 -3
  63. package/dist/providers/cloudflare/rest.js +25 -3
  64. package/dist/providers/cloudflare/skill.generated.js +1 -1
  65. package/dist/providers/cloudflare/value-safety.js +32 -0
  66. package/dist/providers/docs/index.js +4 -4
  67. package/dist/providers/docs/skill.generated.js +2 -2
  68. package/dist/providers/drive/index.js +4 -4
  69. package/dist/providers/drive/skill.generated.js +2 -2
  70. package/dist/providers/forms/index.js +4 -4
  71. package/dist/providers/forms/skill.generated.js +2 -2
  72. package/dist/providers/gmail/index.js +11 -9
  73. package/dist/providers/gmail/skill.generated.js +2 -2
  74. package/dist/providers/mixpanel/index.js +1 -1
  75. package/dist/providers/mixpanel/skill.generated.js +3 -3
  76. package/dist/providers/notion/index.d.ts +7 -0
  77. package/dist/providers/notion/index.js +100 -11
  78. package/dist/providers/notion/rest.js +45 -5
  79. package/dist/providers/notion/skill.generated.js +2 -2
  80. package/dist/providers/notion/value-safety.js +30 -5
  81. package/dist/providers/overflow/index.js +2 -2
  82. package/dist/providers/overflow/skill.generated.js +2 -2
  83. package/dist/providers/planning-center/index.js +2 -2
  84. package/dist/providers/planning-center/skill.generated.js +4 -4
  85. package/dist/providers/revenuecat/skill.generated.js +2 -2
  86. package/dist/providers/sheets/index.d.ts +3 -0
  87. package/dist/providers/sheets/index.js +7 -4
  88. package/dist/providers/sheets/skill.generated.js +2 -2
  89. package/dist/providers/slides/index.js +4 -4
  90. package/dist/providers/slides/skill.generated.js +2 -2
  91. package/dist/providers/stripe/rest.js +9 -2
  92. package/dist/providers/stripe/skill.generated.js +1 -1
  93. package/dist/providers/stripe/value-safety.js +22 -0
  94. package/dist/providers/tithely/index.js +1 -1
  95. package/dist/providers/vercel/index.js +1 -1
  96. package/dist/providers/vercel/rest.js +20 -2
  97. package/dist/providers/vercel/skill.generated.js +2 -2
  98. package/dist/providers/vercel/value-safety.js +22 -0
  99. package/dist/registry.d.ts +2 -3
  100. package/dist/registry.js +23 -9
  101. package/dist/routes/access-tokens.js +82 -75
  102. package/dist/routes/activity-disclosure.js +17 -18
  103. package/dist/routes/activity.js +34 -54
  104. package/dist/routes/actor-labels.js +33 -4
  105. package/dist/routes/agent-path.js +43 -0
  106. package/dist/routes/connect.js +16 -12
  107. package/dist/routes/credentials.js +16 -40
  108. package/dist/routes/mcp.js +161 -104
  109. package/dist/routes/oauth-management.js +11 -5
  110. package/dist/routes/oauth.js +5 -11
  111. package/dist/routes/operator.js +24 -23
  112. package/dist/routes/shared.d.ts +60 -20
  113. package/dist/routes/shared.js +187 -96
  114. package/dist/routes/ui-config.js +437 -97
  115. package/dist/routes/ui-data.js +2 -2
  116. package/dist/routes/ui.js +9 -16
  117. package/dist/runtime/call-admission.js +9 -0
  118. package/dist/server.js +5 -0
  119. package/dist/skills.js +10 -0
  120. package/dist/storage/keys.js +9 -1
  121. package/dist/storage/sql.js +5 -1
  122. package/dist/types.d.ts +29 -7
  123. package/dist/ui.d.ts +1 -1
  124. package/dist/ui.js +23 -16
  125. package/dist/usage-guide.js +1 -1
  126. package/dist/version.d.ts +1 -1
  127. package/dist/version.js +1 -1
  128. package/dist/workspace-address.js +9 -0
  129. package/dist/workspace-subject.js +23 -0
  130. package/documentation/README.md +47 -23
  131. package/documentation/architecture.md +371 -61
  132. package/documentation/auth.md +897 -249
  133. package/documentation/code-mode.md +37 -16
  134. package/documentation/deploying.md +126 -25
  135. package/documentation/integrating.md +97 -23
  136. package/documentation/meta-tools.md +50 -31
  137. package/documentation/migration-0.33.md +352 -0
  138. package/documentation/migration-0.34.md +67 -0
  139. package/documentation/operating.md +3 -1
  140. package/documentation/operator-tests.md +147 -34
  141. package/documentation/operator-ui.md +519 -64
  142. package/documentation/provider-auth-migration.md +37 -23
  143. package/documentation/provider-migration-0.29.md +2 -2
  144. package/examples/worker/AGENTS.md +37 -9
  145. package/examples/worker/README.md +127 -61
  146. package/examples/worker/src/connecta.config.ts +40 -23
  147. package/examples/worker/wrangler.jsonc +4 -1
  148. package/package.json +19 -18
  149. package/templates/node/.env.example +3 -0
  150. package/templates/node/AGENTS.md +33 -6
  151. package/templates/node/Dockerfile +2 -2
  152. package/templates/node/README.md +80 -52
  153. package/templates/node/docker-compose.yml +3 -2
  154. package/templates/node/package.json +4 -3
  155. package/templates/node/src/adopt-token.ts +23 -0
  156. package/templates/node/src/connecta.config.ts +28 -12
  157. package/templates/node/src/rotate-token.ts +17 -0
  158. 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.
@@ -555,6 +555,15 @@ URL-elicitation hosts retain their existing flow.
555
555
  **E2.** The taxonomy: `retryable` is what connecta reports, `Y3` what a program
556
556
  may do. A provider maps each downstream failure to the code that tells the
557
557
  caller what to do next, and never invents a cause it was not told.
558
+ The table lists the codes a program commonly sees. Codes owned by one
559
+ mechanism are specified with it: `resource_template_ambiguous` and
560
+ `resource_match_budget_exceeded` with resource reads above; the
561
+ `executor_*` admission codes in `E5`; `destructive_tool_requires_approval` with
562
+ direct calls; and `input_required_invalid`, `input_required_limit`,
563
+ `downstream_oauth_required`, `oauth_revocation_failed`,
564
+ `provider_permission_denied`, and `auth_replay_refused` in
565
+ [meta-tools](./meta-tools.md). Each of those is non-retryable except
566
+ `executor_overloaded`.
558
567
 
559
568
  | Code | Raised when | `retryable` |
560
569
  | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
@@ -577,6 +586,7 @@ caller what to do next, and never invents a cause it was not told.
577
586
  | `result_too_large` | a discovery response exceeded its byte bound | false |
578
587
  | `budget_exceeded` | the run exhausted a host-call, write, or emitted-output budget | false |
579
588
  | `write_outcome_unknown` | a program write was sent and no answer came back; it is never sent again (`W9`) | false |
589
+ | `executor_failed` | the executor returned a malformed result or failed outside the guest program | false |
580
590
 
581
591
  **E3.** `auth_required` carries the same recovery envelope as `call_tool`:
582
592
  `connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
@@ -685,6 +695,12 @@ when the metadata describes a write on a read-only endpoint.
685
695
  Caught call failures retain that connector visibility when catalog loading or
686
696
  tool resolution fails. Recovery messages listing configured connectors or
687
697
  local guides retain visibility of those contributors too.
698
+ Search guidance retains its referenced connectors even when no tool matches,
699
+ including connectors named by id or title and identities summarized as an
700
+ additional count. Scoped guides and summaries, absence rosters, and catalog
701
+ errors retain their contributing connectors as well.
702
+ Search counts, ranking, and term analysis retain every searched connector,
703
+ including matches outside the returned page or beyond the requested offset.
688
704
  Each page rechecks current auth,
689
705
  grants, pool membership, and the endpoint's `readOnly`. Reading a stash inside a program carries
690
706
  its dependencies forward to any new return stash. The page cap is the smallest
@@ -711,7 +727,7 @@ has no host log stream and cannot supply logs from its suspended guest (`L4`).
711
727
 
712
728
  **R6.** Every run returns `hostCalls: { attempted, admitted, succeeded, failed }`,
713
729
  including admission failure, cancellation, budget refusal, and code-size refusal.
714
- Calls include search, describe, call, result, and skill. `emit` has separate budgets.
730
+ Calls include search, describe, call, read, result, and skill. `emit` has separate budgets.
715
731
  An abandoned host call counts as failed when the run ends; guest catches do not
716
732
  change the counts. These numbers are outside the program's returned `result`.
717
733
  `diagnostics: true` adds the existing payload-free timing block. Emitting adds
@@ -737,9 +753,6 @@ and nonzero pad bits are rejected before collection. Size budgets still apply.
737
753
  MCP-native output a return value cannot carry: base64 is not projectable, so a
738
754
  block that fits the inline call limit (`S5`, `L6`) can use the separate
739
755
  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
756
 
744
757
  **M1.** `connecta.emit(result)` accepts a rich result from this program's
745
758
  `connecta.call` and forwards its original native content blocks, including
@@ -773,7 +786,8 @@ a successful result, appended to `content` after the JSON envelope, which gains
773
786
  response (`R6`). `structuredContent` stays the envelope alone — emission is
774
787
  presentation, not a second data channel.
775
788
 
776
- **M3.** Text emission shares `R2`'s 24,000 serialized-character cap. Its
789
+ **M3.** Text emission shares `R2`'s 24,000 serialized-character cap, less 512
790
+ characters reserved for the envelope (23,488 for emitted text). Its
777
791
  serialized block size reduces the return budget, and oversized text fails with
778
792
  `result_too_large`. Image/audio blocks use the separate transport budget and
779
793
  reach the model as real MCP media content, including when `emit` is not awaited.
@@ -861,7 +875,7 @@ Programs and direct calls use the same invocation deadline and stages. A call's
861
875
  `timeoutMs` wins, then `calls.defaultTimeoutMs`, then `execute.hostCallTimeoutMs`
862
876
  (default 15,000). A guest may request a longer individual call, but the sandbox
863
877
  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.
878
+ operator records retain only checked classifications and numeric facts.
865
879
  A dispatched write timeout becomes `write_outcome_unknown` and retains its
866
880
  deadline details even when the program catches the failure. The final program
867
881
  error includes the first timeout in `details`; multiple write timeouts also
@@ -884,7 +898,7 @@ because connecta enforces them above the sandbox:
884
898
 
885
899
  | Bound | Value |
886
900
  | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
887
- | Host calls per execution, shared by `search`, `describe`, `call`, and `read` | 20 by default, `execute.maxHostCalls` |
901
+ | Host calls per execution, shared by every guest call except `emit` | 20 by default, `execute.maxHostCalls` |
888
902
  | Writes per run on a writable endpoint, on top of the host calls they also spend (`W10`) | 10 by default, `execute.maxWrites` |
889
903
  | Deadline per host call | 15 s, `execute.hostCallTimeoutMs`; one deadline covers catalog resolution, admission, and the connector call |
890
904
  | Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
@@ -894,8 +908,9 @@ because connecta enforces them above the sandbox:
894
908
  | Result | 24,000 serialized characters |
895
909
  | Logs presented to the model | 4,000 characters |
896
910
 
897
- Every `call`, `read`, `search`, and `describe` spends one host call on entry, before
898
- resolution, validation, or dispatch. Catching a local refusal refunds nothing.
911
+ Every `call`, `read`, `search`, `describe`, `result`, and `skill` spends one host
912
+ call on entry, before resolution, validation, or dispatch; automatic transfer
913
+ pages of a paged result do not. Catching a local refusal refunds nothing.
899
914
  The first call beyond the budget ends the host run with one non-retryable
900
915
  `budget_exceeded` (`E2`). Its bridge stays pending until lease disposal so guest
901
916
  `try/catch` cannot swallow it. Later host access, including `emit`, stops; pending
@@ -1018,7 +1033,7 @@ createConnecta({
1018
1033
  connectors, executor,
1019
1034
  readOnly: false, // required: false writes, true refuses every write call
1020
1035
  pools: {
1021
- reporting: { tools: ["posthog"], readOnly: true, grant },
1036
+ reporting: { tools: ["posthog"], readOnly: true, members: analysts },
1022
1037
  },
1023
1038
  classification: {
1024
1039
  posthog: { exec: "read" }, // only for a deployment with a read-only exec contract
@@ -1028,7 +1043,7 @@ createConnecta({
1028
1043
  ```
1029
1044
 
1030
1045
  The removed `trust` option, on the deployment or a pool, fails construction
1031
- with a message naming `readOnly` (`INV-11`).
1046
+ with a message naming `readOnly`.
1032
1047
 
1033
1048
  The registry computes one `classifyTool` verdict on each request-scoped
1034
1049
  published catalog entry. It caches and persists only raw downstream facts.
@@ -1194,7 +1209,13 @@ brand check and reads only own string data descriptors for diagnostics; guest
1194
1209
  accessors, serialization hooks, and Proxy traps do not run. Other thrown objects
1195
1210
  receive a fixed description.
1196
1211
 
1197
- ## Verification
1212
+ ## Contributor reference
1213
+
1214
+ ### Verification
1215
+
1216
+ The earlier alternatives are in [decision history](https://github.com/zackbart/connecta/blob/main/decisions/0001-ethos-verdict-table.md)
1217
+ ([#267](https://github.com/zackbart/connecta/issues/267),
1218
+ [#270](https://github.com/zackbart/connecta/issues/270)).
1198
1219
 
1199
1220
  `test/guest-contract-cases.ts` runs on QuickJS in `test/guest-api-contract-quickjs.node.test.ts`
1200
1221
  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,74 @@ 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
121
- `connecta doctor --config --url <public-url>` prints the caller-scoped
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
200
+ `connecta doctor --config --url <public-url>` (on Node, `CONNECTA_TOKEN` set to a
201
+ maintainer's current Clerk session token issued for this deployment's origin;
202
+ Clerk OAuth access tokens are refused here, and Access on Workers has no
203
+ non-browser maintainer credential, so otherwise use the UI's Settings page) prints the caller-scoped
122
204
  configuration snapshot as JSON. It runs no diagnostic program and prints no
123
205
  raw HTTP failure body. [Operator UI](./operator-ui.md) describes the returned
124
- configuration, live facts, and permission rules.
206
+ configuration, live facts, and permission rules. The config route is a human
207
+ endpoint: an agent token cannot authenticate it.
125
208
 
126
209
  Health success alone does not prove a client can authenticate or reach an
127
210
  integration. Check client connection and a bounded read through a chosen
128
211
  connector; resolve credentials using [authorization recovery](./meta-tools.md#authorization-recovery).
129
212
 
213
+ Use the failed check to choose the next step:
214
+
215
+ | Symptom | Action |
216
+ | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
217
+ | Connection refused or timeout | Check the public URL, listening process/port, proxy, and Worker deployment. |
218
+ | `/health` fails behind Access | Check the Worker-level Access policy and the two service headers above. Do not broaden the agent bypass. |
219
+ | Executor check fails | Retain `quickJsExecutor()` and its peer on Node, or `workerExecutor({ loader })`, the codemode peer, and `LOADER` on Workers. |
220
+ | MCP 401 | Check the saved token, declared agent, and slot state; rotated/revoked tokens refuse. Use `/agent/<pool>` with `--pool`. |
221
+ | MCP 404 | Check the pool name and `members: ["agent:assistant"]`; an existing but inaccessible pool is deliberately indistinguishable from a missing one. |
222
+ | 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. |
223
+
224
+ After a fix, repeat doctor with the same client credentials and pool. Never
225
+ paste a token or raw downstream failure body into a committed diagnostic log.
226
+
130
227
  ## Upgrade an existing deployment
131
228
 
132
229
  Bump the exact package pin, read the [changelog](../CHANGELOG.md) for every
133
230
  intervening release, and apply the relevant migration before startup. `init`
134
231
  does not merge changes into an existing deployment.
135
232
 
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.
233
+ - [Migration to 0.34](./migration-0.34.md) covers the Overview landing page, Endpoints at `/endpoints`, default browser titles, and Workspace discovery in owner-less agent pools. No storage migration or token rotation is needed from 0.33.
234
+ - [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.
235
+ - [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
236
  - [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
237
  - [Worker storage from 0.28](../examples/worker/README.md#upgrading-from-028) copies KV into D1 with a one-shot migration.
139
238
  - [Notion, Vercel, and Cloudflare for 0.29](./provider-migration-0.29.md) change defaults and retained API tool names.
140
239
  - [Executor wiring](./code-mode.md#deploy-time-capability) requires branded shipped executors or a self-managed custom executor.
141
240
 
142
- ## Implementation and evidence
241
+ ## Contributor reference
242
+
243
+ ### Implementation and evidence
143
244
 
144
245
  Check the [CLI](https://github.com/zackbart/connecta/blob/main/bin/connecta.mjs),
145
246
  [Node configuration](../templates/node/src/connecta.config.ts), and
@@ -1,16 +1,65 @@
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: a shared connector goes in the tool list of the lowest tier whose
23
+ entire audience is authorized to use it (billing restricted to leads goes
24
+ in the leads tier); per-user connectors and Workspace connectors with a
25
+ mapping `subject` go only in personal pools.
26
+ 4. Run the deployment's `npm run typecheck` (Worker copies: `tsc --noEmit`),
27
+ restart Node or redeploy the Worker, and
28
+ [connect its credential](./auth.md#connect-a-shared-credential) if needed.
29
+ Run [doctor](./deploying.md#verify-and-diagnose), discover its tools from
30
+ the intended pool, and perform a bounded read. Check an excluded pool too.
31
+
32
+ For example, merge this fragment into the existing configuration, retaining
33
+ its storage, executor, auth, and `readOnly` settings:
34
+
35
+ ```ts
36
+ import { remoteMcp } from "@zackbart/connecta";
37
+
38
+ const crm = remoteMcp("crm", {
39
+ url: "https://crm.example.com/mcp",
40
+ authScope: "shared",
41
+ auth: { type: "credential", credential: { label: "CRM API token" } },
42
+ });
43
+ const base = ["crm", "tickets"]; // the agent's lowest tier
44
+ // connectors: [crm, tickets], agents: ["assistant"],
45
+ // pools: { assistant: { tools: base, members: ["agent:assistant"] } }
46
+ ```
47
+
48
+ To remove `crm`, first disconnect its OAuth grant or delete its saved
49
+ credential while the connector still exists, using a signed-in maintainer
50
+ for shared auth. Each person disconnects their own personal account. Remove
51
+ `crm` and all `crm.*` addresses from every pool (including tier tool lists),
52
+ then remove its entry from `connectors`, unused imports, and unused secret
53
+ configuration. Remove any pools left with no tools (empty `tools` fails
54
+ construction), and update or retire clients pointing at those pools. Typecheck, restart/redeploy, run doctor, and confirm discovery
55
+ and calls no longer find it. Removing configuration alone does not revoke a
56
+ credential at the downstream service; revoke unused credentials there too.
57
+
9
58
  ## Choose a connector
10
59
 
11
60
  | Need | Configuration path | Check before using |
12
61
  | -------------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------- |
13
- | A maintained integration | Its `@zackbart/connecta/providers/<name>` factory | Provider `SKILL.md`, options, auth ownership, supported tools |
62
+ | A maintained integration | Its `@zackbart/connecta/providers/<name>` factory | Published factory declarations, auth ownership, supported tools |
14
63
  | Another hosted remote MCP server | `remoteMcp()` from the root package | Endpoint, authentication, current catalog, and read/write annotations |
15
64
  | A selected HTTP API capability | `api()` from the root package | Input/output schemas, explicit read/write hints, bounded handler, credential handling |
16
65
 
@@ -19,6 +68,14 @@ registry enforcement, caller grants, result bounds, and activity paths. There
19
68
  is no runtime installation or registration. Keep connector IDs unique and
20
69
  stable because they prefix canonical `<connectorId>.<toolName>` addresses.
21
70
 
71
+ `callAdmission` is optional on `remoteMcp()`, `api()`, custom connectors, and
72
+ provider variants that expose an override. Only omission or `undefined` uses
73
+ the connector's documented default, which may be no admission. Every defined
74
+ value is validated at construction. Malformed policies, including `false`,
75
+ `null`, `0`, and `""`, throw before serving requests; they never disable
76
+ admission or select a default. Custom connector policies are validated when
77
+ the deployment constructs its registry.
78
+
22
79
  ## Maintained integrations
23
80
 
24
81
  Import a provider individually. For example, Linear requires an explicit
@@ -35,25 +92,26 @@ const tracker = linear("tracker", {
35
92
  ```
36
93
 
37
94
  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.
42
-
43
- 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
95
+ read scope. That is separate from a Connecta `readOnly` endpoint. Read the installed `dist/providers/linear/index.d.ts` for API-key framing,
96
+ workspace instructions, and admission choices.
97
+
98
+ The [human README](../README.md#what-you-can-connect) lists available
99
+ integrations. In the installed package, `dist/providers/<name>/index.d.ts`
100
+ and its imported declarations describe each factory's options. After adding
101
+ a connector, use `search_tools` to discover its current tools and `connecta.skill("connector:<id>")` in `execute_code`
102
+ to read its runtime guide; provider source skills are not installed files.
103
+ Google Workspace
47
104
  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
105
+ mapping from admitted identity to the downstream account; an agent acts as its
106
+ pool's `owner` instead ([auth guide](auth.md#pool-owners-for-google-workspace)).
107
+ A request must not choose its own account. Gmail never sends mail, and Drive never permanently
50
108
  deletes files.
51
109
 
52
110
  Wispr Flow uses OAuth only through `wisprFlow()` from
53
111
  `@zackbart/connecta/providers/wispr-flow`. It reaches the authorized account's
54
112
  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
113
+ Its runtime guide, available through `connecta.skill("connector:<id>")` in `execute_code`, describes account access,
114
+ search limits, transcript permissions and paging. The
57
115
  reviewed classification includes task writes listed in Claude's directory even
58
116
  though Wispr's public help describes read-only access; discover the account's
59
117
  actual tools before assuming those writes exist.
@@ -75,6 +133,16 @@ MCP clients to its hosted server, an operational gate outside Connecta. See the
75
133
  To use both implementations of one provider, configure two connector ids with
76
134
  independent credentials.
77
135
 
136
+ Both Notion auth variants accept optional `callAdmission`. For a token
137
+ connector it replaces the whole per-runtime default of 180 calls per minute
138
+ and 3 concurrent. Notion's [request limits](https://developers.notion.com/reference/request-limits)
139
+ allow 600 requests per minute per connection on Business and Enterprise and
140
+ 180 on other plans. A separate workspace-wide limit is shared by all
141
+ connections. Size an override for other runtime traffic and tools that make
142
+ multiple HTTP requests. Only an omitted or `undefined` policy selects the
143
+ default; malformed policies, including `false`, `null`, `0`, and `""`, throw
144
+ at construction on both auth variants.
145
+
78
146
  Planning Center's `excludeApps` removes whole products from a connection, for
79
147
  example `planningCenter("pco", { purpose, excludeApps: ["giving"] })` for a
80
148
  deployment that keeps agents away from donor and financial data. Excluded
@@ -130,8 +198,8 @@ const service = remoteMcp("service", {
130
198
  description: "Our team's hosted service",
131
199
  auth: { type: "credential" },
132
200
  });
133
- // Add service to connectors. Configure the vault and permitted operators
134
- // before supplying its credential through the operator UI.
201
+ // Add service to connectors. Configure the vault; a signed-in maintainer
202
+ // supplies its credential through the operator UI.
135
203
  ```
136
204
 
137
205
  Credential auth defaults to an `Authorization: Bearer` header. Choose framing
@@ -198,10 +266,10 @@ schemas when known; runtime observations cannot replace them.
198
266
 
199
267
  `authScope: "shared"` uses deployment-owned downstream auth; `"personal"` uses
200
268
  the admitted principal's partition. Visibility is a different decision,
201
- controlled by identity and pool grants. A personal connector must not close
269
+ controlled by maintainers and pool membership. A personal connector must not close
202
270
  over a shared secret or derive its owner from arguments. Remote MCP refuses
203
271
  literal header credentials with personal scope. Review
204
- [principals and grants](./auth.md#principals-visibility-and-operators) and
272
+ [maintainers and members](./auth.md#principals-visibility-and-operators) and
205
273
  [management permissions](./auth.md#management-permissions) before exposing a
206
274
  new integration or its authentication controls.
207
275
 
@@ -210,7 +278,13 @@ perform a bounded read. Add negative checks for target restrictions and writes
210
278
  when authoring a handler or provider. Follow repository verification policy
211
279
  when changing package code.
212
280
 
213
- ## Implementation and evidence
281
+ ## Contributor reference
282
+
283
+ ### Implementation and evidence
284
+
285
+ To implement a maintained provider in Connecta itself, follow the repository's
286
+ [add-provider skill](https://github.com/zackbart/connecta/blob/main/.claude/skills/add-provider/SKILL.md)
287
+ and [AGENTS.md](https://github.com/zackbart/connecta/blob/main/AGENTS.md).
214
288
 
215
289
  Factory contracts live in [remote MCP](https://github.com/zackbart/connecta/blob/main/src/connectors/remote-mcp.ts),
216
290
  [API connector](https://github.com/zackbart/connecta/blob/main/src/connectors/api-connector.ts),