@zackbart/connecta 0.30.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.
Files changed (133) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/README.md +21 -16
  3. package/bin/connecta.mjs +1 -0
  4. package/bin/version.mjs +1 -1
  5. package/dist/auth-elicitation.js +1 -1
  6. package/dist/call-recovery.js +25 -0
  7. package/dist/catalog-service.js +7 -2
  8. package/dist/config-schema.d.ts +14 -7
  9. package/dist/config-schema.js +29 -10
  10. package/dist/config-value-sources.js +6 -3
  11. package/dist/config.d.ts +26 -8
  12. package/dist/config.js +38 -16
  13. package/dist/connector-access.d.ts +2 -1
  14. package/dist/connectors/guarded-fetch.d.ts +18 -1
  15. package/dist/connectors/guarded-fetch.js +21 -2
  16. package/dist/describe-config.d.ts +6 -3
  17. package/dist/describe-config.js +3 -2
  18. package/dist/errors.d.ts +3 -1
  19. package/dist/errors.js +1 -0
  20. package/dist/execute.js +16 -14
  21. package/dist/index.js +4 -3
  22. package/dist/invocation.js +39 -4
  23. package/dist/meta-tools.js +17 -19
  24. package/dist/operator-record.d.ts +2 -2
  25. package/dist/operator-record.js +2 -0
  26. package/dist/operator-ui/contract.d.ts +5 -4
  27. package/dist/operator-ui/generated.js +2 -2
  28. package/dist/operator-ui/model.d.ts +1 -1
  29. package/dist/operator-ui/view.d.ts +9 -0
  30. package/dist/operator-ui/view.js +12 -0
  31. package/dist/program-writes.js +4 -49
  32. package/dist/provider.d.ts +6 -5
  33. package/dist/provider.js +3 -4
  34. package/dist/providers/_shared/google/workspace.js +1 -1
  35. package/dist/providers/_shared/rest/dispatch.js +41 -0
  36. package/dist/providers/_shared/rest/operation-index.d.ts +133 -0
  37. package/dist/providers/_shared/rest/operation-index.js +527 -0
  38. package/dist/providers/_shared/rest/tools.d.ts +141 -0
  39. package/dist/providers/_shared/rest/tools.js +569 -0
  40. package/dist/providers/_shared/rest/value-safety.d.ts +151 -0
  41. package/dist/providers/_shared/rest/value-safety.js +580 -0
  42. package/dist/providers/breeze/skill.generated.js +2 -2
  43. package/dist/providers/cloudflare/graphql.js +385 -0
  44. package/dist/providers/cloudflare/index.d.ts +47 -51
  45. package/dist/providers/cloudflare/index.js +188 -1997
  46. package/dist/providers/cloudflare/openapi.generated.js +3347 -0
  47. package/dist/providers/cloudflare/rest.d.ts +37 -0
  48. package/dist/providers/cloudflare/rest.js +1056 -0
  49. package/dist/providers/cloudflare/skill.generated.js +7 -4
  50. package/dist/providers/cloudflare/value-safety.js +1247 -0
  51. package/dist/providers/docs/index.d.ts +1 -1
  52. package/dist/providers/docs/index.js +2 -2
  53. package/dist/providers/drive/index.d.ts +1 -1
  54. package/dist/providers/drive/index.js +1 -1
  55. package/dist/providers/forms/index.d.ts +1 -1
  56. package/dist/providers/forms/index.js +2 -2
  57. package/dist/providers/github/skill.generated.js +2 -2
  58. package/dist/providers/gmail/index.d.ts +1 -1
  59. package/dist/providers/gmail/index.js +274 -23
  60. package/dist/providers/gmail/skill.generated.js +2 -2
  61. package/dist/providers/linear/index.js +1 -1
  62. package/dist/providers/mixpanel/index.d.ts +27 -12
  63. package/dist/providers/mixpanel/index.js +58 -6
  64. package/dist/providers/mixpanel/operations.js +20 -0
  65. package/dist/providers/mixpanel/rest.js +603 -0
  66. package/dist/providers/mixpanel/skill.generated.js +3 -2
  67. package/dist/providers/mixpanel/value-safety.js +16 -0
  68. package/dist/providers/notion/index.d.ts +35 -31
  69. package/dist/providers/notion/index.js +76 -1499
  70. package/dist/providers/notion/openapi.generated.js +75 -0
  71. package/dist/providers/notion/rest.d.ts +24 -0
  72. package/dist/providers/notion/rest.js +1186 -0
  73. package/dist/providers/notion/skill.generated.js +5 -4
  74. package/dist/providers/notion/value-safety.js +31 -0
  75. package/dist/providers/planning-center/index.d.ts +27 -0
  76. package/dist/providers/planning-center/index.js +286 -24
  77. package/dist/providers/planning-center/skill.generated.js +24 -4
  78. package/dist/providers/sheets/index.d.ts +1 -1
  79. package/dist/providers/sheets/index.js +3 -3
  80. package/dist/providers/slides/index.d.ts +1 -1
  81. package/dist/providers/slides/index.js +3 -3
  82. package/dist/providers/stripe/index.d.ts +26 -20
  83. package/dist/providers/stripe/index.js +103 -130
  84. package/dist/providers/stripe/openapi.generated.js +649 -0
  85. package/dist/providers/stripe/rest.d.ts +14 -0
  86. package/dist/providers/stripe/rest.js +395 -0
  87. package/dist/providers/stripe/skill.generated.js +7 -5
  88. package/dist/providers/stripe/value-safety.js +705 -0
  89. package/dist/providers/vercel/index.d.ts +31 -22
  90. package/dist/providers/vercel/index.js +96 -853
  91. package/dist/providers/vercel/openapi.generated.js +456 -0
  92. package/dist/providers/vercel/rest.d.ts +20 -0
  93. package/dist/providers/vercel/rest.js +1002 -0
  94. package/dist/providers/vercel/skill.generated.js +4 -5
  95. package/dist/providers/vercel/value-safety.js +328 -0
  96. package/dist/providers/wispr-flow/index.d.ts +14 -0
  97. package/dist/providers/wispr-flow/index.js +117 -0
  98. package/dist/providers/wispr-flow/skill.generated.js +8 -0
  99. package/dist/routes/mcp.js +30 -10
  100. package/dist/routes/operator.js +14 -7
  101. package/dist/routes/ui-config.js +2 -2
  102. package/dist/server.js +3 -0
  103. package/dist/skills.js +5 -4
  104. package/dist/tool-safety.js +13 -5
  105. package/dist/types.d.ts +1 -1
  106. package/dist/ui.d.ts +1 -1
  107. package/dist/ui.js +1 -1
  108. package/dist/usage-guide.js +3 -3
  109. package/dist/version.d.ts +1 -1
  110. package/dist/version.js +1 -1
  111. package/dist/write-outcome.js +47 -0
  112. package/documentation/README.md +1 -0
  113. package/documentation/architecture.md +155 -28
  114. package/documentation/auth.md +86 -27
  115. package/documentation/code-mode.md +81 -72
  116. package/documentation/deploying.md +10 -5
  117. package/documentation/integrating.md +72 -9
  118. package/documentation/meta-tools.md +52 -38
  119. package/documentation/operating.md +11 -8
  120. package/documentation/operator-tests.md +2 -3
  121. package/documentation/operator-ui.md +9 -3
  122. package/documentation/provider-auth-migration.md +308 -0
  123. package/documentation/provider-migration-0.29.md +18 -6
  124. package/examples/worker/AGENTS.md +5 -0
  125. package/examples/worker/README.md +2 -2
  126. package/examples/worker/src/connecta.config.ts +5 -0
  127. package/package.json +7 -2
  128. package/templates/node/AGENTS.md +6 -4
  129. package/templates/node/README.md +6 -4
  130. package/templates/node/package.json +1 -1
  131. package/templates/node/src/connecta.config.ts +5 -0
  132. package/dist/providers/vercel/mcp-ownership.js +0 -212
  133. package/dist/tool-safety.d.ts +0 -9
