@zackbart/connecta 0.36.2 → 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.
- package/CHANGELOG.md +23 -0
- package/bin/version.mjs +1 -1
- package/dist/auth/downstream-oauth.js +31 -12
- package/dist/auth/static-oauth.js +3 -2
- package/dist/auth/stored-observation.js +8 -0
- package/dist/call-admission.js +27 -14
- package/dist/connectors/api-connector.js +4 -0
- package/dist/connectors/option-shapes.js +5 -1
- package/dist/connectors/remote-mcp.d.ts +29 -3
- package/dist/connectors/remote-mcp.js +142 -22
- package/dist/execute.js +5 -5
- package/dist/executor-admission.js +27 -13
- package/dist/executors/quickjs.js +4 -1
- package/dist/index.d.ts +2 -1
- package/dist/invocation.js +6 -7
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +2 -0
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/request-principal.js +34 -0
- package/dist/request-token.d.ts +46 -0
- package/dist/routes/mcp.js +6 -6
- package/dist/routes/ui-config.js +38 -9
- package/dist/routes/ui-data.js +30 -5
- package/dist/routes/ui.js +10 -2
- package/dist/runtime/settlement.js +59 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +4 -1
- package/documentation/architecture.md +68 -25
- package/documentation/auth.md +129 -11
- package/documentation/code-mode.md +25 -5
- package/documentation/integrating.md +18 -4
- package/documentation/operator-tests.md +19 -0
- package/documentation/operator-ui.md +57 -46
- package/examples/worker/README.md +5 -1
- package/guides/connecting-agents.md +6 -0
- package/guides/safety-and-trust.md +1 -0
- package/package.json +1 -1
- package/templates/node/package.json +1 -1
package/documentation/auth.md
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
and package maintenance. This guide owns identity, membership, and downstream
|
|
7
7
|
credential contracts; use [deployment setup](./deploying.md) for runnable steps.
|
|
8
8
|
|
|
9
|
-
| Task | Sections
|
|
10
|
-
| ------------------------------------------------------------------- |
|
|
11
|
-
| Connect a machine or migrate an old client | [Agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens), [agent endpoints](#agent-endpoints), [static bearer migration](#migrating-static-bearer-clients)
|
|
12
|
-
| Configure caller visibility, read-only endpoints, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth)
|
|
13
|
-
| Lay out an agent's pools | [Organizing an agent's pools](#organizing-an-agents-pools)
|
|
14
|
-
| Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [Clerk Organizations](#one-clerk-instance-one-organization-per-deployment), [management](#human-authentication-management)
|
|
15
|
-
| Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting)
|
|
16
|
-
| Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say)
|
|
17
|
-
| Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens), [Google hosted MCP](#google-service-account-tokens-and-hosted-mcp)
|
|
18
|
-
| Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant)
|
|
9
|
+
| Task | Sections |
|
|
10
|
+
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| Connect a machine or migrate an old client | [Agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens), [agent endpoints](#agent-endpoints), [static bearer migration](#migrating-static-bearer-clients) |
|
|
12
|
+
| Configure caller visibility, read-only endpoints, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth) |
|
|
13
|
+
| Lay out an agent's pools | [Organizing an agent's pools](#organizing-an-agents-pools) |
|
|
14
|
+
| Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [Clerk Organizations](#one-clerk-instance-one-organization-per-deployment), [management](#human-authentication-management) |
|
|
15
|
+
| Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting) |
|
|
16
|
+
| Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say) |
|
|
17
|
+
| Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens), [principal forwarding](#forwarding-the-admitted-principal), [Google hosted MCP](#google-service-account-tokens-and-hosted-mcp) |
|
|
18
|
+
| Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant) |
|
|
19
19
|
|
|
20
20
|
[Credential recognition and admission](#credential-recognition-and-admission)
|
|
21
21
|
defines refusal precedence.
|
|
@@ -1336,6 +1336,29 @@ healthy. Restart invalidates it even if the connection ends healthy. Continue
|
|
|
1336
1336
|
trusts the stored registration's `redirect_uris`; after changing `publicUrl`,
|
|
1337
1337
|
Restart registers for the current callback URL.
|
|
1338
1338
|
|
|
1339
|
+
For `remoteMcp()` OAuth, the owner's stored grant decides whether a connection
|
|
1340
|
+
is connected, not whether the downstream answers. Some servers answer
|
|
1341
|
+
`initialize` and `tools/list` without credentials and challenge only
|
|
1342
|
+
`tools/call`. The operator status, the UI's connected state, and a start agree
|
|
1343
|
+
with the gate a call passes:
|
|
1344
|
+
|
|
1345
|
+
| Stored grant | Status | Start (Connect, Continue, Restart, `authorize_connector`) |
|
|
1346
|
+
| ------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1347
|
+
| Disconnected | `auth_required`; the downstream is not asked | Replaces the epoch, then proceeds as for no tokens |
|
|
1348
|
+
| No tokens | Connects; `auth_required` even when the downstream answers | Connects first, so a 401's `resource_metadata` starts consent as before. A downstream that answers without credentials still gets consent, discovered from RFC 9728 metadata at the configured URL's well-known locations. The start answers `auth_required` with the consent URL. |
|
|
1349
|
+
| Tokens (any expiry) | Connects; `ok`, or `auth_required` when a 401 needs consent | Connects; `ok` without starting a flow when healthy |
|
|
1350
|
+
|
|
1351
|
+
A start without a challenge runs the same SDK flow as a 401 without
|
|
1352
|
+
`resource_metadata`. Issuer and resource checks, consent state, PKCE, and
|
|
1353
|
+
the CIMD, registration, and static-client order are unchanged, and nothing is
|
|
1354
|
+
sent to the MCP endpoint. A call never starts consent. A call's 401 that names
|
|
1355
|
+
per-tool metadata, such as `/.well-known/oauth-protected-resource/<tool>`,
|
|
1356
|
+
renews through it when the grant holds no discovery. That metadata must name
|
|
1357
|
+
the configured resource, as any other must. When renewal is refused, the grant
|
|
1358
|
+
loses its tokens and the next start hands out consent. Restart (`force: true`)
|
|
1359
|
+
removes the tokens first, so it starts consent unless the downstream or
|
|
1360
|
+
discovery fails.
|
|
1361
|
+
|
|
1339
1362
|
### Consents and callbacks
|
|
1340
1363
|
|
|
1341
1364
|
A start stores its flow record, then the grant's pointer to it. A reset that
|
|
@@ -2648,12 +2671,18 @@ For providers offering both, deployment config chooses; handlers check `ctx.oaut
|
|
|
2648
2671
|
|
|
2649
2672
|
`remoteMcp("<id>", { auth: { type: "request", token: async (ctx) => … } })` resolves
|
|
2650
2673
|
its token before connecting and before reusing a client. The callback receives
|
|
2651
|
-
the operation's cancellation context
|
|
2674
|
+
the operation's cancellation context only. It never receives an agent-selected
|
|
2675
|
+
credential owner, and without the opt-in below it never receives the caller.
|
|
2652
2676
|
Only shared ownership is accepted. Tokens remain inside the request's hardened
|
|
2653
2677
|
transport, are not stored or described, and a changed token replaces the client.
|
|
2654
2678
|
This mode refuses cleartext non-loopback origins. Optional `auth.headers` carry
|
|
2655
2679
|
static protocol/catalog controls and cannot set Authorization.
|
|
2656
2680
|
|
|
2681
|
+
Return the same token for the same caller while it remains valid, and cache a
|
|
2682
|
+
minted token until shortly before it expires. A value that changes on every
|
|
2683
|
+
resolution replaces the client each time; a change that lands while a call
|
|
2684
|
+
connects fails that call with a retryable error.
|
|
2685
|
+
|
|
2657
2686
|
A resolver may expose `token.invalidate(rejectedToken, ctx): Promise<void>` to
|
|
2658
2687
|
remove a cached token after a downstream HTTP 401. The hook receives the exact
|
|
2659
2688
|
token sent by that transport, so it must leave a newer cached token intact.
|
|
@@ -2672,6 +2701,95 @@ is retained across requests, and rejected writes are never replayed. An omitted
|
|
|
2672
2701
|
`describe()` includes neither the key nor the minted tokens. The maintained
|
|
2673
2702
|
provider skill explains App permissions and installation prerequisites.
|
|
2674
2703
|
|
|
2704
|
+
### Forwarding the admitted principal
|
|
2705
|
+
|
|
2706
|
+
Set `auth.principal` instead of `auth.token` to let the resolver act for the
|
|
2707
|
+
caller inbound auth admitted. Use it only for a downstream the deployment
|
|
2708
|
+
controls and that already trusts this deployment, so the downstream needs no
|
|
2709
|
+
per-person OAuth:
|
|
2710
|
+
|
|
2711
|
+
```ts
|
|
2712
|
+
auth: {
|
|
2713
|
+
type: "request",
|
|
2714
|
+
principal: {
|
|
2715
|
+
accept: "human",
|
|
2716
|
+
token: async (ctx, { principal }) => await notesAssertion(principal),
|
|
2717
|
+
},
|
|
2718
|
+
},
|
|
2719
|
+
```
|
|
2720
|
+
|
|
2721
|
+
- `principal` is `{ kind: "human", namespace, id }`: the admitted person's
|
|
2722
|
+
stable reference, the same `{ namespace, id }` a pool member resolves to.
|
|
2723
|
+
Inbound auth admits no verified email or display name into it; map the id in
|
|
2724
|
+
deployment code or in the downstream when one is needed.
|
|
2725
|
+
- `accept: "human"` admits only people. Agents, anonymous callers,
|
|
2726
|
+
principal-less machine credentials and contexts no request admitted are
|
|
2727
|
+
refused with `auth_required` before the resolver runs. Agents still see the
|
|
2728
|
+
connector, and their calls fail. No identity is ever substituted.
|
|
2729
|
+
- `accept: "human-or-agent"` also admits declared agents as
|
|
2730
|
+
`{ kind: "agent", name, pool }`, the pool being the one the agent arrived on.
|
|
2731
|
+
An agent is never presented as a person, its pool's owner included.
|
|
2732
|
+
- The caller comes only from the authorization that admitted the request.
|
|
2733
|
+
Tool arguments, program code and request headers cannot choose or change it.
|
|
2734
|
+
- The principal joins the client's identity. A different principal never
|
|
2735
|
+
reuses a client, connection or connect in flight, even when the resolver
|
|
2736
|
+
returns the same token. `token.invalidate(rejected, ctx, { principal })`
|
|
2737
|
+
receives the principal the rejected token was minted for.
|
|
2738
|
+
- Catalog listing needs an admitted caller like any call, and its cache stays
|
|
2739
|
+
partitioned per caller. Operator status and catalog checks carry no caller,
|
|
2740
|
+
so they report `auth_required` without connecting.
|
|
2741
|
+
- The connector stays shared. Connecta keeps no per-principal credential state
|
|
2742
|
+
and never stores, logs or describes the principal or the token. `describe()`
|
|
2743
|
+
reports `mode: "request"` as for any request resolver.
|
|
2744
|
+
|
|
2745
|
+
#### Over a Workers service binding
|
|
2746
|
+
|
|
2747
|
+
Pair principal forwarding with the optional `fetch` to reach a sibling Worker
|
|
2748
|
+
through a service binding. Then neither Cloudflare Access, the public internet
|
|
2749
|
+
nor a service token lies between the two Workers:
|
|
2750
|
+
|
|
2751
|
+
```ts
|
|
2752
|
+
const notes = remoteMcp("notes", {
|
|
2753
|
+
url: "https://notes.example.com/mcp",
|
|
2754
|
+
fetch: (input, init) => env.NOTES.fetch(input, init),
|
|
2755
|
+
auth: {
|
|
2756
|
+
type: "request",
|
|
2757
|
+
principal: {
|
|
2758
|
+
accept: "human",
|
|
2759
|
+
token: async (_ctx, { principal }) => await notesAssertion(principal, env.NOTES_ASSERTION_KEY),
|
|
2760
|
+
},
|
|
2761
|
+
},
|
|
2762
|
+
});
|
|
2763
|
+
```
|
|
2764
|
+
|
|
2765
|
+
`notesAssertion` is deployment code. It signs a short-lived assertion naming
|
|
2766
|
+
`principal.namespace` and `principal.id` with a key both Workers hold, and
|
|
2767
|
+
caches it per principal until shortly before it expires. Over a binding, the
|
|
2768
|
+
token may instead be an unsigned principal reference, provided the downstream
|
|
2769
|
+
reads it only where the trust rule below holds.
|
|
2770
|
+
|
|
2771
|
+
The downstream must accept a forwarded identity only through its binding
|
|
2772
|
+
entry point or after verifying the signature. Bind to a named entrypoint, such
|
|
2773
|
+
as `{ "binding": "NOTES", "service": "notes", "entrypoint": "Connecta" }` in
|
|
2774
|
+
the deployment's Wrangler `services`, and read the identity only in that
|
|
2775
|
+
entrypoint. Never accept it on a public route: anyone who can reach that route
|
|
2776
|
+
could name any person. A signature verified with a deployment-held key is also
|
|
2777
|
+
safe on a public route.
|
|
2778
|
+
|
|
2779
|
+
`fetch` carries every MCP request, including handshakes, catalog reads, calls,
|
|
2780
|
+
writes and session close. Global fetch is never used for that connector. The
|
|
2781
|
+
configured `url` stays the logical origin: its HTTPS rule, cleartext refusal,
|
|
2782
|
+
redirect policy and same-origin redirect checks apply unchanged. A request for
|
|
2783
|
+
any other origin is refused before it reaches `fetch`. Each request, writes
|
|
2784
|
+
included, is handed to `fetch` once with `redirect: "manual"`, and a dispatched
|
|
2785
|
+
write is never sent again, so `fetch` must not retry or follow redirects. On
|
|
2786
|
+
Node, such writes do not need the owned transport. `fetch` cannot be combined
|
|
2787
|
+
with OAuth auth, because discovery, registration and token requests go to
|
|
2788
|
+
authorization servers the downstream names.
|
|
2789
|
+
|
|
2790
|
+
Contributor evidence: `test/remote-mcp-principal-forwarding.test.ts` runs on
|
|
2791
|
+
Node and, through a real service binding, on workerd.
|
|
2792
|
+
|
|
2675
2793
|
### Google service-account tokens and hosted MCP
|
|
2676
2794
|
|
|
2677
2795
|
Import `googleServiceAccountToken` from `@zackbart/connecta/google` to connect a
|
|
@@ -65,9 +65,15 @@ createConnecta({ executor: workerExecutor({ loader: env.LOADER }), readOnly: fal
|
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
The adapter uses only `loader` and an optional `timeout`. Its admission defaults
|
|
68
|
-
are two active runs, eight queued runs,
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
are two active runs, eight queued runs, a 60-second queue deadline (the default
|
|
69
|
+
guest deadline) and a one-second `retryAfterMs` hint. `admission` replaces all
|
|
70
|
+
four: `{ concurrency, maxQueueSize, queueTimeoutMs, retryAfterMs? }`, where an
|
|
71
|
+
omitted `retryAfterMs` equals `queueTimeoutMs`. The pool belongs to the executor
|
|
72
|
+
instance, so a Worker that builds one per isolate bounds each isolate, not the
|
|
73
|
+
deployment or a caller: concurrent requests that land on one isolate share its
|
|
74
|
+
slots, and requests on other isolates do not count. Lease release disposes RPC
|
|
75
|
+
and loader handles independently of guest settlement. `bindings`, `modules`, or
|
|
76
|
+
`globalOutbound` violate `P2`.
|
|
71
77
|
The [Worker example](../examples/worker/README.md#code-mode) has the full setup.
|
|
72
78
|
|
|
73
79
|
Keep the Worker's optional peer. Unbranded executors throw, including bundled upstream copies.
|
|
@@ -981,7 +987,21 @@ payload is refused before parsing and names no address.
|
|
|
981
987
|
plus a bounded queue with a wait timeout. Overload is a retryable
|
|
982
988
|
`executor_overloaded` carrying `retryAfterMs`; cancellation and shutdown are
|
|
983
989
|
terminal. Admission happens _before_ any catalog or provider is built, so a
|
|
984
|
-
queued request holds no state.
|
|
990
|
+
queued request holds no state. A full queue refuses at once with
|
|
991
|
+
`Executor queue is full.`; a queued run that outlives `queueTimeoutMs` is
|
|
992
|
+
refused with `Executor admission timed out after <n>ms.`. Either arrives as an
|
|
993
|
+
error result whose `structuredContent.error` is
|
|
994
|
+
`{ code: "executor_overloaded", message, retryable: true, retryAfterMs }`, with
|
|
995
|
+
`hostCalls` all zero; the program never started, so retrying it after
|
|
996
|
+
`retryAfterMs` sends nothing twice. Whichever request releases a slot, the
|
|
997
|
+
queued run resumes, builds its providers, makes its host calls and holds its
|
|
998
|
+
sandbox handles in its own request; the releasing request's result is
|
|
999
|
+
unaffected. A slot handed to a queued run is heard a turn after it is granted.
|
|
1000
|
+
When the executor closes in that gap, the slot is given back and the run is
|
|
1001
|
+
refused with `executor_closed`, so nothing granted before `close()` starts after
|
|
1002
|
+
it. A grant that beat the caller's abort stays the caller's, which releases it
|
|
1003
|
+
on arrival. A queued run also holds its MCP request permit
|
|
1004
|
+
([request admission](./architecture.md#admission-in-two-places)) while it waits.
|
|
985
1005
|
|
|
986
1006
|
**L8.** Result, log, host-call, and emission budgets are deployment configuration.
|
|
987
1007
|
A call may request `timeoutMs` just as a direct call does; the guest deadline is
|
|
@@ -1287,7 +1307,7 @@ rejection, and branded adapter acceptance across module copies.
|
|
|
1287
1307
|
| `L1`, `L2` | `test/guest-api-contract.test.ts` (in-flight call fails `cancelled`), `test/execute.test.ts` (cancels outstanding host calls, discovery included, and refuses discovery after the run; a cancelled wedged executor, or one whose `acquire()` ignores the signal, returns promptly and releases its lease) |
|
|
1288
1308
|
| `L3`, `X1` | `test/guest-api-contract.test.ts` (short-deadline executors), `test/execute.test.ts` (the watchdog ends a never-settling executor, frees the default pool, spares a slow run, and falls back from an unusable value) |
|
|
1289
1309
|
| `L4`, `L8` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (shared discovery/call budgets and terminal catch-and-continue loops and queued-write cancellation on both executors), `test/worker-budget-response.node.test.ts` (native handle disposal and admission recovery across completed HTTP responses) |
|
|
1290
|
-
| `L5`, `L7`, `X2` | `test/quickjs-executor.node.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue)
|
|
1310
|
+
| `L5`, `L7`, `X2` | `test/quickjs-executor.node.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue), `test/executor-requests.test.ts` (queued handoffs and refusal shape across separate workerd requests), `test/request-context.node.test.ts` (queued and QuickJS host-call request context) |
|
|
1291
1311
|
| `L6`, `X10` | `test/guest-api-contract-quickjs.node.test.ts` and `test/guest-api-contract.test.ts` (whole oversized reads and one-write pageable handles), `test/sqlite-storage.node.test.ts` and `test/kv-to-d1.test.ts` (SQL stash and native D1 fault probes), `test/quickjs-executor.node.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message), `test/quickjs-child-stderr.node.test.ts` (outer reply serialization failure settles the call) |
|
|
1292
1312
|
| `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no event for the execution itself), `test/activity.test.ts` (the shared code → friction table, the identity clamp, the one-attempt floor), `test/browser/operator-ui.spec.ts` (identity changes), `test/sql-storage-contract.ts`, run by `test/sqlite-storage.node.test.ts` (historical activity rows still round-trip) |
|
|
1293
1313
|
| `V5`, `W9` | `test/program-writes.test.ts` (unawaited program drain, failed-program accounting), `test/identity-write-journey.node.test.ts` (committed ambiguity, reconciliation and physical writes), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
|
|
@@ -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
|
|
174
|
-
|
|
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.
|
|
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
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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.
|
|
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.
|
|
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.",
|