@zackbart/connecta 0.36.1 → 0.37.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 (46) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/bin/version.mjs +1 -1
  3. package/dist/auth/downstream-oauth.js +34 -12
  4. package/dist/auth/oauth-authorization-params.js +52 -0
  5. package/dist/auth/static-oauth.js +4 -6
  6. package/dist/auth/stored-observation.js +8 -0
  7. package/dist/call-admission.js +27 -14
  8. package/dist/connectors/api-connector.js +4 -0
  9. package/dist/connectors/option-shapes.js +6 -1
  10. package/dist/connectors/remote-mcp.d.ts +32 -3
  11. package/dist/connectors/remote-mcp.js +152 -21
  12. package/dist/execute.js +5 -5
  13. package/dist/executor-admission.js +27 -13
  14. package/dist/executors/quickjs.js +4 -1
  15. package/dist/google.d.ts +13 -0
  16. package/dist/google.js +42 -0
  17. package/dist/index.d.ts +2 -1
  18. package/dist/invocation.js +6 -7
  19. package/dist/operator-ui/generated.js +2 -2
  20. package/dist/operator-ui/model.d.ts +2 -0
  21. package/dist/providers/_shared/google/service-account.d.ts +1 -1
  22. package/dist/providers/_shared/google/service-account.js +20 -8
  23. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  24. package/dist/request-principal.js +34 -0
  25. package/dist/request-token.d.ts +53 -0
  26. package/dist/request-token.js +1 -0
  27. package/dist/routes/mcp.js +6 -6
  28. package/dist/routes/ui-config.js +38 -9
  29. package/dist/routes/ui-data.js +30 -5
  30. package/dist/routes/ui.js +10 -2
  31. package/dist/runtime/settlement.js +59 -0
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.js +1 -1
  34. package/dist/worker.js +4 -1
  35. package/documentation/README.md +13 -12
  36. package/documentation/architecture.md +91 -26
  37. package/documentation/auth.md +317 -12
  38. package/documentation/code-mode.md +25 -5
  39. package/documentation/integrating.md +18 -4
  40. package/documentation/operator-tests.md +19 -0
  41. package/documentation/operator-ui.md +57 -46
  42. package/examples/worker/README.md +5 -1
  43. package/guides/connecting-agents.md +6 -0
  44. package/guides/safety-and-trust.md +1 -0
  45. package/package.json +5 -1
  46. package/templates/node/package.json +1 -1
