@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
@@ -0,0 +1,308 @@
1
+ # Provider auth migration
2
+
3
+ Use this upgrade guide when moving a Stripe, Notion, Vercel, or Cloudflare
4
+ deployment from 0.30 to 0.31, where `auth` selects each provider's
5
+ implementation ([#801](https://github.com/zackbart/connecta/issues/801)).
6
+ The [0.31.0 release notes](https://github.com/zackbart/connecta/blob/main/release-notes/0.31.0.md) summarize the change.
7
+ [Deployment upgrades](./deploying.md#upgrade-an-existing-deployment) cover other
8
+ version changes; [integrating services](./integrating.md) covers new connectors.
9
+
10
+ A dual provider requires `auth`. `{ type: "oauth" }` connects the vendor's
11
+ hosted MCP server, OAuth only. A key type connects Connecta's REST connector
12
+ with an operator-managed credential. There is no default, and one connector id
13
+ uses one implementation: configure two ids for both. The
14
+ [decision record](https://github.com/zackbart/connecta/blob/main/decisions/0005-auth-selects-implementation.md)
15
+ explains why.
16
+
17
+ ## Stripe
18
+
19
+ Stripe's hosted MCP server stops accepting secret keys and untagged
20
+ restricted keys on 2026-10-31; Stripe still documents agent-tagged restricted
21
+ keys for it. Connecta's hosted Stripe connection is OAuth only either way, and
22
+ every key, tagged or not, uses the REST connector. Every `stripe()` call must
23
+ now name `auth`:
24
+
25
+ ```ts
26
+ connectors: [
27
+ stripe("stripe", {
28
+ purpose: "Organization billing",
29
+ auth: { type: "oauth" },
30
+ }),
31
+ stripe("stripe_live", {
32
+ purpose: "Live billing",
33
+ auth: { type: "apiKey" },
34
+ mode: "production",
35
+ }),
36
+ stripe("stripe_sandbox", {
37
+ purpose: "Rehearsal",
38
+ auth: { type: "apiKey" },
39
+ mode: "sandbox",
40
+ }),
41
+ stripe("stripe_merchant", {
42
+ purpose: "One connected merchant",
43
+ auth: { type: "apiKey" },
44
+ mode: "production",
45
+ connectedAccount: "acct_…",
46
+ }),
47
+ ];
48
+ ```
49
+
50
+ | 0.30 configuration | Now |
51
+ | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
52
+ | No `auth`, or `auth: { type: "oauth" }` | `auth: { type: "oauth" }`. Same endpoint, reviewed catalog, guide conventions, and grant. |
53
+ | `auth: { type: "headers", headers }` with `mode` | `auth: { type: "apiKey" }` with `mode`. Paste the key into the connection in the operator UI; literal keys are gone. |
54
+ | `auth: { type: "credential" }` with `mode` | `auth: { type: "apiKey" }` with `mode`. A key stored for the same connector id carries over; run its Test action. |
55
+ | `connectedAccount` with `headers` auth | `connectedAccount` with `auth: { type: "apiKey" }`; it now works with an operator-managed key. |
56
+ | `mode` or `connectedAccount` with OAuth | Refused by name at construction, as before. |
57
+
58
+ The key connector keeps Stripe's tool names (`stripe_api_search`,
59
+ `stripe_api_details`, `stripe_api_read`, `stripe_api_write`,
60
+ `get_stripe_account_info`, and `get_balance_summary`) with Connecta's own
61
+ contracts: search finds operations in a pinned API index, every call is
62
+ checked against that index before it is sent, results arrive as
63
+ `{ status, data, page? }`, and POST writes send the caller's `idempotencyKey`
64
+ or a generated one as `Idempotency-Key` and return it (DELETE sends none).
65
+ Every request, v1 and v2, sends `Stripe-Version: 2026-09-30.endive`, the
66
+ version of the pinned index, whatever the account's default API version, so
67
+ response shapes and accepted parameters follow that version. Check fields
68
+ your programs rely on with `stripe_api_details`.
69
+ A key whose `sk_live_`/`rk_live_` or `sk_test_`/`rk_test_` prefix contradicts
70
+ `mode` is refused before any request. Organization keys (`sk_org_…`) need a
71
+ `Stripe-Context` header the connector does not send; use an account key.
72
+
73
+ Lost on the key path: natural-language Sigma (`stripe_analytics`), metrics,
74
+ documentation search, the implementation planner, reports, and feedback exist
75
+ only on the hosted OAuth connection. Programs calling those tools on a key
76
+ connector need an OAuth connector beside it. The
77
+ [Stripe skill](https://github.com/zackbart/connecta/blob/main/src/providers/stripe/SKILL.md)
78
+ owns the conventions for both.
79
+
80
+ ## Notion
81
+
82
+ Every `notion()` call must now name `auth`. 0.30 defaulted to hosted MCP and
83
+ selected the REST interface with `surface: "api"`; `surface` is gone:
84
+
85
+ ```ts
86
+ connectors: [
87
+ notion("notion", { purpose: "Workspace content as the signed-in user", auth: { type: "oauth" } }),
88
+ notion("notion_bot", { purpose: "Internal integration content", auth: { type: "token" } }),
89
+ ]
90
+ ```
91
+
92
+ | 0.30 configuration | Now |
93
+ | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
94
+ | No `surface`, or `surface: "mcp"` | `auth: { type: "oauth" }`. Same endpoint, reviewed catalog, grant, and optional `callAdmission`. |
95
+ | `surface: "api"` | `auth: { type: "token" }`. The integration token stored for the same connector id carries over; run its Test action. |
96
+ | `credentialLabel` or `defaultPageSize` | Token only, as before. Under OAuth they are refused by name at construction. |
97
+ | `integration_search`, `_get_page`, `_get_page_content`, `_get_data_source_schema`, `_query_data_source`, `_create_page`, `_append_blocks` | Unchanged names and contracts. `integration_append_blocks` also takes `checklist`. |
98
+ | `integration_get_database` | `notion_api_read` `GET /v1/databases/{database_id}`. |
99
+ | `integration_get_page_property` | `notion_api_read` `GET /v1/pages/{page_id}/properties/{property_id}`; `truncated_properties` names it. |
100
+ | `integration_list_users`, `integration_get_self` | `notion_api_read` `GET /v1/users` or `GET /v1/users/me`. |
101
+ | `integration_list_comments`, `integration_add_comment` | `notion_api_read` `GET /v1/comments`; `notion_api_write` `POST /v1/comments`. |
102
+ | `integration_update_page_properties`, `integration_trash_page` | `notion_api_write` `PATCH /v1/pages/{page_id}` with `properties`, or `in_trash` to trash or restore. |
103
+
104
+ The token connector gains Connecta's generic REST tools over Notion's pinned
105
+ API index (`notion_api_search`, `notion_api_details`, `notion_api_read`,
106
+ `notion_api_write`), so every public operation is reachable with the bot's
107
+ token: views, page moves and Markdown, meeting notes, custom agents and
108
+ sessions, file uploads by `external_url`, and data source changes. Every call
109
+ is checked against the index before it is sent, and results arrive as
110
+ `{ status, data, page? }`. The read-only POST queries (search, data source,
111
+ meeting notes, agents, sessions, and session events) use `notion_api_read`;
112
+ `page.in: "body"` says their cursor goes back in the body. Notion's OAuth token
113
+ endpoints and multipart file sends are refused. The 0.29 reconciliation table
114
+ and the `surface: "api"` complement rules no longer apply to Notion.
115
+
116
+ Lost per mode. A token connector acts as the integration bot, never as a
117
+ person, and has none of the hosted MCP's search across connected apps, page
118
+ duplication, team and skill search, attachment downloads, or page-to-skill
119
+ conversion. An OAuth connector acts as the user, and has none of the generic
120
+ REST tools or the `integration_*` projections and authoring helpers. Configure
121
+ both ids when a deployment needs both; a failed call is never retried on the
122
+ other. The [Notion skill](https://github.com/zackbart/connecta/blob/main/src/providers/notion/SKILL.md)
123
+ owns the conventions for both.
124
+
125
+ ## Vercel
126
+
127
+ Every `vercel()` call must now name `auth`. `surface` is gone, and so are the
128
+ 0.29 ownership rules that made the REST complement refuse whatever the hosted
129
+ server also covered: each implementation is complete on its own.
130
+
131
+ ```ts
132
+ connectors: [
133
+ vercel("vercel", { purpose: "Deployment diagnosis", auth: { type: "oauth" } }),
134
+ vercel("vercel_rest", { purpose: "Production apps", auth: { type: "token" }, teamId: "team_…" }),
135
+ ]
136
+ ```
137
+
138
+ | 0.30 configuration | Now |
139
+ | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
140
+ | No `surface`, or `surface: "mcp"` | `auth: { type: "oauth" }`. Same endpoint, reviewed catalog, and grant. |
141
+ | `surface: "api"` with `teamId` or `baseUrl` | `auth: { type: "token" }` with the same options. A token stored for the same connector id carries over; run its Test action. |
142
+ | `defaultPageSize` | Removed (it already had no effect). Pass `limit` in `query`, and follow `page.next` as `page.param`. |
143
+ | `teamId` or `baseUrl` with OAuth | Refused by name at construction. |
144
+ | `vercel_api_get`, `vercel_api_mutate` | `vercel_api_read` and `vercel_api_write`: `query` is a JSON object, not name/value pairs, and `teamId: null` replaces `personalAccount: true`. |
145
+ | `verify_project_domain`, `remove_project_domain`, `delete_deployment` | `vercel_api_write` with the operation from `vercel_api_search`, such as `DELETE /v13/deployments/{id}`. |
146
+ | `vercel_api_upload` with any path and headers | `vercel_api_upload` on octet-stream operations in the index (`POST /v2/files`, project avatars, Remote Cache artifacts); Connecta computes `x-vercel-digest`. |
147
+
148
+ The token connector serves `vercel_api_search`, `vercel_api_details`,
149
+ `vercel_api_read`, and `vercel_api_write` over Vercel's OpenAPI document,
150
+ pinned by content hash because Vercel does not version it. Every call is
151
+ checked against the pinned index before it is sent, and results arrive as
152
+ `{ status, data, page? }`. It adds `list_teams`, the value-safe environment
153
+ variable tools (`list_project_env_vars`, `upsert_project_env_var`,
154
+ `update_project_env_var`, `delete_project_env_var`),
155
+ `get_deployment_build_logs` (build events, never following), `get_runtime_logs`
156
+ (the runtime stream, stopped after `waitMs` or `maxRows`), and
157
+ `vercel_api_upload`.
158
+
159
+ Value safety follows a reviewed table (`src/providers/vercel/value-safety.ts`).
160
+ Every operation whose response schema in the pinned spec may carry a credential
161
+ or a stored secret has a verdict: refused (credential minting, Connect
162
+ authorization, decrypted environment values, Global Config items and tokens,
163
+ domain transfer codes, KMS signing), redacted at reviewed field paths
164
+ (environment values, protection-bypass secrets, deploy hook URLs, drain and
165
+ webhook headers and destination URLs beyond their origin, external route and
166
+ redirect destinations beyond their origin, the args and values of every header,
167
+ cookie, and query rule (route transforms and conditions, firewall conditions), team invite codes, synced Global Config items, URL credentials, and signing
168
+ secrets, including the one a new webhook or drain returns at creation), or
169
+ safe with a reason. Detection reads field names, field descriptions, and
170
+ operation descriptions, so a project transfer request (whose code lets another
171
+ team claim the project) is refused too. A test fails when
172
+ a newly flagged operation has no verdict. A key-name heuristic covers every
173
+ other body as defense in depth. Secret-family failures carry fixed messages
174
+ instead of Vercel's text, and every other reviewed operation also withholds that
175
+ text ([value safety](#value-safety-on-every-key-connector)). Read or rotate withheld secrets in the Vercel
176
+ dashboard. A domain move-out is refused because its answer is a transfer token.
177
+
178
+ Lost on the token path: documentation search, runtime error clusters, toolbar
179
+ threads, agent runs, web analytics summaries, purchase quotes and guided
180
+ purchases, `deploy_to_vercel`, and access grants for protected URLs exist only
181
+ on the hosted OAuth connection. Programs calling those tools need an OAuth
182
+ connector beside the token connector.
183
+
184
+ The hosted connection now lists `filter_project_envs`, `get_project_env`,
185
+ `create_project_env`, and `edit_project_env`, which 0.29 hid. The two reads can
186
+ return decrypted values, so they are reviewed as writes, and a `readOnly`
187
+ endpoint refuses them. Vercel admits only reviewed and approved MCP clients to its
188
+ hosted server; if authorization is refused for that reason, the deployment
189
+ needs Vercel's approval or a token connector. The
190
+ [Vercel skill](https://github.com/zackbart/connecta/blob/main/src/providers/vercel/SKILL.md)
191
+ owns the conventions for both.
192
+
193
+ ## Cloudflare
194
+
195
+ Every `cloudflare()` call must now name `auth`. 0.30 defaulted to hosted MCP
196
+ when `surface` was omitted; that default is gone with `surface` itself:
197
+
198
+ ```ts
199
+ connectors: [
200
+ cloudflare("cloudflare", {
201
+ purpose: "Estate changes",
202
+ auth: { type: "oauth" },
203
+ }),
204
+ cloudflare("cloudflare_api", {
205
+ purpose: "Production DNS and Workers",
206
+ auth: { type: "apiToken" },
207
+ accountId: "<account id>",
208
+ zoneId: "<zone id>",
209
+ pin: { accountIds: ["<account id>"] },
210
+ }),
211
+ cloudflare("cloudflare_legacy", {
212
+ purpose: "Legacy zones behind a user key",
213
+ auth: { type: "globalApiKey" },
214
+ pin: { zoneIds: ["<zone id>"] },
215
+ }),
216
+ ];
217
+ ```
218
+
219
+ | 0.30 configuration | Now |
220
+ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
221
+ | No `surface`, or `surface: "mcp"`, with OAuth | `auth: { type: "oauth" }`. Same endpoint, catalog, and grant. |
222
+ | `surface: "mcp"` with `auth: { type: "credential" }` | `auth: { type: "apiToken" }`: bearer tokens no longer reach `mcp.cloudflare.com`. A token stored for the same connector id carries over. |
223
+ | `surface: "api"` (API token) | `auth: { type: "apiToken" }` with the same `accountId`, `zoneId`, `baseUrl`, and `maxConcurrency`. |
224
+ | `surface: "api"`, `authentication: "globalApiKey"` | `auth: { type: "globalApiKey" }` plus `pin: { accountIds, zoneIds }`, or `unpinned: true` to accept the user's whole estate. |
225
+ | `credential` override | Removed; the credential slot's copy is fixed. |
226
+ | `callAdmission` with hosted MCP | Unchanged under `auth: { type: "oauth" }`. |
227
+
228
+ The key connector is complete on its own. JSON writes that 0.30 refused on an
229
+ API token (they belonged to hosted `execute`) go through
230
+ `cloudflare_api_write`, checked against a pinned API index before they are
231
+ sent. The 23 hand-written reads (`get_zone`, `list_dns_records`,
232
+ `list_r2_objects`, `list_pages_deployments`, and the rest) are replaced by
233
+ `cloudflare_api_read` on the same paths; results are Cloudflare's own
234
+ `result`, unprojected, so programs pass `select` instead of reading the old
235
+ projections. `list_accounts`, `list_zones`, and `cloudflare_api_upload` keep
236
+ their names (upload now takes `parts` for multipart); `verify_api_token` and
237
+ `verify_global_api_key` become `verify_credential`; `cloudflare_api_get` and
238
+ `cloudflare_api_mutate` become `cloudflare_api_read` and `cloudflare_api_write`.
239
+ `graphql_query` is new.
240
+
241
+ Credentials stay in the dashboard. The key connector refuses every operation
242
+ that mints, rotates, or returns a credential (API tokens, Access service
243
+ tokens, R2 temporary credentials, signing keys, direct-upload URLs, deploy
244
+ hooks) and redacts stored secrets on every result. A pinned connector refuses
245
+ operations that name no account or zone unless they are reviewed safe, such
246
+ as `/certificates` and `/memberships/{id}`.
247
+
248
+ Read-only endpoints: hosted `execute` is always a write, even for a program
249
+ that only reads, so a `readOnly` endpoint on an OAuth connector reaches only
250
+ `search`.
251
+ Use an API-token connector for read-only API access.
252
+
253
+ Lost on each path. OAuth has no index validation, named tools, GraphQL tool,
254
+ upload tool, or pin; Cloudflare's OAuth grant is the only boundary. A key has
255
+ no hosted `search` over Cloudflare's live OpenAPI document; the pinned index
256
+ replaces it and `providers:check` reports when it falls behind. A Global API
257
+ Key cannot reach R2, which accepts API tokens only. The
258
+ [Cloudflare skill](https://github.com/zackbart/connecta/blob/main/src/providers/cloudflare/SKILL.md)
259
+ owns the conventions for both.
260
+
261
+ ## Value safety on every key connector
262
+
263
+ All four key connectors now share one value-safety mechanism: a reviewed
264
+ table per vendor over every operation the detector flags in the pinned spec,
265
+ one redaction pass on every result, and one error policy
266
+ ([architecture](./architecture.md#value-safety-for-rest-vendors)). Results
267
+ lose fields they returned before. A `"[redacted]"` value is never the real
268
+ one; read or rotate withheld secrets in the vendor's dashboard.
269
+
270
+ - **Stripe** refuses operations that hand out a credential: ephemeral keys,
271
+ Terminal connection tokens, account links, Express login links, account and
272
+ customer sessions, billing portal sessions, file link creation, Financial
273
+ Connections session creation, and the meter event session. Results redact
274
+ client secrets (PaymentIntents, SetupIntents, Checkout and Identity
275
+ sessions, Sources, Financial Connections, invoice confirmation secrets),
276
+ webhook and event destination signing secrets, Apps secret payloads,
277
+ app-install authorization codes, Terminal Wi-Fi passwords, forwarded
278
+ request header values, and Issuing card numbers and CVCs; webhook, file
279
+ link, and pre-signed import URLs keep only their scheme and host, and the
280
+ rules follow each object into expansions, lists, and events (an expanded
281
+ File's links, an event's `previous_attributes`). Identity verification
282
+ URLs are unchanged. A Checkout Session's URL is returned whole only for a
283
+ guest payment session (`mode: "payment"`, no `customer` or
284
+ `customer_account`, no customer creation, no saved payment method options);
285
+ any other session returns only its scheme and host, because its page can
286
+ show, reuse, or remove a customer's saved payment methods. v2 account
287
+ links and Terminal onboarding links are refused too. Issuing `number` and
288
+ `cvc` expansions are refused on every Issuing path, not only cards.
289
+ Failures of credential-bearing operations keep Stripe's type, code, and
290
+ param but not its message. Create client-side sessions and links from your
291
+ own server code.
292
+ - **Notion** results withhold the signature, credential, and security-token
293
+ parameters of pre-signed file URLs, so a returned file URL no longer
294
+ downloads; credential-named fields, including database properties named
295
+ like credentials, read as `"[redacted]"`. Failures of the OAuth, file
296
+ upload, bot user, and agent operations keep Notion's code but not its
297
+ message.
298
+ - **Vercel** replaces environment values with `"[redacted]"` instead of
299
+ dropping the field, and withholds Vercel's error text for every reviewed
300
+ operation and every operation whose request takes a credential-named
301
+ field, not only secret families. Audit event invite codes, integration
302
+ drain headers, and route header values are redacted too.
303
+ - **Cloudflare** redacts Hyperdrive database passwords, RealtimeKit storage
304
+ credentials, transform-rule header values, and dispatch binding values,
305
+ and reduces notification, RealtimeKit, and data security webhook
306
+ destinations to their scheme and host. A credential-named field now loses
307
+ its whole value, not only its strings (a `cookies` list, a variable named
308
+ `API_KEY`).
@@ -11,6 +11,11 @@ BePresent and One&Many use Notion and Cloudflare; their actual grants and
11
11
  connector ids must be checked in deployment configuration before that release.
12
12
  The provider audit establishes available auth modes, not production OAuth grants.
13
13
 
14
+ Cloudflare's part of this guide is superseded: `cloudflare()` now selects its
15
+ implementation by `auth` and its key connector is complete on its own. Follow
16
+ the [provider auth migration](./provider-auth-migration.md#cloudflare) for
17
+ Cloudflare; the Cloudflare lines below record the 0.29 transition only.
18
+
14
19
  All three providers now default to hosted MCP. `surface: "api"` explicitly
15
20
  selects a REST complement and accepts only its API-specific options. The old
16
21
  Vercel/Cloudflare API modes are no longer complete alternatives; Notion retains
@@ -27,6 +32,11 @@ connectors: [
27
32
  ]
28
33
  ```
29
34
 
35
+ Notion has since moved to auth-selected implementations: `surface` is gone,
36
+ and `auth: { type: "oauth" }` or `{ type: "token" }` selects hosted MCP or the
37
+ REST connector. Upgrade with the
38
+ [provider auth migration](./provider-auth-migration.md#notion); the rest of
39
+ this paragraph records the 0.29 rules.
30
40
  Notion preserves headless/internal-integration capabilities. Add explicit
31
41
  `surface: "api"` to retain that identity and migrate old REST names to
32
42
  `integration_*`, for example `search` to `integration_search`, `create_page`
@@ -48,9 +58,11 @@ complement. Replace `update_r2_bucket` with `cloudflare_api_mutate` using
48
58
  `cf-r2-storage-class` set to `Standard` or `InfrequentAccess`. Omit the body;
49
59
  include `cf-r2-jurisdiction` only when the bucket needs it. This remains a write.
50
60
  `execute` remains a write even when its program only calls GET.
51
- Trusted pools may execute writes; read-only pools use direct host-approved writes.
61
+ Writable endpoints may run `execute`; `readOnly` endpoints refuse it.
52
62
 
53
- For Vercel, authorize the owning teams in hosted OAuth, migrate discovery/logs,
63
+ Vercel's `surface` and ownership rules were later replaced: `auth` now selects
64
+ the implementation. See the [provider auth migration](./provider-auth-migration.md#vercel).
65
+ The 0.29 Vercel guidance follows for history. For Vercel, authorize the owning teams in hosted OAuth, migrate discovery/logs,
54
66
  domain listing/adding, deployment cancellation and file uploads to live MCP
55
67
  schemas, and keep the independently authorized REST complement for value-safe
56
68
  project environment variables (including `upsert: false` create-only writes), domain verification/removal and deployment
@@ -59,7 +71,7 @@ category. Raw hatches refuse all published REST counterparts, including project
59
71
  creation/update. Do not copy REST argument
60
72
  shapes into vendor tools, even when a tool name is unchanged.
61
73
 
62
- The per-provider tables map every removed name and explain retained API tools:
63
- [Notion](https://github.com/zackbart/connecta/blob/main/src/providers/notion/reconciliation.md),
64
- [Vercel](https://github.com/zackbart/connecta/blob/main/src/providers/vercel/reconciliation.md), and
65
- [Cloudflare](https://github.com/zackbart/connecta/blob/main/src/providers/cloudflare/reconciliation.md).
74
+ Every provider's table is retired with `surface`; the provider auth migration
75
+ maps their names ([Notion](./provider-auth-migration.md#notion),
76
+ [Cloudflare](./provider-auth-migration.md#cloudflare),
77
+ [Vercel](./provider-auth-migration.md#vercel)).
@@ -37,6 +37,11 @@ This repository is deployment configuration, not a copy of Connecta itself.
37
37
  Import it from `@zackbart/connecta/worker`; direct upstream
38
38
  `new DynamicWorkerExecutor()` construction throws at boot. Keep the
39
39
  `@cloudflare/codemode` optional peer installed.
40
+ - Keep the required `readOnly` choice in `src/connecta.config.ts`; startup
41
+ fails without it. `true` omits write tools from search and describe and
42
+ refuses every write call; `false` lets them run with MCP host approval. Named
43
+ pools inherit it unless they set their own `readOnly`. Do not configure the
44
+ removed `trust`.
40
45
  - Keep credentials in Worker secrets. Never commit credential values, Access
41
46
  service-token secrets, or `CREDENTIAL_ENCRYPTION_KEY`.
42
47
  - All state lives in the one D1 database bound as `CONNECTA_DB`, through
@@ -316,8 +316,8 @@ Rolling back to 0.24.x then requires reauthorizing those OAuth connectors.
316
316
 
317
317
  The shipped Notion connector uses a deployment-owned static header and echo
318
318
  needs no secret. To exercise vault controls, declare a `credential` slot on an
319
- `api()` connector or use Notion's explicit `surface: "api"` integration
320
- interface, which declares its own slot.
319
+ `api()` connector or use a Notion connector with `auth: { type: "token" }`,
320
+ which declares its own slot.
321
321
  Authorized users manage that slot inside the connection. Configuring a vault
322
322
  does not create credentials or permissions by itself.
323
323
 
@@ -69,6 +69,11 @@ export default defineConfig((env: Env) => {
69
69
  return {
70
70
  publicUrl: env.PUBLIC_URL,
71
71
  storage,
72
+ // Required: can /mcp write? false lets call_destructive_tool and programs
73
+ // write, with the MCP client approving; true leaves write tools out of
74
+ // search and describe and refuses every write call. Pools inherit it
75
+ // unless they set their own readOnly. Startup fails without it.
76
+ readOnly: false,
72
77
  // Required adapter owns each run's handles; direct upstream construction throws.
73
78
  executor: workerExecutor({ loader: env.LOADER }),
74
79
  // Access owns edge admission and proves human identity for MCP and the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zackbart/connecta",
3
- "version": "0.30.0",
3
+ "version": "0.32.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.",
@@ -157,6 +157,10 @@
157
157
  "types": "./dist/providers/vercel/index.d.ts",
158
158
  "import": "./dist/providers/vercel/index.js"
159
159
  },
160
+ "./providers/wispr-flow": {
161
+ "types": "./dist/providers/wispr-flow/index.d.ts",
162
+ "import": "./dist/providers/wispr-flow/index.js"
163
+ },
160
164
  "./ui": {
161
165
  "types": "./dist/ui.d.ts",
162
166
  "import": "./dist/ui.js"
@@ -227,7 +231,8 @@
227
231
  "eval:regrade": "tsx eval/regrade.ts",
228
232
  "eval:compare": "tsx eval/compare.ts",
229
233
  "format": "oxfmt --write .",
230
- "format:check": "oxfmt --check ."
234
+ "format:check": "oxfmt --check .",
235
+ "providers:spec": "tsx scripts/generate-openapi.mjs"
231
236
  },
232
237
  "dependencies": {
233
238
  "@cfworker/json-schema": "^4.1.1",
@@ -5,10 +5,12 @@ This repository is deployment configuration, not a copy of Connecta itself.
5
5
  - Edit `src/connecta.config.ts` for connectors, authentication, storage, public
6
6
  URL, and optional modules. `src/index.ts` only starts it.
7
7
  - Keep `executor: quickJsExecutor()` for the prescribed six-tool code-first
8
- surface. Root and named pools default to `trust: "read-only"`: programs
9
- may read, and writes use `call_destructive_tool`. Explicit `trusted` pools
10
- also permit program writes and annotate `execute_code` as a write. Approval
11
- belongs to the MCP host. Do not configure the removed `execute.approval`.
8
+ surface. Keep the required `readOnly` choice in the config: `false` lets
9
+ `call_destructive_tool` and programs write and annotates `execute_code` as
10
+ a write; `true` omits write tools from search and describe and refuses every
11
+ write call. Named pools inherit it unless they set their own `readOnly`.
12
+ Approval belongs to the MCP host. Do not configure the removed `trust` or
13
+ `execute.approval`.
12
14
  - Keep credentials in environment variables or an external secret store.
13
15
  Never commit `.env`, `.connecta.sqlite` (and its `-wal`/`-shm` files),
14
16
  tokens, or credential values.
@@ -125,8 +125,10 @@ stored machine token to inspect Connections. Sign in with Clerk for human
125
125
  connection management. Omit that option and import for an API-only
126
126
  server. OAuth callbacks remain in core even with no UI.
127
127
 
128
- The page labels each tool as a read or write. Trusted programs may write;
129
- read-only pools use `call_destructive_tool`, with approval controlled by the host. For
128
+ The page labels each tool as a read or write and each endpoint as writable or
129
+ read-only. `src/connecta.config.ts` must set `readOnly`: a read-only endpoint
130
+ refuses every write call and leaves write tools out of search, and the host
131
+ controls approval on a writable one. For
130
132
  classified connection failures it offers a fixed repair prompt, and its
131
133
  endpoint section has client setup commands for `/mcp` and any pool available
132
134
  to the signed-in identity. Those commands contain no bearer token.
@@ -188,8 +190,8 @@ node -e "console.log(crypto.randomBytes(32).toString('base64'))"
188
190
  Keep this key outside the database. Losing it makes saved values unreadable;
189
191
  upgrades must reuse it. The shipped `time` connector declares no credential
190
192
  slot. Add `credential: { label: "API token" }` to an `api()` connector and read
191
- it through `await ctx.credential?.get()`, or use Notion's explicit
192
- `surface: "api"` integration interface, which declares its own slot. Authorized humans manage the slot inside that
193
+ it through `await ctx.credential?.get()`, or use a Notion
194
+ connector with `auth: { type: "token" }`, which declares its own slot. Authorized humans manage the slot inside that
193
195
  connection on `/`; there is no separate Credentials tab.
194
196
 
195
197
  In 0.25.0, this vault also seals downstream OAuth tokens on their first read.
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@clerk/backend": "^3.12.0",
20
- "@zackbart/connecta": "0.30.0",
20
+ "@zackbart/connecta": "0.32.0",
21
21
  "quickjs-emscripten": "0.32.0"
22
22
  },
23
23
  "devDependencies": {
@@ -68,6 +68,11 @@ export default defineConfig((env: Env) => {
68
68
  return {
69
69
  storage,
70
70
  publicUrl,
71
+ // Required: can /mcp write? false lets call_destructive_tool and programs
72
+ // write, with the MCP client approving; true leaves write tools out of
73
+ // search and describe and refuses every write call. Pools inherit it
74
+ // unless they set their own readOnly. Startup fails without it.
75
+ readOnly: false,
71
76
  // Required: model-written programs run in a bounded QuickJS child.
72
77
  executor: quickJsExecutor(),
73
78
  auth: clerk ? [clerk] : [],