@zackbart/connecta 0.31.0 → 0.32.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 +34 -0
- package/README.md +15 -13
- package/bin/connecta.mjs +1 -0
- package/bin/version.mjs +1 -1
- package/dist/auth-elicitation.js +1 -1
- package/dist/catalog-service.js +7 -2
- package/dist/config-value-sources.js +6 -3
- package/dist/config.d.ts +26 -8
- package/dist/config.js +38 -16
- package/dist/connector-access.d.ts +2 -1
- package/dist/connectors/guarded-fetch.d.ts +9 -0
- package/dist/connectors/guarded-fetch.js +7 -0
- package/dist/describe-config.d.ts +6 -3
- package/dist/describe-config.js +3 -2
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -0
- package/dist/execute.js +16 -14
- package/dist/index.js +4 -3
- package/dist/invocation.js +7 -4
- package/dist/meta-tools.js +17 -19
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +2 -0
- package/dist/operator-ui/contract.d.ts +5 -4
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +1 -1
- package/dist/operator-ui/view.js +1 -0
- package/dist/program-writes.js +2 -2
- package/dist/provider.d.ts +2 -3
- package/dist/providers/_shared/google/workspace.js +1 -1
- package/dist/providers/breeze/skill.generated.js +2 -2
- package/dist/providers/cloudflare/skill.generated.js +1 -1
- package/dist/providers/docs/index.d.ts +1 -1
- package/dist/providers/docs/index.js +2 -2
- package/dist/providers/drive/index.d.ts +1 -1
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/forms/index.d.ts +1 -1
- package/dist/providers/forms/index.js +2 -2
- package/dist/providers/github/skill.generated.js +2 -2
- package/dist/providers/gmail/index.d.ts +1 -1
- package/dist/providers/gmail/index.js +274 -23
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/linear/index.js +1 -1
- package/dist/providers/mixpanel/index.d.ts +27 -12
- package/dist/providers/mixpanel/index.js +58 -6
- package/dist/providers/mixpanel/operations.js +20 -0
- package/dist/providers/mixpanel/rest.js +603 -0
- package/dist/providers/mixpanel/skill.generated.js +3 -2
- package/dist/providers/mixpanel/value-safety.js +16 -0
- package/dist/providers/notion/index.js +16 -0
- package/dist/providers/notion/rest.js +138 -19
- package/dist/providers/notion/skill.generated.js +4 -4
- package/dist/providers/planning-center/index.d.ts +27 -0
- package/dist/providers/planning-center/index.js +286 -24
- package/dist/providers/planning-center/skill.generated.js +24 -4
- package/dist/providers/sheets/index.d.ts +1 -1
- package/dist/providers/sheets/index.js +3 -3
- package/dist/providers/slides/index.d.ts +1 -1
- package/dist/providers/slides/index.js +3 -3
- package/dist/providers/stripe/skill.generated.js +2 -2
- package/dist/providers/vercel/index.js +2 -2
- package/dist/providers/vercel/skill.generated.js +2 -2
- package/dist/providers/wispr-flow/index.d.ts +14 -0
- package/dist/providers/wispr-flow/index.js +117 -0
- package/dist/providers/wispr-flow/skill.generated.js +8 -0
- package/dist/routes/mcp.js +30 -10
- package/dist/routes/operator.js +14 -7
- package/dist/routes/ui-config.js +2 -2
- package/dist/server.js +3 -0
- package/dist/skills.js +5 -4
- package/dist/tool-safety.js +13 -5
- package/dist/types.d.ts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +1 -1
- package/dist/usage-guide.js +3 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +38 -23
- package/documentation/auth.md +86 -27
- package/documentation/code-mode.md +81 -72
- package/documentation/deploying.md +10 -5
- package/documentation/integrating.md +56 -4
- package/documentation/meta-tools.md +44 -37
- package/documentation/operating.md +11 -8
- package/documentation/operator-tests.md +2 -3
- package/documentation/operator-ui.md +9 -3
- package/documentation/provider-auth-migration.md +5 -4
- package/documentation/provider-migration-0.29.md +1 -1
- package/examples/worker/AGENTS.md +5 -0
- package/examples/worker/src/connecta.config.ts +5 -0
- package/package.json +5 -1
- package/templates/node/AGENTS.md +6 -4
- package/templates/node/README.md +4 -2
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +5 -0
- package/dist/tool-safety.d.ts +0 -9
|
@@ -48,7 +48,7 @@ QuickJS subpath:
|
|
|
48
48
|
import { createConnecta } from "@zackbart/connecta";
|
|
49
49
|
import { quickJsExecutor } from "@zackbart/connecta/quickjs";
|
|
50
50
|
|
|
51
|
-
createConnecta({ executor: quickJsExecutor() /* connectors, auth, storage… */ });
|
|
51
|
+
createConnecta({ executor: quickJsExecutor(), readOnly: false /* connectors, auth, storage… */ });
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
`quickJsExecutor()` runs each program in a disposable child process, with CPU,
|
|
@@ -61,7 +61,7 @@ On Workers the Worker Loader is the sandbox; Dynamic Workers require the Paid pl
|
|
|
61
61
|
```ts
|
|
62
62
|
import { workerExecutor } from "@zackbart/connecta/worker";
|
|
63
63
|
|
|
64
|
-
createConnecta({ executor: workerExecutor({ loader: env.LOADER }) });
|
|
64
|
+
createConnecta({ executor: workerExecutor({ loader: env.LOADER }), readOnly: false /* … */ });
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
The adapter uses only `loader` and an optional `timeout`. Its admission defaults
|
|
@@ -283,7 +283,10 @@ signature is byte-identical to the one `search_tools` returns.
|
|
|
283
283
|
integration is ambiguous. `safety: "readOnly"` selects stored read verdicts;
|
|
284
284
|
`"approvalRequired"` selects stored write verdicts; omitted or `"all"` selects
|
|
285
285
|
both. Search and describe rows carry `classification`. These filters grant no
|
|
286
|
-
authority and change no
|
|
286
|
+
authority and change no call admission decision. On a read-only endpoint,
|
|
287
|
+
search and describe omit write tools (`W12`): a write is absent from every
|
|
288
|
+
filter, and describing one is `unknown_tool`. `connecta.skill` guides are not
|
|
289
|
+
filtered and may name such a write; calling it is still refused.
|
|
287
290
|
|
|
288
291
|
**S2.** A requested object schema carries `inputKeys`, `requiredInputKeys`
|
|
289
292
|
(declared properties only), and `outputKeys`: the names the rendered schema
|
|
@@ -394,10 +397,10 @@ and a 32 KiB bridge ceiling. `page` is zero-based and mutually exclusive with
|
|
|
394
397
|
are at least four bytes wide and align both edges to UTF-8 boundaries.
|
|
395
398
|
Follow `nextOffset`, reassemble inside the program, and return a reduced value.
|
|
396
399
|
Before returning a page, the host rechecks current authentication, connector/tool
|
|
397
|
-
grants, pool membership, and endpoint
|
|
398
|
-
tool classification. A read-only endpoint cannot page a
|
|
399
|
-
|
|
400
|
-
|
|
400
|
+
grants, pool membership, and whether the endpoint is read-only against both the
|
|
401
|
+
original and current tool classification. A read-only endpoint cannot page a
|
|
402
|
+
write result; it never runs a write, so any write result it is asked for came
|
|
403
|
+
from a writable endpoint. Binding
|
|
401
404
|
mismatches, revoked access, and old entries without bindings are `not_found`, as
|
|
402
405
|
are unknown or expired ids; a storage failure is `unavailable`. IDs are random UUIDs.
|
|
403
406
|
The `get_result` meta-tool is removed.
|
|
@@ -477,7 +480,7 @@ Connecta passes an admitted decoded URI as an opaque MCP `resources/read`
|
|
|
477
480
|
argument to the configured endpoint. It never fetches that
|
|
478
481
|
URI or creates a connector from it.
|
|
479
482
|
|
|
480
|
-
Resources are protocol reads
|
|
483
|
+
Resources are protocol reads on read-only and writable endpoints alike. A read
|
|
481
484
|
requires a grant to the whole connector through the endpoint/pool and identity
|
|
482
485
|
intersection. Exact-tool grants, including guarded grants, authorize no
|
|
483
486
|
resources. An absent, hidden, tool-restricted or resource-incapable connector
|
|
@@ -553,27 +556,27 @@ URL-elicitation hosts retain their existing flow.
|
|
|
553
556
|
may do. A provider maps each downstream failure to the code that tells the
|
|
554
557
|
caller what to do next, and never invents a cause it was not told.
|
|
555
558
|
|
|
556
|
-
| Code
|
|
557
|
-
|
|
|
558
|
-
| `unknown_address`
|
|
559
|
-
| `unknown_tool`
|
|
560
|
-
| `
|
|
561
|
-
| `auth_required`
|
|
562
|
-
| `invalid_args`
|
|
563
|
-
| `not_found`
|
|
564
|
-
| `conflict`
|
|
565
|
-
| `input_required_unsupported`
|
|
566
|
-
| `rate_limited`
|
|
567
|
-
| `unavailable`
|
|
568
|
-
| `timeout`
|
|
569
|
-
| `program_error`
|
|
570
|
-
| `cancelled`
|
|
571
|
-
| `connector_call_failed`
|
|
572
|
-
| `catalog_lookup_failed`
|
|
573
|
-
| `result_processing_failed`
|
|
574
|
-
| `result_too_large`
|
|
575
|
-
| `budget_exceeded`
|
|
576
|
-
| `write_outcome_unknown`
|
|
559
|
+
| Code | Raised when | `retryable` |
|
|
560
|
+
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
|
561
|
+
| `unknown_address` | no visible connector owns the tool address or grants the resource reader | false |
|
|
562
|
+
| `unknown_tool` | the connector has no such tool | false |
|
|
563
|
+
| `read_only_endpoint` | a program on a read-only endpoint attempted a write (`E4`) | false |
|
|
564
|
+
| `auth_required` | the credential is missing, expired, or rejected | false |
|
|
565
|
+
| `invalid_args` | arguments or discovery bounds were rejected | false |
|
|
566
|
+
| `not_found` | the downstream answered and the resource is not there — the one code that says skip this id rather than stop, raised only where the provider tells absence from a permission gap | false |
|
|
567
|
+
| `conflict` | the write named a base version someone else already moved past; nothing changed. `details.current` says where things stand (at most 20 whole-number entries) — re-read, reapply, retry with that base | false |
|
|
568
|
+
| `input_required_unsupported` | a downstream asked for input; use `nextAction` for the equivalent direct call | false |
|
|
569
|
+
| `rate_limited` | the downstream reported a rate limit | true |
|
|
570
|
+
| `unavailable` | the downstream is down or unreachable; optional sanitized `details.host` and `details.code` describe the transport failure without paths, queries, credentials, or provider prose | true |
|
|
571
|
+
| `timeout` | a call or sandbox deadline expired; details name operation, stage, elapsedMs, and deadlineMs | per operation |
|
|
572
|
+
| `program_error` | guest JavaScript failed; `details.name`, `line`, and a fixed repair hint describe it | false |
|
|
573
|
+
| `cancelled` | the run ended while this call was in flight (`E5`) | false |
|
|
574
|
+
| `connector_call_failed` | anything else the connector threw | per message |
|
|
575
|
+
| `catalog_lookup_failed` | the connector's catalog could not be loaded | per cause |
|
|
576
|
+
| `result_processing_failed` | the result could not be prepared | per message |
|
|
577
|
+
| `result_too_large` | a discovery response exceeded its byte bound | false |
|
|
578
|
+
| `budget_exceeded` | the run exhausted a host-call, write, or emitted-output budget | false |
|
|
579
|
+
| `write_outcome_unknown` | a program write was sent and no answer came back; it is never sent again (`W9`) | false |
|
|
577
580
|
|
|
578
581
|
**E3.** `auth_required` carries the same recovery envelope as `call_tool`:
|
|
579
582
|
`connector`, `operation`, `recovery` (`oauth`, `operator_config`, or
|
|
@@ -581,16 +584,13 @@ caller what to do next, and never invents a cause it was not told.
|
|
|
581
584
|
sentence. A program cannot recover credentials — only an operator can — so stop
|
|
582
585
|
and let the failure reach the model.
|
|
583
586
|
|
|
584
|
-
**E4.** A tool classified as a write never runs
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
call is the approval, and the only one: generated code can neither mint the
|
|
592
|
-
capability nor approve its own write, and the model's short `reason` grants no
|
|
593
|
-
authority and never goes downstream.
|
|
587
|
+
**E4.** A tool classified as a write never runs on a read-only endpoint
|
|
588
|
+
(`W12`). A program's call is refused with `read_only_endpoint` before
|
|
589
|
+
validation and before anything is sent. The refusal carries no `nextAction` and
|
|
590
|
+
echoes no arguments: no route on that endpoint writes, so the program reports
|
|
591
|
+
it rather than retrying. Generated code can neither mint the capability nor
|
|
592
|
+
change the endpoint; on a writable endpoint, approval of the program as a write
|
|
593
|
+
belongs to the client.
|
|
594
594
|
|
|
595
595
|
**E5.** Failures of the _execution_, not of a call, never appear inside the
|
|
596
596
|
guest: admission rejection (`executor_overloaded`, retryable, with
|
|
@@ -631,7 +631,7 @@ Program diagnostic fields are bounded before transport, including escaped text;
|
|
|
631
631
|
a large custom error name cannot turn a failure into a truncated success.
|
|
632
632
|
|
|
633
633
|
**E7.** `retryable` for `unknown_address`, `unknown_tool`, and
|
|
634
|
-
`
|
|
634
|
+
`read_only_endpoint` is pinned false, never inferred from an
|
|
635
635
|
address containing `503`, `429`, or `temporar`. The first two carry
|
|
636
636
|
`nextAction: { function: "connecta.search", arguments: { query, connector?, includeSchemas: "compact" } }`
|
|
637
637
|
— scoped discovery keyed to the surface the caller has, since a program cannot
|
|
@@ -686,7 +686,7 @@ Caught call failures retain that connector visibility when catalog loading or
|
|
|
686
686
|
tool resolution fails. Recovery messages listing configured connectors or
|
|
687
687
|
local guides retain visibility of those contributors too.
|
|
688
688
|
Each page rechecks current auth,
|
|
689
|
-
grants, pool membership, and
|
|
689
|
+
grants, pool membership, and the endpoint's `readOnly`. Reading a stash inside a program carries
|
|
690
690
|
its dependencies forward to any new return stash. The page cap is the smallest
|
|
691
691
|
of the deployment cap and contributing connectors' effective `maxResultBytes`.
|
|
692
692
|
Read-only endpoints cannot page write results. A UUID is a handle, never a grant.
|
|
@@ -882,17 +882,17 @@ deadline past the ceiling means raising the ceiling too.
|
|
|
882
882
|
**L4.** Per-execution bounds that are contract, identical in both executors
|
|
883
883
|
because connecta enforces them above the sandbox:
|
|
884
884
|
|
|
885
|
-
| Bound
|
|
886
|
-
|
|
|
887
|
-
| Host calls per execution, shared by `search`, `describe`, `call`, and `read`
|
|
888
|
-
|
|
|
889
|
-
| Deadline per host call
|
|
890
|
-
| Discovery page
|
|
891
|
-
| `describe` addresses
|
|
892
|
-
| `describe` nearby suggestions
|
|
893
|
-
| Caller text echoed by `describe` recovery
|
|
894
|
-
| Result
|
|
895
|
-
| Logs presented to the model
|
|
885
|
+
| Bound | Value |
|
|
886
|
+
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
887
|
+
| Host calls per execution, shared by `search`, `describe`, `call`, and `read` | 20 by default, `execute.maxHostCalls` |
|
|
888
|
+
| Writes per run on a writable endpoint, on top of the host calls they also spend (`W10`) | 10 by default, `execute.maxWrites` |
|
|
889
|
+
| Deadline per host call | 15 s, `execute.hostCallTimeoutMs`; one deadline covers catalog resolution, admission, and the connector call |
|
|
890
|
+
| Discovery page | ≤ 100 tools, ≤ 256,000 serialized bytes |
|
|
891
|
+
| `describe` addresses | ≤ 100 |
|
|
892
|
+
| `describe` nearby suggestions | ≤ 3 canonical addresses per failed entry |
|
|
893
|
+
| Caller text echoed by `describe` recovery | ≤ 512 UTF-8 bytes per field, plus `…` |
|
|
894
|
+
| Result | 24,000 serialized characters |
|
|
895
|
+
| Logs presented to the model | 4,000 characters |
|
|
896
896
|
|
|
897
897
|
Every `call`, `read`, `search`, and `describe` spends one host call on entry, before
|
|
898
898
|
resolution, validation, or dispatch. Catching a local refusal refunds nothing.
|
|
@@ -901,7 +901,7 @@ The first call beyond the budget ends the host run with one non-retryable
|
|
|
901
901
|
`try/catch` cannot swallow it. Later host access, including `emit`, stops; pending
|
|
902
902
|
replies are withheld. The refusal reaches no connector, returns no partial result,
|
|
903
903
|
and discards accepted emits (`M4`). Calls still resolving or awaiting admission
|
|
904
|
-
are cancelled, including
|
|
904
|
+
are cancelled, including writes past their gate. Dispatched writes
|
|
905
905
|
drain under `W9`; unknown outcomes retain precedence. The lease then releases:
|
|
906
906
|
QuickJS terminates its child, and the Worker adapter disposes RPC and loader
|
|
907
907
|
handles independently of the guest deadline. Workers guest computation may
|
|
@@ -1003,22 +1003,22 @@ payload-free _by construction_ means the event has nowhere to put a payload.
|
|
|
1003
1003
|
distinctive output is the program source — exactly what a payload-free history
|
|
1004
1004
|
must never keep.
|
|
1005
1005
|
|
|
1006
|
-
**V5.** A
|
|
1006
|
+
**V5.** A write a program issues after it has already returned is not
|
|
1007
1007
|
sent and records nothing: it was never an attempt (`W9`).
|
|
1008
1008
|
|
|
1009
1009
|
## Writes
|
|
1010
1010
|
|
|
1011
|
-
|
|
1012
|
-
`
|
|
1013
|
-
|
|
1014
|
-
|
|
1011
|
+
Whether an endpoint writes belongs to deployment code. Every deployment sets
|
|
1012
|
+
`readOnly` for `/mcp` explicitly; construction fails without it. Each named pool inherits
|
|
1013
|
+
the deployment's `readOnly` unless it sets its own, in either direction. The
|
|
1014
|
+
client owns approval; Connecta answers one question per endpoint: can it write.
|
|
1015
1015
|
|
|
1016
1016
|
```ts
|
|
1017
1017
|
createConnecta({
|
|
1018
1018
|
connectors, executor,
|
|
1019
|
-
|
|
1019
|
+
readOnly: false, // required: false writes, true refuses every write call
|
|
1020
1020
|
pools: {
|
|
1021
|
-
|
|
1021
|
+
reporting: { tools: ["posthog"], readOnly: true, grant },
|
|
1022
1022
|
},
|
|
1023
1023
|
classification: {
|
|
1024
1024
|
posthog: { exec: "read" }, // only for a deployment with a read-only exec contract
|
|
@@ -1027,6 +1027,9 @@ createConnecta({
|
|
|
1027
1027
|
});
|
|
1028
1028
|
```
|
|
1029
1029
|
|
|
1030
|
+
The removed `trust` option, on the deployment or a pool, fails construction
|
|
1031
|
+
with a message naming `readOnly` (`INV-11`).
|
|
1032
|
+
|
|
1030
1033
|
The registry computes one `classifyTool` verdict on each request-scoped
|
|
1031
1034
|
published catalog entry. It caches and persists only raw downstream facts.
|
|
1032
1035
|
Every caller consumes that stored verdict. Config cannot classify an individual
|
|
@@ -1044,7 +1047,7 @@ fail construction; unknown remote tool keys fail publication of the whole
|
|
|
1044
1047
|
catalog. Overrides are exact tool names, including names containing dots.
|
|
1045
1048
|
They are never inferred from descriptions or arguments.
|
|
1046
1049
|
|
|
1047
|
-
**W9.** Programs
|
|
1050
|
+
**W9.** Programs on a writable endpoint may write. A dispatched write is awaited even
|
|
1048
1051
|
if the program does not await it, bounded by its host-call deadline. A write
|
|
1049
1052
|
that reaches accounting after the program settles is not sent. An unanswered
|
|
1050
1053
|
write makes the run fail `write_outcome_unknown` with
|
|
@@ -1054,7 +1057,7 @@ write is automatically retried, including an ambiguous timeout. A downstream
|
|
|
1054
1057
|
refusal or a tool's `isError` can establish failure; a timeout or an unanswered
|
|
1055
1058
|
transport failure leaves the outcome unknown.
|
|
1056
1059
|
|
|
1057
|
-
**W10.** `execute.maxWrites`, default 10, bounds
|
|
1060
|
+
**W10.** `execute.maxWrites`, default 10, bounds a program's writes in
|
|
1058
1061
|
addition to the host-call budget. It is checked after argument validation and
|
|
1059
1062
|
before admission, so an over-budget write spends no permit.
|
|
1060
1063
|
|
|
@@ -1067,14 +1070,20 @@ reconciliation. Any new call must use the original arguments when its echo is
|
|
|
1067
1070
|
partial or absent. A returned `writes` count cannot make a run safe to repeat.
|
|
1068
1071
|
See [argument echo rules](./meta-tools.md#echo-budgets) for supported schema shapes.
|
|
1069
1072
|
|
|
1070
|
-
**W12.**
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
`
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1073
|
+
**W12.** Writable endpoints annotate `execute_code` as a write
|
|
1074
|
+
(`readOnlyHint: false, destructiveHint: true`) and permit program writes and
|
|
1075
|
+
`call_destructive_tool`; `call_tool` still accepts only reads and refuses a write
|
|
1076
|
+
with `destructive_tool_requires_approval`. Read-only endpoints annotate
|
|
1077
|
+
`execute_code` as read-only (`readOnlyHint: true, destructiveHint: false`),
|
|
1078
|
+
omit write tools from `search_tools`, `connecta.search`, and
|
|
1079
|
+
`connecta.describe`, and refuse every write call through `call_tool`,
|
|
1080
|
+
`call_destructive_tool`, and programs with `read_only_endpoint` before
|
|
1081
|
+
validation or dispatch. Guides and skills are documentation and may still name
|
|
1082
|
+
those tools. `tools/list` is the same six tools on both. Approval belongs to the client.
|
|
1083
|
+
`readOnly` grants no tool or connector access and cannot be set by guest code,
|
|
1084
|
+
request arguments, or a downstream catalog. `execute.approval`,
|
|
1085
|
+
`Connector.approval`, per-tool approval exemptions, and pool `trust` are
|
|
1086
|
+
removed. `W1`–`W8` and `W11` remain retired.
|
|
1078
1087
|
|
|
1079
1088
|
## Executor exceptions
|
|
1080
1089
|
|
|
@@ -1225,8 +1234,8 @@ rejection, and branded adapter acceptance across module copies.
|
|
|
1225
1234
|
| `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) |
|
|
1226
1235
|
| `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/d1-storage.node.test.ts` (shared stash transfers), `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) |
|
|
1227
1236
|
| `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/operator-view.test.ts`, `test/sql-storage-contract.ts`, run by `test/d1-storage.node.test.ts` and `test/sqlite-storage.node.test.ts` (historical pause and approval rows still render and round-trip) |
|
|
1228
|
-
| `V5`, `W9` | `test/program-writes.test.ts` (an unawaited
|
|
1229
|
-
| `W10`, `W12` | `test/program-writes.test.ts` (a
|
|
1237
|
+
| `V5`, `W9` | `test/program-writes.test.ts` (an unawaited program write finished and recorded, its unknown outcome reported, counts on a failed program, the classification table), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
|
|
1238
|
+
| `W10`, `W12` | `test/program-writes.test.ts` (a writable endpoint's program write runs, a read-only endpoint omits writes from search and describe and refuses every write call on every path, including a write its guide names, a required deployment `readOnly`, the write budget, `readOnly` inheritance and overrides, `call_tool` still refusing, search and describe verdicts, construction refusals including retired `trust`), `test/operator-ui-model.test.ts`, `test/browser/operator-ui.spec.ts` (the badge) |
|
|
1230
1239
|
| `M1` | `test/guest-api-contract.test.ts` (invalid emits throw catchably, accept nothing), `test/execute-emit.test.ts` (rejected shapes, bounded native snapshots, paging before metadata traversal, and cyclic presentation refusal) |
|
|
1231
1240
|
| `M2`, `M3` | `test/guest-api-contract.test.ts` (delivery order, truncated return plus delivered blocks), `test/execute-emit.test.ts` (envelope, `structuredContent`, byte-for-byte no-emit path) |
|
|
1232
1241
|
| `M4` | `test/guest-api-contract.test.ts` (discard is visible), `test/execute-emit.test.ts` (structured and plain paths), `test/quickjs-executor.node.test.ts` (mid-run shutdown) |
|
|
@@ -64,7 +64,7 @@ through to `fetch`. The example owns those details and
|
|
|
64
64
|
|
|
65
65
|
## Compose capabilities and access
|
|
66
66
|
|
|
67
|
-
Both configurations use `defineConfig((env) => …)
|
|
67
|
+
Both configurations use `defineConfig((env) => …)`, a required executor, and a required `readOnly` choice.
|
|
68
68
|
[Integrating services](./integrating.md) explains the `connectors` array.
|
|
69
69
|
Configuration selects optional factories through explicit slots:
|
|
70
70
|
|
|
@@ -96,10 +96,15 @@ personal connection, token management, and activity access explicit.
|
|
|
96
96
|
[Clerk setup](./auth.md#clerk-oauth-tokens-and-operator-sessions) owns resource-bound
|
|
97
97
|
tokens and `aud_claim_enabled`. A person's operator session is not an MCP token.
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
Every deployment must set `readOnly` in its configuration; startup fails without
|
|
100
|
+
it, and the template and Worker example set `readOnly: false`. Each named pool
|
|
101
|
+
inherits that value unless it sets its own. A writable endpoint (`false`) allows
|
|
102
|
+
writes through `call_destructive_tool` and programs and marks `execute_code` as
|
|
103
|
+
a write; the client decides what to approve. A read-only endpoint (`true`) omits
|
|
104
|
+
write tools from search and describe and refuses every write call; provider
|
|
105
|
+
guides may still name those tools. Pool membership intersects
|
|
106
|
+
the caller's grants; it never widens them. See [pools](./auth.md#pools) before
|
|
107
|
+
changing `readOnly`.
|
|
103
108
|
|
|
104
109
|
## Verify and diagnose
|
|
105
110
|
|
|
@@ -35,7 +35,7 @@ const tracker = linear("tracker", {
|
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
Linear defaults to OAuth; its read-only endpoint requests only the downstream
|
|
38
|
-
read scope. That is separate from Connecta
|
|
38
|
+
read scope. That is separate from a Connecta `readOnly` endpoint. Read the
|
|
39
39
|
[Linear skill](https://github.com/zackbart/connecta/blob/main/src/providers/linear/SKILL.md)
|
|
40
40
|
and [factory options](https://github.com/zackbart/connecta/blob/main/src/providers/linear/index.ts)
|
|
41
41
|
for API-key framing, workspace instructions, and admission choices.
|
|
@@ -49,6 +49,15 @@ mapping from admitted identity to the downstream account; a request must not
|
|
|
49
49
|
choose its own account. Gmail never sends mail, and Drive never permanently
|
|
50
50
|
deletes files.
|
|
51
51
|
|
|
52
|
+
Wispr Flow uses OAuth only through `wisprFlow()` from
|
|
53
|
+
`@zackbart/connecta/providers/wispr-flow`. It reaches the authorized account's
|
|
54
|
+
Notetaker content, with personal and work accounts holding separate data.
|
|
55
|
+
Its [provider guide](https://github.com/zackbart/connecta/blob/main/src/providers/wispr-flow/SKILL.md)
|
|
56
|
+
owns account access, search limits, transcript permissions and paging. The
|
|
57
|
+
reviewed classification includes task writes listed in Claude's directory even
|
|
58
|
+
though Wispr's public help describes read-only access; discover the account's
|
|
59
|
+
actual tools before assuming those writes exist.
|
|
60
|
+
|
|
52
61
|
Stripe requires `auth`: `{ type: "oauth" }` reaches Stripe's hosted MCP
|
|
53
62
|
server, and `{ type: "apiKey" }` with `mode` reaches Connecta's REST connector
|
|
54
63
|
with an operator-managed key. Notion requires `auth` the same way:
|
|
@@ -66,6 +75,48 @@ MCP clients to its hosted server, an operational gate outside Connecta. See the
|
|
|
66
75
|
To use both implementations of one provider, configure two connector ids with
|
|
67
76
|
independent credentials.
|
|
68
77
|
|
|
78
|
+
Planning Center's `excludeApps` removes whole products from a connection, for
|
|
79
|
+
example `planningCenter("pco", { purpose, excludeApps: ["giving"] })` for a
|
|
80
|
+
deployment that keeps agents away from donor and financial data. Excluded
|
|
81
|
+
products lose their named tools, guide text, and version pins, and the
|
|
82
|
+
`pco_api_get` and `pco_api_mutate` hatches refuse their paths before sending.
|
|
83
|
+
The check reads the exact URL the transport would fetch, and while any product
|
|
84
|
+
is excluded a hatch path must be literal: dot segments, empty segments,
|
|
85
|
+
backslashes, whitespace, and percent-encoded `/`, `\`, `?`, `#`, or `%` are
|
|
86
|
+
refused rather than normalized.
|
|
87
|
+
|
|
88
|
+
Exclusion also closes the routes through which the excluded product's data is
|
|
89
|
+
reachable through another product. While any product is excluded the connection removes
|
|
90
|
+
`list_lists`, `list_list_people`, and `run_list` and refuses every People list,
|
|
91
|
+
list result, rule, and condition, by path or `include`, because a list's
|
|
92
|
+
conditions can draw on any product, such as a donor list built from Giving
|
|
93
|
+
donations, and lists can depend on other lists. Planning Center publishes a
|
|
94
|
+
list's conditions, but a reliable check would have to follow every nested list,
|
|
95
|
+
so the connection does not attempt one. It refuses webhook event and delivery
|
|
96
|
+
payloads, since a payload can carry any product's records, and every change to
|
|
97
|
+
an existing webhook subscription by id, since the id does not name its product.
|
|
98
|
+
A new subscription must name a reachable product's event. People cannot be
|
|
99
|
+
excluded.
|
|
100
|
+
|
|
101
|
+
What exclusion guarantees is that agents reach none of the excluded product's
|
|
102
|
+
endpoints, no live cross-product query such as a People list, and no webhook
|
|
103
|
+
payload or change to an existing subscription. It does not hide records that the church's own
|
|
104
|
+
automations or staff have copied into a reachable product. A
|
|
105
|
+
[Giving automation](https://help.planningcenter.com/en/144355-automations-in-giving.html)
|
|
106
|
+
can add first-time donors to a People workflow, and those workflow cards stay
|
|
107
|
+
readable, as do donor details recorded in notes, custom fields, or form
|
|
108
|
+
answers. Those are People data once written, so the connection does not refuse
|
|
109
|
+
them by guessing at their origin.
|
|
110
|
+
|
|
111
|
+
For a hard boundary, give the connection a personal access token belonging to a
|
|
112
|
+
Planning Center user with No Access in the excluded product. Planning Center
|
|
113
|
+
sets permissions per product for each user, does not grant Giving to
|
|
114
|
+
organization administrators automatically, and lets People workflows be shared
|
|
115
|
+
per workflow; a token carries its user's permissions, so Planning Center itself
|
|
116
|
+
refuses what that user cannot see. `excludeApps` is defense in depth on top of
|
|
117
|
+
that boundary: it narrows what Connecta exposes, not what the token can do
|
|
118
|
+
elsewhere.
|
|
119
|
+
|
|
69
120
|
## Another remote MCP server
|
|
70
121
|
|
|
71
122
|
Declare its endpoint and intended auth mode. This fragment uses a server-stored
|
|
@@ -137,9 +188,10 @@ which binds the grant to configured API origins and manages authentication.
|
|
|
137
188
|
[Downstream OAuth on api()](./auth.md#downstream-oauth-on-api) owns a concrete
|
|
138
189
|
HTTP example and the client setup contract.
|
|
139
190
|
|
|
140
|
-
Mark every side-effecting tool `readOnlyHint: false`. On
|
|
141
|
-
writes use `call_destructive_tool
|
|
142
|
-
|
|
191
|
+
Mark every side-effecting tool `readOnlyHint: false`. On a writable endpoint
|
|
192
|
+
writes use `call_destructive_tool` or a program, within the caller's grants; a
|
|
193
|
+
`readOnly` endpoint refuses them and omits them from search. Client approval is independent of
|
|
194
|
+
tool definition. Use declared output
|
|
143
195
|
schemas when known; runtime observations cannot replace them.
|
|
144
196
|
|
|
145
197
|
## Separate ownership from visibility
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Connecta keeps one small tool surface in model context and resolves downstream
|
|
4
4
|
tools behind it. `search_tools` finds addresses, the call tools enforce the
|
|
5
|
-
stored classification, `execute_code` runs programs
|
|
5
|
+
stored classification, `execute_code` runs programs on a writable or read-only endpoint, and `connecta.result` inside programs
|
|
6
6
|
pages bounded results.
|
|
7
7
|
|
|
8
8
|
This guide is the contract an MCP client sees. The in-program `connecta.*` API
|
|
@@ -31,10 +31,13 @@ configuration adds a seventh or removes one, so a client's cached tool list
|
|
|
31
31
|
never depends on the storage behind the deployment. There was an eighth,
|
|
32
32
|
`resume_execution`, while programs paused at writes; it left with pausing
|
|
33
33
|
([#672](https://github.com/zackbart/connecta/issues/672)). `execute_code`
|
|
34
|
-
is annotated `readOnlyHint: true, destructiveHint: false` on a
|
|
35
|
-
endpoint, and `readOnlyHint: false, destructiveHint: true` on a
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
is annotated `readOnlyHint: true, destructiveHint: false` on a read-only
|
|
35
|
+
endpoint, and `readOnlyHint: false, destructiveHint: true` on a writable one.
|
|
36
|
+
Approval belongs to the client. A read-only endpoint keeps all six tools, omits
|
|
37
|
+
write tools from `search_tools` results and describe, and refuses every write
|
|
38
|
+
call; `readOnly` never changes connector visibility or read grants. Guides
|
|
39
|
+
(`queryAnalysis.guideSummary`, `skills`, `connecta.skill`, skill
|
|
40
|
+
`resources/read`) are documentation and may name tools the endpoint refuses. The four tools with structured-only success results
|
|
38
41
|
advertise `outputSchema`. The two direct-call tools omit it because their default
|
|
39
42
|
mode returns native `content`. Default-mode successes declare
|
|
40
43
|
`_meta["dev.connecta/format"]: "json" | "text"`; value mode places the value in
|
|
@@ -70,9 +73,11 @@ credentials, logs, or raw error text.
|
|
|
70
73
|
|
|
71
74
|
Connecta's discovery tools carry read-only hints. `authorize_connector`
|
|
72
75
|
requests interactive authentication changes; `call_destructive_tool` sends
|
|
73
|
-
writes and is annotated destructive. `execute_code`
|
|
74
|
-
|
|
75
|
-
program
|
|
76
|
+
writes and is annotated destructive. `execute_code` states whether the endpoint
|
|
77
|
+
is read-only or writable in its description and annotations. On a writable
|
|
78
|
+
endpoint the client may approve the whole program as a write. On a read-only
|
|
79
|
+
endpoint, the `call_tool`, `call_destructive_tool`, and `search_tools`
|
|
80
|
+
descriptions say so too; `call_destructive_tool` still runs reads there. `call_destructive_tool.reason` is dropped before
|
|
76
81
|
anything runs.
|
|
77
82
|
|
|
78
83
|
## Routing between the call surfaces
|
|
@@ -93,12 +98,16 @@ page the completed direct-call result or oversized program return with
|
|
|
93
98
|
`connecta.result` after the write runs once. Sampling or slicing a write's output
|
|
94
99
|
inside the program can still discard the answer.
|
|
95
100
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
`
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
On a writable endpoint the same program discovers and dispatches writes, capped
|
|
102
|
+
by `execute.maxWrites`, and `call_destructive_tool` sends one known write. On a
|
|
103
|
+
read-only endpoint nothing writes: `search_tools`, `connecta.search`, and
|
|
104
|
+
`connecta.describe` omit write tools as if the connector lacked them, and
|
|
105
|
+
every write call through `call_tool`, `call_destructive_tool`, or a program is
|
|
106
|
+
refused before validation or dispatch with `read_only_endpoint`,
|
|
107
|
+
`retryable: false`, and no `nextAction`. Connector guides are not filtered and
|
|
108
|
+
may still name such a write. No route on that endpoint writes; the agent
|
|
109
|
+
reports it. Connecta
|
|
110
|
+
never pauses, replays, or approves a program.
|
|
102
111
|
|
|
103
112
|
Both search routes return the flat page specified under
|
|
104
113
|
[lexical discovery](#lexical-discovery), using the catalog's shared output schema.
|
|
@@ -129,14 +138,14 @@ Read-only lookup belongs in `connecta.search` inside the program; top-level
|
|
|
129
138
|
`search_tools` stays for explicit catalog inspection and approval-required
|
|
130
139
|
discovery. Both take the same arguments.
|
|
131
140
|
|
|
132
|
-
| Argument | What it does
|
|
133
|
-
| ------------------ |
|
|
134
|
-
| `query` | two to four action/object terms; empty or whitespace-only browses
|
|
135
|
-
| `connector` | scopes to one id, loading that catalog alone instead of fanning out across every configured connector. Set it when the integration is obvious, omit it when the right one is genuinely ambiguous
|
|
136
|
-
| `safety` | `"readOnly"` selects stored `read` verdicts; `"approvalRequired"` selects stored `write` verdicts; omitted or `"all"` selects both. Each row carries `classification: "read" \| "write"`. These legacy filter names do not describe
|
|
137
|
-
| `limit` / `offset` | page the ranked results; omit `limit` initially so the default eight-result page stays small
|
|
138
|
-
| `includeSchemas` | `"compact"` for the rendered routing view, `"json"` for the exact schema, `"typescript"` for a function signature ([below](#typescript-signatures))
|
|
139
|
-
| `fullDescriptions` | unabridged tool purposes, at the obvious cost
|
|
141
|
+
| Argument | What it does |
|
|
142
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
143
|
+
| `query` | two to four action/object terms; empty or whitespace-only browses |
|
|
144
|
+
| `connector` | scopes to one id, loading that catalog alone instead of fanning out across every configured connector. Set it when the integration is obvious, omit it when the right one is genuinely ambiguous |
|
|
145
|
+
| `safety` | `"readOnly"` selects stored `read` verdicts; `"approvalRequired"` selects stored `write` verdicts; omitted or `"all"` selects both. Each row carries `classification: "read" \| "write"`. These legacy filter names do not describe client approval policy; a read-only endpoint has no write rows. |
|
|
146
|
+
| `limit` / `offset` | page the ranked results; omit `limit` initially so the default eight-result page stays small |
|
|
147
|
+
| `includeSchemas` | `"compact"` for the rendered routing view, `"json"` for the exact schema, `"typescript"` for a function signature ([below](#typescript-signatures)) |
|
|
148
|
+
| `fullDescriptions` | unabridged tool purposes, at the obvious cost |
|
|
140
149
|
|
|
141
150
|
Neither `connector` nor `safety` grants authority or changes invocation
|
|
142
151
|
admission; they select what discovery shows and nothing else.
|
|
@@ -381,7 +390,7 @@ caught catalog-load errors, unknown-tool failures, and recovery messages that
|
|
|
381
390
|
list visible connectors or local guides. Returns with no downstream calls still
|
|
382
391
|
require matching identity, origin and endpoint, current auth, and pool membership. A page must
|
|
383
392
|
match those bindings and pass current auth, connector/tool grants, pool membership,
|
|
384
|
-
and
|
|
393
|
+
and read-only checks. Old entries lacking bindings fail closed. A random UUID is a
|
|
385
394
|
handle, never an access grant.
|
|
386
395
|
|
|
387
396
|
New entries store UTF-8 bytes in a base64 envelope split across storage keys,
|
|
@@ -512,13 +521,10 @@ format: "text", text }`. The page size is clamped to the original call's inline
|
|
|
512
521
|
cap and a 32 KiB program bridge ceiling, with UTF-8 alignment and forward progress. Reassemble inside the sandbox;
|
|
513
522
|
returning pages unchanged can hit the program result cap. The admitted subject
|
|
514
523
|
and principal, endpoint/pool, request origin, connector, and tool bindings must
|
|
515
|
-
match. The host rechecks current access and
|
|
516
|
-
read-only endpoint cannot read a write stash.
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
usable bounded preview. It says paging is unavailable and the write already
|
|
520
|
-
ran; it carries no result handle or recovery action and stores no unreachable
|
|
521
|
-
stash. Write paging remains available on trusted endpoints.
|
|
524
|
+
match. The host rechecks current access and whether the endpoint is read-only
|
|
525
|
+
before returning each page; a read-only endpoint cannot read a write stash.
|
|
526
|
+
Caller arguments select no partition. A read-only endpoint never runs a write,
|
|
527
|
+
so every write result it could page came from a writable endpoint.
|
|
522
528
|
|
|
523
529
|
A refused stash write cannot undo a downstream success. Direct calls and program returns produce
|
|
524
530
|
a paging-unavailable notice, without a handle or recovery action. A write's
|
|
@@ -820,10 +826,11 @@ attempted tool name; an unknown tool scopes the same query to the connector that
|
|
|
820
826
|
answered. The suggested route follows the route the caller took:
|
|
821
827
|
`tool: "search_tools"` for a top-level call, `function: "connecta.search"` with
|
|
822
828
|
the same arguments when the miss happened inside `execute_code`, which has no
|
|
823
|
-
way to call a tool. `call_tool` reaching an unannotated,
|
|
824
|
-
destructive tool returns `
|
|
825
|
-
|
|
826
|
-
|
|
829
|
+
way to call a tool. On a writable endpoint, `call_tool` reaching an unannotated,
|
|
830
|
+
write-capable, or destructive tool returns `destructive_tool_requires_approval`
|
|
831
|
+
with `nextAction` for `call_destructive_tool` and the canonical address. On a
|
|
832
|
+
read-only endpoint every write path returns `read_only_endpoint` with no
|
|
833
|
+
`nextAction`. Nothing is executed by these records.
|
|
827
834
|
|
|
828
835
|
`connecta.describe` keeps its failures inline instead, so one miss cannot
|
|
829
836
|
discard the other schemas; each failed entry carries a human `error`, typed
|
|
@@ -932,12 +939,12 @@ original arguments cannot repeat the write while the vendor retains the key
|
|
|
932
939
|
(Stripe may prune v1 keys after 24 hours). After that, look the object up before
|
|
933
940
|
retrying. When a connector recorded such facts, any other dispatched write whose
|
|
934
941
|
outcome is unknown (a lost response body, a 5xx) also carries `uncertainCall`
|
|
935
|
-
with them under its own code, and
|
|
942
|
+
with them under its own code, and program write accounting keeps it. `args` may be partial reconciliation context
|
|
936
943
|
with `argsRedacted: true`. A withheld echo or arguments over the 512-byte budget
|
|
937
944
|
carry `argsOmitted: true`; sensitive or unresolved schemas also carry
|
|
938
945
|
`argsRedacted: true`. When `args` is absent or redacted, use the original
|
|
939
|
-
arguments if reconciliation requires another call. A timeout before dispatch remains `timeout`.
|
|
940
|
-
|
|
946
|
+
arguments if reconciliation requires another call. A timeout before dispatch remains `timeout`.
|
|
947
|
+
Program write accounting keeps this detail even if the guest catches the
|
|
941
948
|
failure or returns early. Multiple uncertain writes use `uncertainCalls`,
|
|
942
949
|
bounded to ten calls with `uncertainCallsTruncated` when more exist.
|
|
943
950
|
|