@@ -163,15 +163,28 @@ also supports OAuth, static headers, and request-local token resolution. Use
163
163
  [client registration](./auth.md#downstream-oauth-client-registration), and
164
164
  [request-local tokens](./auth.md#request-local-downstream-bearer-tokens)
165
165
  for those cases. Secrets belong in deployment secret storage or the vault,
166
- not source literals.
166
+ not source literals. For a first-party downstream that already trusts this
167
+ deployment, request auth can
168
+ [forward the admitted principal](./auth.md#forwarding-the-admitted-principal)
169
+ instead of asking each person to authorize it.
170
+
171
+ Optional `fetch` sends every MCP request for one connector through a
172
+ deployment-supplied function instead of global fetch, such as a Workers
173
+ service binding: `fetch: (input, init) => env.NOTES.fetch(input, init)`. `url`
174
+ still names the logical origin, and its HTTPS, redirect and same-origin rules
175
+ apply. `fetch` receives each request once with `redirect: "manual"` and must
176
+ not retry. It cannot be combined with OAuth auth. See
177
+ [over a Workers service binding](./auth.md#over-a-workers-service-binding).
167
178
 
168
179
  Use optional top-level `headers` for non-secret resource context:
169
180
  `remoteMcp("crm", { url: "https://crm.example/mcp", headers: {
170
181
  "X-MCP-Toolsets": "tickets,notes" }, readOnly: true })`. These headers apply to
171
182
  MCP handshake, catalog and call requests at the exact resource URL path, never OAuth
172
183
  metadata, token or registration requests. Values are included in cache and
173
- negotiation identities; values of at least eight characters are redacted if echoed. Configuration descriptions
174
- show only names. Never put credentials here. Use `auth` for secrets. Extra
184
+ negotiation identities. They are never treated as sent secrets: program and
185
+ direct-call arguments equal to a header value reach the downstream unchanged on
186
+ every call, and results, catalogs and errors that echo a value return it as
187
+ sent. Configuration descriptions show only names. Never put credentials here. Use `auth` for secrets. Extra
175
188
  headers cannot be combined with `auth.type: "headers"`, collide with an
176
189
  auth-owned name, or set HTTP transport/session headers
177
190
  (`Accept`, `Cookie`, `Host`, `Content-*`, hop-by-hop headers, `Last-Event-ID`,
@@ -205,7 +218,8 @@ optional and composes with endpoint policy.
205
218
  On Node, serve with `listen` from `@zackbart/connecta/node`: it installs the
206
219
  owned transport that prevents native fetch or a host-configured dispatcher
207
220
  from silently resending a remote MCP write. Without that transport, Node
208
- refuses remote writes before dispatch. Downstream OAuth token, registration and
221
+ refuses remote writes before dispatch. A connector with its own `fetch` hands
222
+ writes to that function instead, which owns their single attempt. Downstream OAuth token, registration and
209
223
  revocation POSTs also require the owned sender. Read calls and OAuth discovery
210
224
  GETs keep native fetch. Workers
211
225
  use their native fetch with manual redirects; transport failures after
@@ -43,6 +43,25 @@ clock, UTC, en-US, locally hosted fonts, disabled animations, hidden carets,
43
43
  and a 1280 × 900 viewport. Narrow fixtures use 390 × 844. Every test waits for
44
44
  its state and fonts before capturing.
45
45
 
46
+ ## Loading and recovery probes
47
+
48
+ `test/operator-contract-server.test.ts` counts physical legacy MCP initialize,
49
+ initialized, SSE GET and tools/list requests: cached admission and summary perform
50
+ none, then one detail per connector performs one session and complete listing.
51
+ The suite also checks default v2 compatibility, the unchanged member projection,
52
+ blocked custom connectors and bounded essential token-slot failure.
53
+ `test/ui.test.ts` checks complete detail schemas and independent sibling scope cleanup.
54
+
55
+ `test/oauth-stored-observation.test.ts` covers absent, disconnected, tokenless,
56
+ sealed/unreadable and issuer-mismatched grants, owner isolation, both built-in
57
+ connector paths and custom connectors. Storage spies require zero auth mutations,
58
+ including migration and resealing. `test/operator-store.test.ts` counts scoped
59
+ refresh/focus/mutation/poll requests, coalescing and bounded retries, and verifies
60
+ stale admission blocks privileged reads, connection writes and token actions while
61
+ retaining the snapshot. Late reads, role/identity changes and concurrent polls
62
+ exercise publication fences. Browser recovery and flow suites use real routes to
63
+ check a usable shell with a stalled connector, OAuth return and scoped Refresh.
64
+
46
65
  ## Page and state matrix
47
66
 
48
67
  Each row runs empty, loading, error, populated, and restricted-viewer fixtures
@@ -391,6 +391,21 @@ activity and access-token routes answer 404, and owners are `null`. Every respon
391
391
  allowed. One shape serves both roles; a field a role does not receive is
392
392
  `null` or empty, never omitted.
393
393
 
394
+ The browser uses `GET /ui/api/config?live=cached` for admission. For maintainers,
395
+ this mode performs no downstream status or catalog probes. `live.connectors`
396
+ contains only known observations, or `[]`; absence means unobserved, never healthy
397
+ or failed. The default URL and published v2 type remain unchanged. Members receive
398
+ their existing projection in either mode. Cached metadata reads have a 500 ms
399
+ budget each and the entire metadata build has a five-second budget, below the
400
+ browser's 15-second admission deadline. Essential token-slot failure still returns 503. Optional reads degrade conservatively. No downstream session spans requests.
401
+
402
+ Cached matrix counts use credential vault metadata or a built-in OAuth stored-auth
403
+ observation in each owner's partition. This boolean observation opens sealed grants
404
+ and checks token presence, disconnect state and issuer binding, without refreshing,
405
+ authorizing, migrating or writing auth. Unreadable grants and custom connectors
406
+ without this capability contribute an unconfirmed count of zero. For example,
407
+ `notes_alice` reporting zero connected people does not prove that nobody connected.
408
+
394
409
  The maintainer view carries:
395
410
 
396
411
  - `config`, the construction-time `describeConfig()` snapshot, including
@@ -507,6 +522,15 @@ exception: clearing a stored value that cannot be read is accepted although
507
522
  `manage` lists only `save` in that state. Public HTML shells and static assets carry no catalog data.
508
523
  Access-token management also requires an interactive session.
509
524
 
525
+ `GET /ui/connectors/:id` returns the existing auth controls plus an optional typed
526
+ `overlay`, all from the same status/catalog observation. The overlay contains
527
+ complete classified tools with input/output schemas, registration path, catalog
528
+ age, and activity-derived last-call facts under the reader's disclosure policy.
529
+ A failed or timed-out connector releases its own scope; siblings finish independently.
530
+ The browser publishes the detail and overlay atomically. Config observations never
531
+ overwrite a completed detail row. Each connector opens at most one live observation
532
+ per page load; the summary `/ui/data` performs no downstream probes.
533
+
510
534
  `GET /ui/api/config?connector=<id>` narrows the same contract to one connector
511
535
  before status and catalog probes. For a maintainer, config, provenance, live
512
536
  rows and matrix cells contain only that connector's facts; for a member,
@@ -545,52 +569,39 @@ no duplicate standalone server modules. The shared contract owns the unchanged
545
569
  `OperatorPage` union exported from `@zackbart/connecta/ui`. An Access-only shell selects
546
570
  Access even when the HTML request has no trusted context; only authenticated
547
571
  API responses establish the session. A gated Access session offers Retry.
548
- Focus, visibility, and timer-driven recovery share one admission-read schedule
549
- with at least two seconds between passive attempts. An admission read takes
550
- the contract first and, for a maintainer, then `/ui/data` and the connector
551
- details, which reuse that contract rather than read it again. An in-flight
552
- passive attempt coalesces returns until its admission read finishes, so slow
553
- successful reads can publish. Each admission read shares one 15-second
554
- deadline, including token acquisition, fetches and response bodies. This is an admission deadline, not a request
555
- lifetime limit: it releases the recovery guard and exposes Retry without aborting
556
- the pending read or invalidating its identity/revision fence. A late healthy
557
- response still publishes if that cycle is current. Passive timers and returns
558
- coalesce onto the pending cycle, including after its deadline; they never start
559
- duplicate summary, token, config, detail or body reads. The passive retry schedule
560
- stays bounded and does not reset its budget on a deadline. Retry remains available
561
- during recovery configuration loading and supersedes and aborts the old cycle;
562
- identity changes, navigation and connector-page unmount also cancel it. Navigation
563
- that cancels pending data schedules a bounded reread for the destination. A scoped OAuth poll publishes only its connector's detail and live row. It does
564
- not supersede a pending full admission or catalog/auth cycle, release its
565
- recovery guard, or clear its deadline. That full cycle still publishes other
566
- connectors as they settle and decides recovery, retaining the newer polled row
567
- even when its summary was pending or its deadline exposed Retry. Only a newer
568
- full cycle, mutation, identity change, or sign-out fences older work. Owed
569
- publication neither spends nor resets the failed-read retry budget and adds no
570
- timer. Failed reads retry after
571
- 2, 2, 4, and 8 seconds. Refusals preserve the throttle and retry budget. After
572
- those four automatic retries, a focus return can request one more read after
573
- the eight-second backoff, without replenishing the automatic budget. Retry
574
- resets the budget with a 150 ms click debounce. Successful reads and explicit
575
- identity transitions also reset recovery. Identity changes
576
- cancel pending retries and fence out responses from the previous identity.
577
- Queued focus callbacks carry the same identity fence. Explicit sign-out
578
- suppresses passive reads until an intentional sign-in or a new Clerk session.
579
-
580
- Catalog and connector auth details share one revisioned refresh cycle. Each
581
- completed connector publishes its catalog facts and auth controls together,
582
- while a slow provider leaves other rows usable.
583
- Catalog refresh, connector refresh, and mutation follow-up reads update both
584
- consumers together. A later refresh or mutation invalidates an older cycle's publication fence
585
- without aborting its read, and a failed contract read clears its old snapshot;
586
- the next refresh then reads the contract before any connector detail. After an explicit OAuth
587
- start, visible tabs poll only that connector's read-only catalog and auth facts
588
- every two seconds for up to 30 seconds. The scoped result replaces only that
589
- connector's matching live row and auth controls. Success, a failed read or status,
590
- disconnect, navigation away from the connector, unmount, or identity reset ends
591
- polling and aborts its in-flight reads. The deadline also aborts a stalled read.
592
- A later focus return can request another status read. Recovery never starts OAuth
593
- or replays a mutation.
572
+ Focus, visibility and timer-driven admission recovery share a schedule with at
573
+ least two seconds between passive attempts. Admission reads the cached contract
574
+ first, then `/ui/data` for maintainers. Token acquisition, fetches and response
575
+ bodies share a 15-second deadline. Once admission succeeds, connector details
576
+ have independent deadlines and cannot withdraw deployment-wide admission.
577
+
578
+ A passive admission deadline retains the last maintainer snapshot with a stale
579
+ notice. Admission validity is separate from snapshot presence: privileged reads,
580
+ connection writes and token actions require valid admission. A scoped connector
581
+ result cannot restore it. Queued reads withheld locally after admission expires
582
+ are discarded without clearing the snapshot or showing sign-in. A late full
583
+ admission may publish behind its original identity and revision fences. HTTP
584
+ 401/403 refusal, identity change and sign-out clear immediately;
585
+ member failures still remove their actionable projection. Retry aborts the old
586
+ admission cycle; navigation and identity transitions cancel its reads. Passive
587
+ attempts coalesce even after the deadline. Automatic admission retries remain
588
+ bounded at 2, 2, 4 and 8 seconds; a later focus return can try again after backoff.
589
+
590
+ Connector Refresh, focus recovery and mutation follow-ups read only affected
591
+ connectors. Focus probes unhealthy rows, never healthy siblings. All detail reads
592
+ share a four-request limit and coalesce per connector. Failures retry only their
593
+ connector with the same bounded backoff; recovery never replays a mutation.
594
+ Mutations cancel affected reads and advance their publication fence both at
595
+ dispatch and on settlement, including failure. Follow-ups require an observation
596
+ started after settlement, so a Refresh or retry started during the write cannot
597
+ supply its result. Newer publications also fence older results per connector,
598
+ while identity and full refresh revisions fence the complete view. An explicit
599
+ OAuth start polls only that connector every two seconds for up to 30 seconds.
600
+ Concurrent authorizations retain independent cancellation and publication. Success,
601
+ failed reads, disconnect, navigation, unmount and identity reset stop the matching
602
+ poll. Recovery never starts authorization. A scoped result cannot confirm identity,
603
+ role, token slots or any other deployment-wide admission fact.
604
+
594
605
  Unreadable credential metadata uses `configured: null`, including named fields,
595
606
  and renders as unavailable with a retry prompt. It does not claim that the slot
596
607
  is empty. Overview, Endpoints, Connectors, connector
@@ -547,7 +547,11 @@ LOADER: Cloudflare.WorkerLoader(),
547
547
  `@zackbart/connecta/worker`. The adapter constructs the upstream
548
548
  `DynamicWorkerExecutor` with only the loader and a deadline, and disposes each
549
549
  run's loader and RPC handles when its lease ends, even if the guest has not
550
- settled. It serves the six-tool surface. Do not add `bindings`, `modules`, or
550
+ settled. It serves the six-tool surface. Its admission pool is per isolate:
551
+ two runs, eight queued for up to 60 seconds, then a retryable
552
+ `executor_overloaded`. Pass `admission` to `workerExecutor()` to change it; see
553
+ [code mode](../../documentation/code-mode.md#deploy-time-capability). Do not
554
+ add `bindings`, `modules`, or
551
555
  `globalOutbound`; they grant guest code ambient authority. A copied deployment
552
556
  owns the package install — see
553
557
  [copied into its own repository](#copied-into-its-own-repository).
@@ -146,6 +146,12 @@ owner whose account the agent will use. See
146
146
  [Workspace pool owners](../documentation/auth.md#pool-owners-for-google-workspace)
147
147
  before connecting an agent to someone's account.
148
148
 
149
+ A connection to the organization's own service can instead act as whoever
150
+ signed in, by forwarding that person's identity. Agents cannot use such a
151
+ connection unless the operator also allows them, and then they appear to the
152
+ service as themselves, never as a person. See
153
+ [forwarding the admitted principal](../documentation/auth.md#forwarding-the-admitted-principal).
154
+
149
155
  An assistant serving several people must use a pool appropriate for everyone
150
156
  in the conversation. Its app chooses that pool; the model does not choose
151
157
  its own access level. Keep private results out of broader conversations.
@@ -141,6 +141,7 @@ own availability, project/environment choices, and revocation details.
141
141
  | Enable the credential vault | Encrypt stored credentials and OAuth tokens before connecting accounts. |
142
142
  | Pool membership and tools | Each member can use every action that pool grants. |
143
143
  | Service permissions | A shared credential uses the same account for every member. |
144
+ | Forwarded identity | A service that trusts a forwarded person must accept it only from this deployment. |
144
145
  | Approval policy | A writable pool can run unattended; the Eve helper defaults to that behavior. |
145
146
  | Tool classification | Labels must match the service's actual behavior. |
146
147
  | Deployment and secret handling | Enable the credential vault; protect storage, encryption keys, backups, ingress, and the agent host. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.36.1",
3
+ "version": "0.37.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.",
@@ -162,6 +162,10 @@
162
162
  "./worker": {
163
163
  "types": "./dist/worker.d.ts",
164
164
  "import": "./dist/worker.js"
165
+ },
166
+ "./google": {
167
+ "types": "./dist/google.d.ts",
168
+ "import": "./dist/google.js"
165
169
  }
166
170
  },
167
171
  "scripts": {
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@clerk/backend": "^3.12.0",
21
- "@zackbart/connecta": "0.36.1",
21
+ "@zackbart/connecta": "0.37.0",
22
22
  "quickjs-emscripten": "0.32.0",
23
23
  "undici": "6.29.1"
24
24
  },