@zackbart/connecta 0.38.0 → 0.40.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 (180) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/README.md +3 -2
  3. package/bin/connecta.mjs +4 -8
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.js +6 -4
  6. package/dist/agent-routes.js +231 -0
  7. package/dist/auth/downstream-oauth/consent.js +519 -0
  8. package/dist/auth/downstream-oauth/grant-store.js +439 -0
  9. package/dist/auth/downstream-oauth/refresh-coordinator.js +562 -0
  10. package/dist/auth/downstream-oauth/refresh-records.js +447 -0
  11. package/dist/auth/downstream-oauth/sdk-hooks.js +242 -0
  12. package/dist/auth/downstream-oauth.js +66 -2382
  13. package/dist/auth/native.js +37 -0
  14. package/dist/auth/oauth-grant.js +13 -0
  15. package/dist/auth/static-oauth.js +153 -131
  16. package/dist/auth-elicitation.js +7 -1
  17. package/dist/authorization-handoff.js +11 -5
  18. package/dist/catalog-cache.js +219 -59
  19. package/dist/catalog-policy.js +18 -0
  20. package/dist/catalog-service.js +284 -47
  21. package/dist/catalog.js +18 -2
  22. package/dist/config.d.ts +16 -11
  23. package/dist/config.js +9 -6
  24. package/dist/connector-caller.d.ts +1 -1
  25. package/dist/connector-health.js +59 -0
  26. package/dist/connector-status.js +117 -0
  27. package/dist/connectors/api-connector.d.ts +7 -2
  28. package/dist/connectors/api-connector.js +7 -4
  29. package/dist/connectors/negotiation-cache.js +22 -4
  30. package/dist/connectors/remote-mcp/call.js +164 -0
  31. package/dist/connectors/remote-mcp/catalog.js +278 -0
  32. package/dist/connectors/remote-mcp/config.js +263 -0
  33. package/dist/connectors/remote-mcp/connection.js +682 -0
  34. package/dist/connectors/remote-mcp/errors.js +488 -0
  35. package/dist/connectors/remote-mcp/oauth.js +473 -0
  36. package/dist/connectors/remote-mcp/resources.js +473 -0
  37. package/dist/connectors/remote-mcp/runtime.js +1 -0
  38. package/dist/connectors/remote-mcp/transport.js +555 -0
  39. package/dist/connectors/remote-mcp.js +62 -2863
  40. package/dist/credential-contract.d.ts +23 -0
  41. package/dist/credential-reads.js +17 -0
  42. package/dist/credential-verification.js +53 -0
  43. package/dist/credentials.d.ts +7 -0
  44. package/dist/credentials.js +60 -25
  45. package/dist/describe-config.d.ts +12 -2
  46. package/dist/describe-config.js +2 -0
  47. package/dist/discovery-health.js +199 -0
  48. package/dist/downstream-input.js +2 -0
  49. package/dist/errors.d.ts +55 -17
  50. package/dist/errors.js +95 -41
  51. package/dist/execute.js +152 -114
  52. package/dist/executor-result.js +3 -1
  53. package/dist/guest-functions.js +83 -0
  54. package/dist/guest-runtime.js +60 -19
  55. package/dist/guest-types.d.ts +19 -5
  56. package/dist/http/argument-problems.js +35 -0
  57. package/dist/http/errors.js +5 -4
  58. package/dist/http/fields.js +192 -13
  59. package/dist/http/hatch-response.js +73 -0
  60. package/dist/http/hatch.js +88 -23
  61. package/dist/http/oauth.js +3 -2
  62. package/dist/http/provider.js +32 -4
  63. package/dist/http/safety.js +8 -0
  64. package/dist/http/tools.js +44 -23
  65. package/dist/index.d.ts +1 -1
  66. package/dist/index.js +9 -2
  67. package/dist/invocation.js +26 -8
  68. package/dist/membership.d.ts +13 -5
  69. package/dist/membership.js +34 -2
  70. package/dist/meta-tools.js +54 -31
  71. package/dist/native-recovery.js +4 -16
  72. package/dist/native-tools.js +5 -2
  73. package/dist/oauth-sealing.js +10 -0
  74. package/dist/operation-vocabulary.js +135 -13
  75. package/dist/operator-record.d.ts +2 -2
  76. package/dist/operator-record.js +1 -0
  77. package/dist/operator-ui/contract.d.ts +10 -0
  78. package/dist/operator-ui/generated.js +2 -2
  79. package/dist/operator-ui/model.d.ts +22 -1
  80. package/dist/provider-call-facts.js +6 -2
  81. package/dist/providers/_shared/rest/operation-index.js +48 -50
  82. package/dist/providers/_shared/rest/value-safety.js +80 -0
  83. package/dist/providers/breeze/api.js +7 -1
  84. package/dist/providers/breeze/skill.generated.js +1 -1
  85. package/dist/providers/breeze/tools.js +4 -3
  86. package/dist/providers/ccb/api.js +2 -1
  87. package/dist/providers/ccb/index.js +5 -0
  88. package/dist/providers/ccb/skill.generated.js +1 -1
  89. package/dist/providers/cloudflare/api.js +8 -1
  90. package/dist/providers/cloudflare/skill.generated.js +1 -1
  91. package/dist/providers/docs/lib/schemas.js +24 -22
  92. package/dist/providers/docs/safety.js +2 -0
  93. package/dist/providers/docs/skill.generated.js +1 -1
  94. package/dist/providers/docs/tools.js +2 -4
  95. package/dist/providers/drive/api.js +1 -0
  96. package/dist/providers/drive/lib/contracts.js +8 -5
  97. package/dist/providers/drive/skill.generated.js +1 -1
  98. package/dist/providers/drive/tools.js +11 -16
  99. package/dist/providers/forms/api.js +1 -0
  100. package/dist/providers/forms/skill.generated.js +1 -1
  101. package/dist/providers/forms/tools.js +10 -7
  102. package/dist/providers/github/api.js +1 -0
  103. package/dist/providers/github/lib/discovery.js +1 -1
  104. package/dist/providers/github/skill.generated.js +1 -1
  105. package/dist/providers/github/tools.js +61 -17
  106. package/dist/providers/gmail/lib/schemas.js +60 -50
  107. package/dist/providers/gmail/skill.generated.js +1 -1
  108. package/dist/providers/gmail/tools.js +7 -5
  109. package/dist/providers/granola/index.js +2 -0
  110. package/dist/providers/granola/skill.generated.js +1 -1
  111. package/dist/providers/overflow/api.js +7 -1
  112. package/dist/providers/overflow/skill.generated.js +1 -1
  113. package/dist/providers/planning-center/api.js +3 -1
  114. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  115. package/dist/providers/planning-center/skill.generated.js +1 -1
  116. package/dist/providers/quickbooks/safety.js +80 -39
  117. package/dist/providers/quickbooks/skill.generated.js +1 -1
  118. package/dist/providers/sheets/api.js +1 -0
  119. package/dist/providers/sheets/skill.generated.js +1 -1
  120. package/dist/providers/sheets/tools.js +13 -14
  121. package/dist/providers/slides/skill.generated.js +1 -1
  122. package/dist/providers/slides/tools.js +15 -13
  123. package/dist/providers/stripe/skill.generated.js +1 -1
  124. package/dist/providers/tithely/api.js +3 -1
  125. package/dist/providers/tithely/skill.generated.js +1 -1
  126. package/dist/providers/vercel/api.js +1 -1
  127. package/dist/providers/vercel/skill.generated.js +1 -1
  128. package/dist/providers/vercel/tools.js +4 -3
  129. package/dist/registry.d.ts +10 -4
  130. package/dist/registry.js +41 -13
  131. package/dist/routes/credentials.js +129 -60
  132. package/dist/routes/dispatch.js +5 -1
  133. package/dist/routes/mcp.js +30 -14
  134. package/dist/routes/operator.js +2 -2
  135. package/dist/routes/shared.d.ts +6 -1
  136. package/dist/routes/shared.js +7 -1
  137. package/dist/routes/ui-config.js +2 -2
  138. package/dist/routes/ui-data.js +126 -20
  139. package/dist/routes/ui.js +16 -0
  140. package/dist/runtime/call.js +7 -2
  141. package/dist/runtime/native-storage.js +12 -0
  142. package/dist/runtime/request-reads.js +207 -0
  143. package/dist/runtime/request-scope.js +16 -0
  144. package/dist/runtime/storage.js +12 -0
  145. package/dist/skills.js +37 -20
  146. package/dist/storage/keys.js +53 -3
  147. package/dist/storage/memory.js +8 -2
  148. package/dist/storage/sql-native.js +53 -1
  149. package/dist/storage/sql.js +12 -2
  150. package/dist/storage-digest.js +32 -0
  151. package/dist/timeout.js +8 -0
  152. package/dist/types.d.ts +33 -0
  153. package/dist/usage-guide.js +16 -35
  154. package/dist/validate.d.ts +3 -0
  155. package/dist/validate.js +220 -158
  156. package/dist/version.d.ts +1 -1
  157. package/dist/version.js +1 -1
  158. package/dist/write-outcome.js +0 -3
  159. package/documentation/README.md +13 -13
  160. package/documentation/architecture.md +385 -20
  161. package/documentation/auth.md +233 -64
  162. package/documentation/code-mode.md +122 -39
  163. package/documentation/deploying.md +6 -3
  164. package/documentation/integrating.md +16 -2
  165. package/documentation/meta-tools.md +240 -49
  166. package/documentation/migration-0.33.md +5 -1
  167. package/documentation/migration-0.34.md +3 -1
  168. package/documentation/operator-tests.md +1 -1
  169. package/documentation/operator-ui.md +105 -12
  170. package/documentation/pool-membership-upgrade.md +44 -0
  171. package/documentation/providers.md +105 -0
  172. package/examples/worker/AGENTS.md +10 -10
  173. package/examples/worker/README.md +4 -3
  174. package/examples/worker/src/connecta.config.ts +1 -0
  175. package/guides/connecting-agents.md +2 -1
  176. package/guides/how-it-works.md +4 -2
  177. package/package.json +1 -1
  178. package/templates/node/AGENTS.md +13 -12
  179. package/templates/node/package.json +1 -1
  180. package/templates/node/src/connecta.config.ts +1 -0