@@ -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 pool trust. Read the
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,11 +49,73 @@ 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
- Notion, Vercel, and Cloudflare default to hosted MCP. Their explicit
53
- `surface: "api"` choices retain selected REST capabilities, with Notion's
54
- internal-integration identity preserved. To use both, configure distinct IDs
55
- and independent credentials. Read the [0.29 migration guide](./provider-migration-0.29.md)
56
- and its provider reconciliation tables for retained and removed names.
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
+
61
+ Stripe requires `auth`: `{ type: "oauth" }` reaches Stripe's hosted MCP
62
+ server, and `{ type: "apiKey" }` with `mode` reaches Connecta's REST connector
63
+ with an operator-managed key. Notion requires `auth` the same way:
64
+ `{ type: "oauth" }` acts as the signed-in user over Notion's hosted MCP, and
65
+ `{ type: "token" }` acts as an internal integration over Connecta's REST
66
+ connector. Cloudflare requires `auth` too: `{ type: "oauth" }` reaches its
67
+ hosted MCP server, and `{ type: "apiToken" }` or `{ type: "globalApiKey" }`
68
+ reaches Connecta's REST connector, with an optional `pin` of accounts and zones
69
+ (required for a Global API Key unless `unpinned: true`). Vercel requires `auth`
70
+ as well: `{ type: "oauth" }` reaches Vercel's hosted MCP server, and
71
+ `{ type: "token" }` with an optional `teamId` reaches Connecta's REST connector
72
+ with an operator-managed access token. Vercel admits only reviewed and approved
73
+ MCP clients to its hosted server, an operational gate outside Connecta. See the
74
+ [provider auth migration](./provider-auth-migration.md).
75
+ To use both implementations of one provider, configure two connector ids with
76
+ independent credentials.
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.
57
119
 