@@ -113,6 +113,12 @@ Pool endpoint rows show the effective surface (`meta` or `native`) and whether
113
113
  code is on. These are read-only facts from deployment configuration; change
114
114
  `surface` and `executeCode` in deployment code.
115
115
 
116
+ Plain `/mcp` is routed by sign-in: the `/mcp` row and matrix column show the
117
+ whole catalog maintainers receive, and a pool row shows what its people
118
+ receive at both `/mcp/<pool>` and, when they belong to no other pool, plain
119
+ `/mcp`. A note above the rows says so. People in several pools appear in
120
+ `config.peopleInSeveralPools`; plain `/mcp` refuses them.
121
+
116
122
  One row each for `/mcp` and every pool: the path, a read-only pill on a
117
123
  read-only endpoint, a copyable URL, who can use it, its connectors as colored
118
124
  monograms (four, then "+N"), its tool count over a read and write bar, and a
@@ -121,7 +127,9 @@ Setup button.
121
127
  - Who can use it is `root.maintainers` on `/mcp` and the pool's `members`
122
128
  elsewhere, as an avatar stack named on hover and to screen readers (label,
123
129
  raw id when the label is null, or agent name). Four show, then a "+N"
124
- button. A pool with agent members labels its two URLs People and Agents.
130
+ button. A pool lists only people or only agents. An agent pool's row
131
+ still shows two URLs labeled People and Agents; the People URL
132
+ (`/mcp/<pool>`) admits no one, because the pool has no people.
125
133
  - Expanding a row lists everyone by name, then each connector with what it
126
134
  runs as and its tools, write tools amber with a pencil. Runs-as comes from
127
135
  the endpoint's matrix cell: "Shared · saved <date>", "Shared · connected
@@ -194,8 +202,9 @@ tabs:
194
202
  annotation badges (Destructive, Open world), filtered by search and All,
195
203
  Read and Write. A tool opens its schema in a side drawer.
196
204
  - **Connection.** An Authentication card (mode, OAuth client mechanism, who
197
- manages it, OAuth actions, and the credential card with masked last four,
198
- field states, and Replace, Test and Remove); a Diagnostics card of tiles
205
+ manages it, the last verification, OAuth actions, and the credential card
206
+ with masked last four, field states, and Replace, Test and Remove); a
207
+ Diagnostics card of tiles
199
208
  (catalog age and last call) with a fix prompt beside each
200
209
  problem; and a Configuration card. Its badges name the kind (Remote MCP,
201
210
  HTTP API or Custom), provider, auth scope, endpoint host and usage guide.
@@ -340,10 +349,12 @@ connections**, has no tabs; its heading shows the signed-in email when the
340
349
  sign-in exposes one (Clerk's primary email), and its top bar shows branding and
341
350
  an **Account** button with Sign out or Change token. It lists:
342
351
 
343
- - **Your endpoints**: one card per admitted `/mcp/<pool>` URL with a copy button
344
- and **Client setup**. For example, Bob in pool `team` copies
345
- `https://connecta.example.com/mcp/team` into an MCP client and authenticates
346
- as Bob.
352
+ - **Your endpoints**: one card per address with a copy button and **Client
353
+ setup**. A member of exactly one pool sees one card for plain `/mcp`, named
354
+ after the pool it serves; a member of several pools sees one
355
+ `/mcp/<pool>` card per pool. For example, Bob, only in pool `team`, copies
356
+ `https://connecta.example.com/mcp` into an MCP client and authenticates as
357
+ Bob.
347
358
  - **Your accounts**: the personal connectors in their pools, each with a
348
359
  monogram, tool count, one status badge in an aligned column, and only the
349
360
  controls its `manage.actions` lists. A saved credential that no longer fits
@@ -415,7 +426,8 @@ without this capability contribute an unconfirmed count of zero. For example,
415
426
  The maintainer view carries:
416
427
 
417
428
  - `config`, the construction-time `describeConfig()` snapshot, including
418
- `maintainers` and each pool's `members`, and `configSources`, which labels
429
+ `maintainers`, each pool's `members`, `peopleInSeveralPools` (non-maintainer
430
+ people plain `/mcp` refuses because they are in several pools), and `configSources`, which labels
419
431
  each serialized leaf as `default` or `config` from option presence rather
420
432
  than equality with the default. Factory descriptions carry relative
421
433
  `optionSources` paths so resolved module and transport defaults retain their
@@ -488,7 +500,9 @@ The maintainer view carries:
488
500
  The member view is exactly that member's projection: `config`,
489
501
  `configSources` and `root` are `null`, `live.connectors`, `agents` and
490
502
  `matrix` are empty, and `live.activity` is `"forbidden"`. `pools` lists only
491
- their pools, each with `owner: null` and `members: []`. `connectors` lists
503
+ their pools, each with `owner: null` and `members: []`. With exactly one pool,
504
+ its `humanPath` is `/mcp`, the address that serves them that pool, and the
505
+ member's catalog reads carry that pool as plain `/mcp` would. `connectors` lists
492
506
  each connector in those pools with its title, the member's pools that serve
493
507
  it, its auth scope, the tools those pools grant with their classification,