58
120
  ## Another remote MCP server
59
121
 
@@ -126,9 +188,10 @@ which binds the grant to configured API origins and manages authentication.
126
188
  [Downstream OAuth on api()](./auth.md#downstream-oauth-on-api) owns a concrete
127
189
  HTTP example and the client setup contract.
128
190
 
129
- Mark every side-effecting tool `readOnlyHint: false`. On read-only endpoints
130
- writes use `call_destructive_tool`; trusted programs may write within their
131
- grants. Host approval is independent of tool definition. Use declared output
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
132
195
  schemas when known; runtime observations cannot replace them.
133
196
 
134
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 under pool trust, and `connecta.result` inside 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 `read-only`
35
- endpoint, and `readOnlyHint: false, destructiveHint: true` on a `trusted`
36
- endpoint. Approval belongs to the host. Pool trust never changes connector
37
- visibility or tool grants. The four tools with structured-only success results
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` reports the selected pool's
74
- trust in its description and annotations. Its host may approve the whole
75
- program on a trusted endpoint. `call_destructive_tool.reason` is dropped before
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
- A `trusted` pool lets the same program discover and dispatch writes.
97
- A `read-only` pool refuses writes before validation or dispatch with
98
- `destructive_tool_requires_approval` and a `nextAction` naming
99
- `call_destructive_tool` and the canonical address. Each write then uses its
100
- own top-level call. Connecta never pauses, replays, or approves a program.
101
- Top-level discovery remains available for catalog inspection and write routing.
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 host approval policy or pool trust. |
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 trust checks. Old entries lacking bindings fail closed. A random UUID is a
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 trust before returning each page; a
516
- read-only endpoint cannot read a write stash. Caller arguments select no partition.
517
-
518
- A write on a read-only endpoint returns an inline truncation notice and any
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, write-capable, or
824
- destructive tool returns `nextAction` for `call_destructive_tool` with the
825
- canonical address, and so does the same call inside a program, unless config
826
- marks that pool trusted. Nothing is executed by these records.
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
@@ -924,13 +931,20 @@ scoped registry, including an empty list when none are accessible. They never
924
931
  list connectors from another pool or identity's grants.
925
932
 
926
933
  A write that times out after dispatch returns `write_outcome_unknown` with
927
- `retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted? }`.
928
- No write is automatically replayed. `args` may be partial reconciliation context
934
+ `retryable: false` and an agent-only `uncertainCall: { address, args?, argsOmitted?, argsRedacted?, recovery? }`.
935
+ No write is automatically replayed. `recovery` carries facts the connector
936
+ recorded before dispatch (`src/call-recovery.ts`), such as the `idempotencyKey`
937
+ a REST connector generated; a deliberate retry that reuses it with the exact
938
+ original arguments cannot repeat the write while the vendor retains the key
939
+ (Stripe may prune v1 keys after 24 hours). After that, look the object up before
940
+ retrying. When a connector recorded such facts, any other dispatched write whose
941
+ outcome is unknown (a lost response body, a 5xx) also carries `uncertainCall`
942
+ with them under its own code, and program write accounting keeps it. `args` may be partial reconciliation context
929
943
  with `argsRedacted: true`. A withheld echo or arguments over the 512-byte budget
930
944
  carry `argsOmitted: true`; sensitive or unresolved schemas also carry
931
945
  `argsRedacted: true`. When `args` is absent or redacted, use the original
932
- arguments if reconciliation requires another call. A timeout before dispatch remains `timeout`. Trusted
933
- program write accounting keeps this detail even if the guest catches the
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
934
948
  failure or returns early. Multiple uncertain writes use `uncertainCalls`,
935
949
  bounded to ten calls with `uncertainCallsTruncated` when more exist.
936
950
 
@@ -15,14 +15,17 @@ instructions and `skills` usage guide for deployment-specific context.
15
15
  | ------------------------------------------------------------------ | --------------------------------------------------------- |
16
16
  | One known read, with usable arguments and a small result | `call_tool` |
17
17
  | Discover a tool, chain calls, combine services, or reduce a result | `execute_code` |
18
- | One write on a read-only endpoint | `call_destructive_tool` |
18
+ | One known write on a writable endpoint | `call_destructive_tool` |
19
19
  | Obtain connector instructions | `skills` with the returned guide name |
20
20
  | Start a required authentication handoff | `authorize_connector`, using the returned recovery action |
21
21
 
22
- The endpoint's trust is part of its tool descriptions. On a `trusted` endpoint,
23
- programs may write within the caller's grants, and `execute_code` is annotated
24
- as a write. On `read-only`, every write is a separate top-level call. The host
25
- controls approval. Never change pool, identity, or trust to work around a refusal.
22
+ Whether the endpoint is read-only or writable is part of its tool
23
+ descriptions. On a writable endpoint, programs and `call_destructive_tool` may
24
+ write within the caller's grants, and `execute_code` is annotated as a write;
25
+ the client controls approval. On a read-only endpoint, search and describe omit
26
+ write tools and every write call is refused with `read_only_endpoint`, even one
27
+ a connector guide names; tell the user rather than looking for another route. Never change pool, identity, or `readOnly` to work
28
+ around a refusal.
26
29
  [Routing between the call surfaces](./meta-tools.md#routing-between-the-call-surfaces)
27
30
  owns the full decision rules.
28
31
 
@@ -35,9 +38,9 @@ failed catalog or a service outside the caller's grants.
35
38
 
36
39
  For normal reads, keep discovery, calls, and reduction in one program when the
37
40
  schemas suffice; return the answer rather than catalog matches. Use top-level
38
- `search_tools` for catalog inspection or to discover a write on a read-only
39
- endpoint, then dispatch each write with `call_destructive_tool`. An unfamiliar
40
- read result may return a bounded sample for inspection before the next program.
41
+ `search_tools` for catalog inspection or to find one known write on a writable
42
+ endpoint, then dispatch it with `call_destructive_tool`. An unfamiliar read
43
+ result may return a bounded sample for inspection before the next program.
41
44
 
42
45
  Use the exact returned `address`, including punctuation. A TypeScript signature
43
46
  is guidance for writing JavaScript; do not paste types into executable code.
@@ -72,9 +72,8 @@ gh run download RUN_ID --name operator-linux-snapshots \
72
72
 
73
73
  The workflow uploads all generated baselines and never commits them. Review the
74
74
  images and changed files, commit the intentional changes, and push again. The
75
- normal CI browser job must compare and pass against that commit. Opening the
76
- initial PR also runs the update workflow, so the first baselines can be generated
77
- before the workflow reaches main. Later updates use `workflow_dispatch`.
75
+ normal CI browser job must compare and pass against that commit. Snapshot
76
+ updates run only through `workflow_dispatch`.
78
77
 
79
78
  On a matching Linux runner, `npm ci`, install Chromium with the command above,
80
79
  then run `npm run test:browser:snapshots`. The normal command is
@@ -42,7 +42,9 @@ from `connecta/ui`. `src/operator-ui/contract.ts` is also the browser's type
42
42
  source. The response has `schemaVersion: 1`, `config`, `configSources`, `live`, and `you`.
43
43
  `config` is the construction-time `describeConfig()` snapshot, filtered to
44
44
  visible connectors, granted tools, classification overrides, and admitted
45
- pools. Pool tools are the intersection with the caller's identity grants.
45
+ pools. `config.hasGrant` reports whether deployment code configures a grant for
46
+ plain `/mcp`; Access displays that fact and Config includes its provenance.
47
+ Pool tools are the intersection with the caller's identity grants.
46
48
  `configSources` labels each serialized leaf as `default` or `config`, based on
47
49
  option presence rather than equality with the default. Factory descriptions carry
48
50
  relative `optionSources` paths so resolved module and transport defaults retain
@@ -52,6 +54,10 @@ ids and pool names and are filtered after disclosure; hidden values leave no
52
54
  provenance keys. It contains no source code or credential material.
53
55
  Every response is private, `no-store` JSON. Only GET is allowed, with the
54
56
  same auth gate and identity partition as `/ui/data` and `/ui/connectors/:id`.
57
+ These catalog reads, activity reads, and connector auth routes also require
58
+ admission through the default `/mcp` grant. Refusals return private JSON 404s;
59
+ there is no per-pool UI view. Public HTML shells and static assets carry no
60
+ catalog data. Access-token management retains its interactive permission gate.
55
61
 
56
62
  `live.connectors` carries status and problem codes, complete registry tools
57
63
  with their final read/write classification, catalog age in milliseconds, and
@@ -69,7 +75,7 @@ personal connectors to the caller's activity identity. Each retained fact
69
75
  copies a validated timestamp and outcome only. A null call means unknown or
70
76
  absent in that window, not proof of no calls. `live.activity` distinguishes
71
77
  available, unconfigured, forbidden, and unavailable history. `you` names no
72
- identity or auth material; it reports grants, root and admitted-pool trust,
78
+ identity or auth material; it reports grants, whether the root and admitted pools are read-only,
73
79
  and effective activity, token, and per-connector auth permissions.
74
80
 
75
81
  `connecta doctor --config` fetches this authenticated contract and prints only
@@ -112,7 +118,7 @@ id and typed actor when available, otherwise a fresh id and system actor. No
112
118
  names, descriptions or schemas enter the event. Private catalog changes require
113
119
  a matching principal owner even when the connector uses shared auth. Empty credential declarations
114
120
  report `credential_required` before running a connector status or catalog probe.
115
- The Access adapter reports inbound provider kinds, admitted pools, grants, trust
121
+ The Access adapter reports inbound provider kinds, admitted pools, grants, read-only endpoints
116
122
  and endpoint setup; token controls retain their existing permission gate.
117
123
  Configuration remains in deployment code. The UI mutates only credentials,
118
124
  OAuth connections and client tokens through the existing routes.