494
508
  `connection` (their own partition's state and problem code for a personal
@@ -556,6 +570,58 @@ the config snapshot, and never treats a `per_caller` overlay as an error.
556
570
  complete classified tools with input/output schemas, registration path, catalog
557
571
  age, and activity-derived last-call facts under the reader's disclosure policy.
558
572
  A failed or timed-out connector releases its own scope; siblings finish independently.
573
+ The status and catalog observation has its own deadline: 8 seconds, or
574
+ `discovery.probeTimeoutMs` when that is shorter, in which case the whole row
575
+ uses that one deadline. The 8-second value is fixed, not configured; agent
576
+ discovery keeps the full `discovery.probeTimeoutMs`. When the deadline ends
577
+ first, the downstream read is aborted and the row returns `status: "error"`
578
+ with `problem: "connector_timeout"` in the row and its overlay. The row still
579
+ carries its credential card and auth permissions, so a slow connector's slot
580
+ stays manageable. Any other failure keeps `connector_unavailable`. The
581
+ connector renders as **Not responding**, with fixed copy, a fix prompt and
582
+ Refresh.
583
+ **Stored status.** Each live `GET /ui/connectors/:id` records one status
584
+ observation for that connector and viewer partition before it answers: its
585
+ state, problem code, tool count and observation time. Authored static catalogs
586
+ such as `api()` connectors are recorded too, since their status still checks
587
+ auth. The record lives in the `connector-health:v1:` family under the same
588
+ owner partition as the catalog cache (principal, subject, caller identity and
589
+ pool, endpoint, described configuration and package version), so a personal
590
+ connector's observation is one person's. It also carries the catalog
591
+ generation read before the probe, through the request's read memo that the
592
+ catalog listing reuses. It holds no tools, schemas, status prose or
593
+ credential facts. It expires 120 seconds after it was observed, and lapses at
594
+ once when a credential change, OAuth connect or disconnect, or invalidation
595
+ rotates the generation. A read that ends at the row's own deadline, including
596
+ a configured `discovery.probeTimeoutMs` shorter than 8 seconds, records
597
+ `connector_timeout` before it answers. An observation is written only while
598
+ the caller is still waiting: a request its caller has already left starts no
599
+ write. A write that has started is never raced against a deadline or
600
+ detached, because a storage write cannot be withdrawn once sent. The request,
601
+ whether it times out or its caller leaves meanwhile, settles only after that
602
+ write does, bounded by the storage layer like every other awaited write, so
603
+ nothing commits after the request ends.
604
+
605
+ `GET /ui/connectors/:id?observation=stored` answers from a current observation
606
+ without probing the downstream. The row and overlay carry `observedAgeMs`, the
607
+ stored `status`, `problem` and `toolCount`, and `tools: []`; the tools stay in
608
+ the catalog cache. The credential card, drift, permissions and any
609
+ authorization link are computed fresh on every request. Without a current
610
+ observation the route does the live read and records it, with no
611
+ `observedAgeMs`. Nothing is refreshed after the response: no deferred work and
612
+ no `ctx.waitUntil`.
613
+
614
+ The browser asks each connector for its stored observation on the page load
615
+ that follows admission, and fixes the observation time once as each stored
616
+ row lands. The list card and the connector page mark a stored row the same
617
+ way, "Stored status, checked … ago", with an age that keeps counting, and
618
+ show its tools as awaiting the live read rather than as none. The browser then
619
+ sends one plain live read per stored row, through the same queue of eight
620
+ detail reads. A stored `connector_timeout` row is not read again on its own;
621
+ its mark says to Refresh, and it waits for Refresh like a live timeout.
622
+ Refresh, a single connector's read, recovery retries and OAuth polls always
623
+ read live.
624
+
559
625
  The summary and each detail carry `viewerKey` for the principal admitted to that
560
626
  request. The browser publishes the detail and overlay atomically only when that
561
627
  key matches admission. A changed viewer key, including an Access switch between
@@ -568,7 +634,7 @@ screen. The first answer for the new viewer triggers readmission, which clears
568
634
  everything retained.
569
635
  Config observations never
570
636
  overwrite a completed detail row. Each connector opens at most one live observation
571
- per page load; the summary `/ui/data` performs no downstream probes.
637
+ per page load, after its stored read; the summary `/ui/data` performs no downstream probes.
572
638
 
573
639
  `GET /ui/api/config?connector=<id>` narrows the same contract to one connector
574
640
  before status and catalog probes. For a maintainer, config, provenance, live
@@ -628,8 +694,23 @@ bounded at 2, 2, 4 and 8 seconds; a later focus return can try again after backo
628
694
 
629
695
  Connector Refresh, focus recovery and mutation follow-ups read only affected
630
696
  connectors. Focus probes unhealthy rows, never healthy siblings. All detail reads
631
- share a four-request limit and coalesce per connector. Failures retry only their
697
+ in one tab share an eight-request limit and coalesce per connector, so a page of
698
+ up to eight connectors loads in one round. The limit is fixed, not configured.
699
+ It bounds how many connectors one tab waits on at once; a tab still reads each
700
+ connector once per load, so the limit adds no requests to any one connector.
701
+ Detail reads do not consume `admission.requests` permits, which govern `/mcp`
702
+ only. They still share isolate CPU and memory, D1, and downstream account quotas
703
+ with agent traffic; each tab adds up to eight concurrent downstream reads, and
704
+ several connectors on one vendor account share that vendor's rate limit. Over HTTP/1.1 the browser itself allows about six
705
+ connections per origin.
706
+ Failures retry only their
632
707
  connector with the same bounded backoff; recovery never replays a mutation.
708
+ A `connector_timeout` row is parked instead. It spends its automatic retry
709
+ budget, and focus and visibility returns skip it. Only Refresh, a full refresh
710
+ or a mutation follow-up reads it again, so an outage holds a detail slot once
711
+ per explicit request, never repeatedly. An OAuth start's consent-return poll is
712
+ the exception: a timeout during its 30-second window keeps polling, and success
713
+ still stops it.
633
714
  Mutations cancel affected reads and advance their publication fence both at
634
715
  dispatch and on settlement, including failure. Follow-ups require an observation
635
716
  started after settlement, so a Refresh or retry started during the write cannot
@@ -638,9 +719,21 @@ while identity and full refresh revisions fence the complete view. An explicit
638
719
  OAuth start polls only that connector every two seconds for up to 30 seconds.
639
720
  Concurrent authorizations retain independent cancellation and publication. Success,
640
721
  failed reads, disconnect, navigation, unmount and identity reset stop the matching
641
- poll. Recovery never starts authorization. A scoped result cannot confirm identity,
722
+ poll; a `connector_timeout` result does not. Recovery never starts authorization. A scoped result cannot confirm identity,
642
723
  role, token slots or any other deployment-wide admission fact.
643
724
 
725
+ A credential connector's `/ui/data` and `/ui/connectors/<id>` row carries
726
+ `verification: { outcome, observedAt }` when a record of the stored save
727
+ exists (see [last verification](./auth.md#test-then-save-and-last-verification)).
728
+ The auth facts show it as **Last verified**: Verified, Saved without a test,
729
+ Rejected, Missing permission, Service unreachable, Test timed out or Test
730
+ failed, with its time. A maintainer's credential form on a testable connector
731
+ saves through **Test and save** (`verify: "test"`). A failed test leaves the
732
+ form open with fixed copy for its failure class, and adds **Save without
733
+ testing**, a save without `verify`. Test shows the same per-class copy and reloads
734
+ the connector so its last verification updates. The member page's
735
+ credential form saves without a test and offers Test once a value is stored.
736
+
644
737
  Unreadable credential metadata uses `configured: null`, including named fields,
645
738
  and renders as unavailable with a retry prompt. It does not claim that the slot
646
739
  is empty. Overview, Endpoints, Connectors, connector
@@ -0,0 +1,44 @@
1
+ # Split mixed pools
2
+
3
+ Every pool must list only people or only `agent:` references. A mixed pool
4
+ throws `ConfigError` at construction naming the pool, for example
5
+ `ConnectaConfig.pools.team.members mixes people and agents; list only one kind.`
6
+ This also applies to personal pools with or without `owner`. Pool names and
7
+ connector ids do not decide membership type. People-only pools may retain an
8
+ `owner`; it never changes the account a person uses.
9
+
10
+ Before upgrading, inspect every pool's `members`, including generated lists.
11
+ For each mixed team pool, define its tool list once and create two pools:
12
+
13
+ ```ts
14
+ const teamTools = ["crm", "tickets"];
15
+ const pools = {
16
+ team: { tools: teamTools, members: ["user_alice", "user_bob"] },
17
+ "assistant-team": { tools: teamTools, members: ["agent:assistant"] },
18
+ };
19
+ ```
20
+
21
+ Keep the original human pool name when possible so people's existing
22
+ `/mcp/team` address stays valid. Point the agent harness at
23
+ `/agent/assistant-team` and update its tier list to that name. Copy the
24
+ original `readOnly`, `surface`, `executeCode` and `description` settings
25
+ to both new pools as appropriate so each preserves its intended behavior.
26
+ If the mixed pool set `owner`, keep `owner` on the agent pool: it is the
27
+ Workspace address the agent acts as, and without it Workspace connectors with
28
+ a mapping `subject` disappear for the agent. People never use `owner`; they
29
+ always act as themselves, so the people pool does not need it. Agent pools
30
+ never count toward a person's plain `/mcp` routing, so a person who was in
31
+ one mixed pool and no other still reaches the people pool at plain `/mcp`.
32
+
33
+ For personal `assistant-alice` pools, keep only `"agent:assistant"` in
34
+ `members` and retain the owner's Workspace address and tools. Give people
35
+ their own people-only pools. Workspace mapping connectors act as each person;
36
+ shared-auth connectors act as their saved account for every member, so review
37
+ those grants before copying personal tool lists.
38
+
39
+ An agent can still belong to many pools with one token. Tokens, credentials,
40
+ `poolFor` tier selection logic and the Eve helper need no changes beyond pool
41
+ names in client configuration. Rebuild and restart the deployment, then verify
42
+ people and agents can each open their intended endpoints. See
43
+ [Organizing an agent's pools](./auth.md#organizing-an-agents-pools) for the
44
+ complete layout.
@@ -360,6 +360,67 @@ discovery for your deployment's catalog.
360
360
 
361
361
  <!-- provider-inventory:end -->
362
362
 
363
+ ## Field selection with `fields`
364
+
365
+ Every maintained read that projects a result takes an optional `fields`
366
+ argument: up to 50 dot paths, each 1 to 200 characters, that narrow the
367
+ returned data. Selection runs after value safety and privacy filtering, so it
368
+ can only remove values. `a.b` and `a[].b` are equivalent; a list on the way is
369
+ narrowed item by item.
370
+
371
+ Where paths start, the anchor, depends on the tool and is stated in each
372
+ `fields` schema description with one valid example:
373
+
374
+ | Tool | Paths start at | Example |
375
+ | ------------------------------------------ | ---------------------------------- | -------------------------------------------- |
376
+ | A named read (`get_*` and similar) | The result | `["id"]`, `["owner.name"]` |
377
+ | A named list or search (`{ items, page }`) | Each item in `items` | `["id"]`, not `["items.id"]` |
378
+ | `*_api_read` and `*_api_write` | The vendor response body in `data` | `["projects.name"]`, not `["data.projects"]` |
379
+
380
+ A hatch body that is a list is narrowed item by item, so its paths start at
381
+ each item. An enveloping vendor nests records under a root, such as
382
+ `{ "projects": [...] }`; select `projects.name`, not `name`. Copy hatch paths
383
+ from the operation's `*_api_details` `response.fields`, which are already in
384
+ these coordinates. Paging, cut and recovery metadata (`page`, `truncated`)
385
+ remain outside the selection where a tool says so.
386
+
387
+ **Refused before dispatch.** A path outside the declared contract returns
388
+ `invalid_args` before any request. Each finding names the entry position
389
+ (`/fields/1`), the segment where the path left the contract and the properties
390
+ declared there, never the submitted path:
391
+
392
+ - Named tools check the first segment against the properties their published
393
+ output schema declares for the result or item. Deeper segments follow JSON
394
+ Schema's default: an object is closed only where the schema says
395
+ `additionalProperties: false`, and only such an object refuses an undeclared
396
+ property. Paths through other objects, open values such as GitHub
397
+ `search_scoped` rows' `item`, and paths past a scalar are accepted; a miss
398
+ there is omitted from the selection.
399
+ - Hatch reads and writes check the first segment against the operation's
400
+ declared response roots. An operation that declares no response properties
401
+ refuses `fields`. The pinned index does not establish whether deeper levels
402
+ are complete, so deeper segments are not refused.
403
+
404
+ **Declared but absent is not an error.** Optional values, empty lists and
405
+ values that value safety or privacy filtering removed are ordinary. A declared
406
+ path the current response lacks is never refused, and the selection omits it.
407
+
408
+ **Hatch miss warnings.** A hatch answer adds `warnings: string[]` when a
409
+ `fields` entry matched nothing anywhere in a nonempty response. Each warning
410
+ names the entry position, the segment where matching stopped and the
411
+ properties the operation's contract declares there, for example
412
+ `fields/1 matched nothing in this response after value safety and privacy
413
+ filtering, at segment 2; declared there: id, name.` Where the contract
414
+ declares none, such as inside an open map, it says so. A warning never lists
415
+ the response's own keys, which in an open map such as `metadata` are data, and
416
+ never repeats the submitted path. A path that matched in any listed item, a
417
+ path that runs past a scalar (selection keeps the scalar), and any path of an
418
+ empty body (`null`, `[]` or `{}`) produce no warning. Warnings are bounded: each
419
+ is at most 512 characters, at most 10 are kept within 4,096 characters
420
+ together, reviewed vendor warnings included, and a final entry counts the rest
421
+ (`3 more warnings omitted.`). Treat a warning as a repair hint for the next
422
+ call, not as a failure.
423
+
363
424
  ## API operation discovery and boundaries
364
425
 
365
426
  Standard API operation search accepts resource nouns, actions and conversational
@@ -418,6 +479,50 @@ then follow `page.nextCursor` to reach operations beyond the leading profile
418
479
  results. Responses contain at most eight compact hits within 16 KiB; fetch
419
480
  operation details separately and keep the same query when continuing.
420
481
 
482
+ Operation details return the request contract and, for an operation that is
483
+ not refused and declares success-response properties, a `response` section.
484
+ Write `fields` filters and reductions from it without a sampling read:
485
+
486
+ - `anchor` is `data` when paths start at the read result's `data`, or `data[]`
487
+ when the body is a list and each path applies to every item.
488
+ - `fields` lists the declared paths a read can select, in the read and write
489
+ tools' `fields` coordinates; pass them unchanged, such as
490
+ `fields: ["data[].id"]`.
491
+ - `withheld` is a best-effort, incomplete hint: the listed paths whose
492
+ non-null values value safety always withholds or redacts, by a reviewed
493
+ redaction path, a credential name, the deployment's sensitive-field choice
494
+ or a closed projection. A rule that can leave some value unchanged is never
495
+ a hint. Examples are URL sanitizing, reduction to an origin, which returns a
496
+ bare origin unchanged, redaction by a key, type or label, and a credential
497
+ suffix rule that scrubs only strings. A container above a hinted path is
498
+ not hinted either. Every hinted path also appears in `fields`.
499
+ - `omitted` counts declared paths not listed.
500
+
501
+ **`response` promises no readability.** Value safety runs on every read. A
502
+ value at any listed path, hinted or not, may still be withheld or redacted at
503
+ read time. An unlisted path may be too, so never treat it as safe. Many rules
504
+ depend on the values a vendor returns, which no index knows in advance:
505
+
506
+ - An expanded or polymorphic object is redacted by its type: for example, a
507
+ Stripe event's `data` or a Checkout Session's expanded `payment_intent`
508
+ loses a payment intent's `client_secret`.
509
+ - An opaque container is redacted by its contents: Google Docs
510
+ `inlineObjects` loses image `contentUri` capabilities, and Vercel drain
511
+ `headers`, deployment `env` and `protectionBypass` keys are withheld.
512
+ - A labelled or typed record is redacted by its label or type.
513
+ - URLs carrying credentials are sanitized. QuickBooks replaces every http(s)
514
+ URL in a returned string, including ordinary text such as a Customer
515
+ `DisplayName` or `CompanyName`, with `[redacted]`.
516
+ - A value echoing a secret the request sent is redacted.
517
+
518
+ Paths list shallowest first, in index order, up to 100 paths and 4,096
519
+ characters of path JSON, the hint included; a path over 200 characters is
520
+ never listed. The pinned index carries names only, never response types or
521
+ example values. Only the first page of a whole-operation answer carries
522
+ `response`; `param` answers and continuation pages omit it. An operation
523
+ without declared response properties has no `response`, and its reads refuse
524
+ `fields`.
525
+
421
526
  Maintained REST hatches validate path parameters against their pinned contracts
422
527
  before dispatch. Integer ids use canonical ASCII decimal notation within the
423
528
  safe-integer range and obey the operation's declared bounds; for example,
@@ -95,16 +95,16 @@ tokens, upgrading, and diagnosis with `connecta doctor` to their procedures.
95
95
  Each agent keeps one Connecta connection whose harness, never the model,
96
96
  picks the pool per turn. Tiers are nested pools of shared connectors with
97
97
  no `owner`, each spreading the tool list below. Recommended names are
98
- `<agent>` for the lowest and `<agent>-<tier>` above it; any named pool,
99
- including an existing `team` or `leadership` pool, may be a tier if all its
100
- human members are cleared for it. A chat gets the most capable tier everyone
101
- present is cleared for, and the lowest when anyone present is unknown.
102
- `<agent>-<user>` (that user's highest tier plus their own connector ids,
103
- `owner` set to their Workspace address) is used only in a one-on-one with
104
- that user. List only cleared people in a tier, and never another person in
105
- a `<agent>-<user>` pool: its per-user connectors act as that user for every
106
- member. Never add a second Connecta connection or mount downstream tools
107
- directly.
98
+ `<agent>` for the lowest and `<agent>-<tier>` above it. Every pool lists
99
+ only people or only `agent:` references; mixing them throws at
100
+ construction. Tiers and `<agent>-<user>` pools list only the agent. People
101
+ with a tier's reach get their own people-only pool (for example `team`
102
+ beside `assistant-team`) spreading the same tool-list constant. A chat gets
103
+ the most capable tier everyone present is cleared for, and the lowest when
104
+ anyone present is unknown. `<agent>-<user>` (that user's highest tier plus
105
+ their own connector ids, `owner` set to their Workspace address) is used
106
+ only in a one-on-one with that user. Never add a second Connecta connection
107
+ or mount downstream tools directly.
108
108
  - Brand the operator UI in `operatorUi({ branding })`: product and owner names,
109
109
  description, favicon, and `theme` (`accent`, `radius`, `fontFamily`,
110
110
  `monoFamily`, `colorScheme`). Ask the deployment's owner for their brand
@@ -380,9 +380,10 @@ declared agent, is available to signed-in maintainers.
380
380
  ## Select optional modules
381
381
 
382
382
  `cloudflareAccessAuth()` reads trusted identity after Access admits the Worker
383
- request. Maintainers use plain `/mcp` and the operator pages; people in a pool
384
- use `/mcp/<pool>`, and agents in it `/agent/<pool>`, which serve exactly that
385
- pool's tools. Agents authenticate with `accessTokens(storage)` over the same D1
383
+ request. Maintainers use plain `/mcp` and the operator pages; people in a
384
+ people pool use `/mcp/<pool>` (or plain `/mcp` when it is their only pool), and
385
+ agents in an agent pool use `/agent/<pool>`; each serves exactly that pool's
386
+ tools. Agents authenticate with `accessTokens(storage)` over the same D1
386
387
  database; human endpoints refuse their tokens, and Access service headers alone
387
388
  never grant MCP access or interactive authority.
388
389
  Keep users, groups, and admission in Access. Connector visibility and management
@@ -111,6 +111,7 @@ export default defineConfig((env: Env) => {
111
111
  // Access user ids or "agent:<name>". The starter pool is the agent's base
112
112
  // pool (shared connectors, no owner), named after the agent as the agent
113
113
  // pool layout requires.
114
+ // Each pool lists only people or only agents; share tool lists across separate pools.
114
115
  pools: { ci: { tools: ["echo"], members: ["agent:ci"] } },
115
116
  // Connectors that declare a `credential` slot become editable by a
116
117
  // maintainer (shared) or by the member it belongs to (personal),
@@ -11,7 +11,8 @@ has exact setup and verification steps.
11
11
 
12
12
  Imagine an agent named `assistant` that helps with CRM records and tickets.
13
13
  Its operator creates a pool with the actions needed for that job and adds
14
- the agent as a member. The pool can be read-only if updates must be impossible.
14
+ the agent as a member. Agent pools contain only agents; people use separate
15
+ pools that can share the same tool list. The pool can be read-only if updates must be impossible.
15
16
 
16
17
  ```mermaid
17
18
  flowchart LR
@@ -41,7 +41,7 @@ For setup and migrations, see [Integrating services](../documentation/integratin
41
41
 
42
42
  A **pool** is a named selection of tools and the people or agents allowed
43
43
  to use them. The operator creates it and gives it an **endpoint**: a
44
- connection address for that pool.
44
+ connection address for that pool. Each pool lists only people or only agents.
45
45
 
46
46
  One pool might let the assistant read CRM records. Another might let a
47
47
  person update tickets. Tools and accounts are separate choices: a pool
@@ -65,7 +65,9 @@ choose the right one for the conversation. The model does not choose its
65
65
  own access level.
66
66
 
67
67
  Maintainers run the deployment and can reach its whole tool list through
68
- the main human endpoint. Other people use their assigned pools.
68
+ the main human endpoint, `/mcp`. Other people use their assigned pools. A
69
+ person in exactly one pool can use that same `/mcp` address: their sign-in
70
+ selects the pool, and they get only its tools.
69
71
  Agents use shared accounts selected by the operator. They do not inherit
70
72
  the account of the person chatting with them.
71
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.38.0",
3
+ "version": "0.40.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of six meta-tools.",
@@ -33,8 +33,9 @@ tokens, upgrading, and diagnosis with `connecta doctor` to their procedures.
33
33
  Issue or rotate an agent's token with `npm run --silent rotate-token -- <agent>`
34
34
  against the same configuration and database the server uses; bind a token
35
35
  from before agents once with `npm run adopt-token -- <token-id> <agent>`.
36
- Plain `/mcp` serves `maintainers` only; other people reach `/mcp/<pool>` and
37
- agents `/agent/<pool>` for a pool listing them in `members` (`npm run doctor`
36
+ Plain `/mcp` serves `maintainers` the whole catalog and each other person
37
+ in exactly one pool that pool; people also reach `/mcp/<pool>` and agents
38
+ `/agent/<pool>` for a pool listing them in `members` (`npm run doctor`
38
39
  checks `/agent/local-machine`). Human endpoints refuse `cta_` tokens. `CONNECTA_TOKEN` is only for clients and doctor; never add a configured
39
40
  static bearer or an open startup mode.
40
41
  All state, activity included, lives in the one SQLite file `CONNECTA_DATABASE`
@@ -45,16 +46,16 @@ tokens, upgrading, and diagnosis with `connecta doctor` to their procedures.
45
46
  Each agent keeps one Connecta connection whose harness, never the model,
46
47
  picks the pool per turn. Tiers are nested pools of shared connectors with
47
48
  no `owner`, each spreading the tool list below. Recommended names are
48
- `<agent>` for the lowest and `<agent>-<tier>` above it; any named pool,
49
- including an existing `team` or `leadership` pool, may be a tier if all its
50
- human members are cleared for it. A chat gets the most capable tier everyone
51
- present is cleared for, and the lowest when anyone present is unknown.
52
- `<agent>-<user>` (that user's highest tier plus their own connector ids,
53
- `owner` set to their Workspace address) is used only in a one-on-one with
54
- that user. List only cleared people in a tier, and never another person in
55
- a `<agent>-<user>` pool: its per-user connectors act as that user for every
56
- member. Never add a second Connecta connection or mount downstream tools
57
- directly.
49
+ `<agent>` for the lowest and `<agent>-<tier>` above it. Every pool lists
50
+ only people or only `agent:` references; mixing them throws at
51
+ construction. Tiers and `<agent>-<user>` pools list only the agent. People
52
+ with a tier's reach get their own people-only pool (for example `team`
53
+ beside `assistant-team`) spreading the same tool-list constant. A chat gets
54
+ the most capable tier everyone present is cleared for, and the lowest when
55
+ anyone present is unknown. `<agent>-<user>` (that user's highest tier plus
56
+ their own connector ids, `owner` set to their Workspace address) is used
57
+ only in a one-on-one with that user. Never add a second Connecta connection
58
+ or mount downstream tools directly.
58
59
  - Brand the operator UI in `operatorUi({ branding })`: product and owner names,
59
60
  description, favicon, and `theme` (`accent`, `radius`, `fontFamily`,
60
61
  `monoFamily`, `colorScheme`). Ask the deployment's owner for their brand
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@clerk/backend": "^3.12.0",
21
- "@zackbart/connecta": "0.38.0",
21
+ "@zackbart/connecta": "0.40.0",
22
22
  "quickjs-emscripten": "0.32.0",
23
23
  "undici": "6.29.1"
24
24
  },
@@ -102,6 +102,7 @@ export default defineConfig((env: Env) => {
102
102
  // `npm run doctor` checks /agent/local-machine with its token. The starter
103
103
  // pool is the agent's base pool, its lowest tier (shared connectors, no
104
104
  // owner), named after the agent as the agent pool layout requires.
105
+ // Each pool lists only people or only agents; share tool lists across separate pools.
105
106
  pools: { "local-machine": { tools: ["time"], members: ["agent:local-machine"] } },
106
107
  // Machine clients are agents declared here by name, each with one cta_
107
108
  // token. Issue or rotate one locally with