@zackbart/connecta 0.28.1 → 0.29.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 (319) hide show
  1. package/CHANGELOG.md +788 -5
  2. package/README.md +62 -42
  3. package/bin/connecta.mjs +110 -139
  4. package/bin/meta-tool-names.d.mts +2 -0
  5. package/bin/meta-tool-names.mjs +9 -0
  6. package/bin/version.mjs +3 -0
  7. package/dist/access-tokens.js +53 -63
  8. package/dist/activity-request.js +8 -0
  9. package/dist/activity.d.ts +76 -42
  10. package/dist/activity.js +110 -42
  11. package/dist/auth/clerk-sdk/client.d.ts +65 -0
  12. package/dist/auth/clerk-sdk/client.js +5 -0
  13. package/dist/auth/clerk-sdk/generated.js +1845 -0
  14. package/dist/auth/clerk-transport.js +19 -0
  15. package/dist/auth/clerk.d.ts +17 -7
  16. package/dist/auth/clerk.js +189 -107
  17. package/dist/auth/cloudflare-access.js +25 -26
  18. package/dist/auth/downstream-client-metadata.d.ts +29 -0
  19. package/dist/auth/downstream-client-metadata.js +133 -0
  20. package/dist/auth/downstream-oauth.js +1584 -1580
  21. package/dist/auth/downstream-revocation.js +106 -0
  22. package/dist/auth/oauth-token-response.js +231 -0
  23. package/dist/auth/oauth-v2-migration.js +116 -0
  24. package/dist/auth/static-oauth.js +93 -71
  25. package/dist/auth-elicitation.js +283 -0
  26. package/dist/branding.d.ts +8 -16
  27. package/dist/branding.js +26 -59
  28. package/dist/byte-read-response.js +39 -0
  29. package/dist/call-admission.js +12 -16
  30. package/dist/catalog-cache.js +739 -0
  31. package/dist/catalog-drift.d.ts +50 -15
  32. package/dist/catalog-drift.js +365 -128
  33. package/dist/catalog-fingerprint.js +2 -4
  34. package/dist/catalog-freshness.js +13 -0
  35. package/dist/catalog-limits.js +2 -2
  36. package/dist/catalog-service.js +368 -224
  37. package/dist/catalog.js +43 -84
  38. package/dist/concurrency.js +3 -4
  39. package/dist/config-defaults.d.ts +39 -0
  40. package/dist/config-defaults.js +46 -0
  41. package/dist/config-schema.d.ts +181 -0
  42. package/dist/config-schema.js +435 -0
  43. package/dist/config-value-sources.js +102 -0
  44. package/dist/config.d.ts +631 -0
  45. package/dist/config.js +503 -0
  46. package/dist/connector-access.d.ts +1 -0
  47. package/dist/connector-access.js +13 -15
  48. package/dist/connector-caller.d.ts +8 -0
  49. package/dist/connector-caller.js +15 -6
  50. package/dist/connector-scope.js +1 -1
  51. package/dist/connectors/api-connector.d.ts +59 -7
  52. package/dist/connectors/api-connector.js +106 -19
  53. package/dist/connectors/api.js +4 -1
  54. package/dist/connectors/guarded-fetch.d.ts +144 -0
  55. package/dist/connectors/guarded-fetch.js +84 -35
  56. package/dist/connectors/negotiation-cache.js +46 -0
  57. package/dist/connectors/option-shapes.js +27 -0
  58. package/dist/connectors/remote-mcp.d.ts +30 -4
  59. package/dist/connectors/remote-mcp.js +1774 -320
  60. package/dist/connectors/resource-uri.js +335 -0
  61. package/dist/credential-contract.d.ts +10 -0
  62. package/dist/credential-rules.js +1 -3
  63. package/dist/credential-url.js +108 -0
  64. package/dist/credentials.d.ts +6 -1
  65. package/dist/credentials.js +27 -18
  66. package/dist/d1.d.ts +130 -0
  67. package/dist/d1.js +290 -0
  68. package/dist/describe-config.d.ts +165 -0
  69. package/dist/describe-config.js +315 -0
  70. package/dist/described.js +106 -0
  71. package/dist/downstream-input-context.js +119 -0
  72. package/dist/downstream-input.js +323 -0
  73. package/dist/errors.d.ts +97 -20
  74. package/dist/errors.js +158 -80
  75. package/dist/execute.js +453 -305
  76. package/dist/executor-admission.d.ts +1 -0
  77. package/dist/executor-admission.js +11 -15
  78. package/dist/executor-contract.d.ts +20 -0
  79. package/dist/executor-contract.js +48 -0
  80. package/dist/executor-result.js +24 -9
  81. package/dist/executors/quickjs-child.js +3 -4
  82. package/dist/executors/quickjs-runtime.js +147 -59
  83. package/dist/executors/quickjs.js +29 -35
  84. package/dist/favicon.js +1 -2
  85. package/dist/fix-prompt.js +1 -3
  86. package/dist/guest-runtime.js +188 -0
  87. package/dist/guest-types.d.ts +176 -0
  88. package/dist/guest-types.js +1 -0
  89. package/dist/html-security.js +24 -0
  90. package/dist/identity.js +1 -3
  91. package/dist/inbound-credential.js +52 -0
  92. package/dist/index.d.ts +24 -256
  93. package/dist/index.js +65 -478
  94. package/dist/invocation-auth.js +69 -0
  95. package/dist/invocation.js +230 -73
  96. package/dist/json-schema.d.ts +1 -1
  97. package/dist/mcp-client-context.js +35 -0
  98. package/dist/mcp-result.js +11 -7
  99. package/dist/meta-output.js +76 -0
  100. package/dist/meta-tool-names.d.ts +2 -0
  101. package/dist/meta-tool-names.js +3 -0
  102. package/dist/meta-tools.js +285 -209
  103. package/dist/module-contracts.d.ts +8 -23
  104. package/dist/node.d.ts +0 -2
  105. package/dist/node.js +2 -2
  106. package/dist/oauth-callback-outcome.js +1 -3
  107. package/dist/oauth-handoff.js +117 -0
  108. package/dist/oauth-sealing.js +20 -0
  109. package/dist/operator-record.d.ts +137 -0
  110. package/dist/operator-record.js +420 -0
  111. package/dist/operator-ui/assets.js +31 -0
  112. package/dist/operator-ui/contract.d.ts +72 -0
  113. package/dist/operator-ui/contract.js +1 -0
  114. package/dist/operator-ui/fix-prompts.d.ts +18 -0
  115. package/dist/operator-ui/generated.js +5 -4
  116. package/dist/operator-ui/model.d.ts +12 -87
  117. package/dist/operator-ui/model.js +2 -9
  118. package/dist/operator-ui/setup-commands.js +1 -3
  119. package/dist/operator-ui/view.d.ts +323 -0
  120. package/dist/operator-ui/view.js +62 -189
  121. package/dist/page-styles.js +2 -3
  122. package/dist/program-source.js +14 -2
  123. package/dist/{exempt-writes.js → program-writes.js} +55 -18
  124. package/dist/provider.d.ts +142 -0
  125. package/dist/provider.js +187 -0
  126. package/dist/providers/_shared/google/result-size.js +57 -0
  127. package/dist/providers/_shared/google/service-account.d.ts +58 -0
  128. package/dist/providers/_shared/google/service-account.js +500 -0
  129. package/dist/providers/_shared/google/workspace.d.ts +276 -0
  130. package/dist/providers/_shared/google/workspace.js +518 -0
  131. package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
  132. package/dist/providers/basecamp/index.js +997 -0
  133. package/dist/providers/basecamp/skill.generated.js +9 -0
  134. package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
  135. package/dist/providers/{breeze.js → breeze/index.js} +184 -140
  136. package/dist/providers/breeze/skill.generated.js +9 -0
  137. package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
  138. package/dist/providers/{ccb.js → ccb/index.js} +271 -116
  139. package/dist/providers/ccb/skill.generated.js +9 -0
  140. package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
  141. package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
  142. package/dist/providers/cloudflare/skill.generated.js +10 -0
  143. package/dist/providers/docs/index.d.ts +54 -0
  144. package/dist/providers/docs/index.js +1237 -0
  145. package/dist/providers/docs/skill.generated.js +9 -0
  146. package/dist/providers/drive/index.d.ts +53 -0
  147. package/dist/providers/drive/index.js +1523 -0
  148. package/dist/providers/drive/skill.generated.js +9 -0
  149. package/dist/providers/forms/index.d.ts +54 -0
  150. package/dist/providers/forms/index.js +1217 -0
  151. package/dist/providers/forms/skill.generated.js +11 -0
  152. package/dist/providers/github/auth.d.ts +32 -0
  153. package/dist/providers/github/auth.js +361 -0
  154. package/dist/providers/github/hosted.js +218 -0
  155. package/dist/providers/github/index.d.ts +13 -0
  156. package/dist/providers/github/index.js +140 -0
  157. package/dist/providers/github/key.js +100 -0
  158. package/dist/providers/github/rest.js +321 -0
  159. package/dist/providers/github/scope.d.ts +29 -0
  160. package/dist/providers/github/scope.js +124 -0
  161. package/dist/providers/github/skill.generated.js +8 -0
  162. package/dist/providers/gmail/index.d.ts +51 -0
  163. package/dist/providers/gmail/index.js +1436 -0
  164. package/dist/providers/gmail/skill.generated.js +9 -0
  165. package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
  166. package/dist/providers/linear/index.js +352 -0
  167. package/dist/providers/linear/skill.generated.js +8 -0
  168. package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
  169. package/dist/providers/mixpanel/index.js +416 -0
  170. package/dist/providers/mixpanel/skill.generated.js +9 -0
  171. package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
  172. package/dist/providers/{notion.js → notion/index.js} +226 -239
  173. package/dist/providers/notion/skill.generated.js +10 -0
  174. package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
  175. package/dist/providers/{overflow.js → overflow/index.js} +74 -111
  176. package/dist/providers/overflow/skill.generated.js +8 -0
  177. package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
  178. package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
  179. package/dist/providers/planning-center/skill.generated.js +10 -0
  180. package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
  181. package/dist/providers/revenuecat/index.js +557 -0
  182. package/dist/providers/revenuecat/skill.generated.js +11 -0
  183. package/dist/providers/sheets/index.d.ts +54 -0
  184. package/dist/providers/sheets/index.js +1237 -0
  185. package/dist/providers/sheets/skill.generated.js +10 -0
  186. package/dist/providers/slides/index.d.ts +53 -0
  187. package/dist/providers/slides/index.js +2645 -0
  188. package/dist/providers/slides/skill.generated.js +9 -0
  189. package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
  190. package/dist/providers/stripe/index.js +266 -0
  191. package/dist/providers/stripe/skill.generated.js +11 -0
  192. package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
  193. package/dist/providers/{tithely.js → tithely/index.js} +60 -91
  194. package/dist/providers/tithely/skill.generated.js +8 -0
  195. package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
  196. package/dist/providers/vercel/index.js +1038 -0
  197. package/dist/providers/vercel/mcp-ownership.js +212 -0
  198. package/dist/providers/vercel/skill.generated.js +11 -0
  199. package/dist/registry.d.ts +108 -171
  200. package/dist/registry.js +513 -925
  201. package/dist/request-state.js +21 -0
  202. package/dist/resource-read.js +100 -0
  203. package/dist/result-shapes.js +17 -49
  204. package/dist/retired-toolkits.js +1 -1
  205. package/dist/routes/access-tokens.js +3 -6
  206. package/dist/routes/activity-context.js +21 -0
  207. package/dist/routes/activity-disclosure.js +40 -0
  208. package/dist/routes/activity.js +59 -21
  209. package/dist/routes/actor-labels.js +3 -50
  210. package/dist/routes/connect.js +221 -0
  211. package/dist/routes/credentials.js +15 -27
  212. package/dist/routes/mcp.js +356 -130
  213. package/dist/routes/oauth-client-metadata.js +15 -0
  214. package/dist/routes/oauth-management.js +45 -107
  215. package/dist/routes/oauth.js +110 -57
  216. package/dist/routes/operator.js +3 -3
  217. package/dist/routes/shared.d.ts +25 -64
  218. package/dist/routes/shared.js +160 -80
  219. package/dist/routes/skills.js +30 -0
  220. package/dist/routes/ui-config.js +225 -0
  221. package/dist/routes/ui-data.js +40 -35
  222. package/dist/routes/ui.js +66 -56
  223. package/dist/runtime/admission.js +4 -2
  224. package/dist/runtime/call-admission.js +3 -1
  225. package/dist/runtime/connector-scope.js +1 -4
  226. package/dist/runtime/run.js +10 -39
  227. package/dist/runtime/services.js +12 -117
  228. package/dist/runtime/shared-read.js +1 -3
  229. package/dist/runtime/storage.js +8 -1
  230. package/dist/sent-secrets.d.ts +52 -0
  231. package/dist/sent-secrets.js +474 -0
  232. package/dist/server.js +28 -71
  233. package/dist/skills.js +406 -174
  234. package/dist/sqlite.d.ts +48 -0
  235. package/dist/sqlite.js +166 -0
  236. package/dist/storage/keys.js +428 -0
  237. package/dist/storage/memory.js +7 -0
  238. package/dist/storage/sql.d.ts +66 -0
  239. package/dist/storage/sql.js +534 -0
  240. package/dist/timeout.js +4 -2
  241. package/dist/tool-name.js +17 -0
  242. package/dist/tool-safety.d.ts +9 -35
  243. package/dist/tool-safety.js +20 -40
  244. package/dist/types.d.ts +320 -54
  245. package/dist/types.js +0 -1
  246. package/dist/ui.d.ts +7 -19
  247. package/dist/ui.js +76 -90
  248. package/dist/url-safety.js +3 -6
  249. package/dist/usage-guide.js +137 -0
  250. package/dist/validate.d.ts +7 -0
  251. package/dist/validate.js +309 -47
  252. package/dist/version.d.ts +1 -7
  253. package/dist/version.js +3 -7
  254. package/dist/worker.d.ts +11 -0
  255. package/dist/worker.js +434 -0
  256. package/documentation/architecture.md +664 -231
  257. package/documentation/auth.md +936 -546
  258. package/documentation/code-mode.md +501 -312
  259. package/documentation/meta-tools.md +449 -200
  260. package/documentation/operator-tests.md +93 -0
  261. package/documentation/provider-migration-0.29.md +60 -0
  262. package/examples/worker/AGENTS.md +34 -12
  263. package/examples/worker/README.md +286 -255
  264. package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
  265. package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
  266. package/examples/worker/src/connecta.config.ts +151 -0
  267. package/examples/worker/src/index.ts +14 -197
  268. package/examples/worker/wrangler.jsonc +15 -35
  269. package/package.json +120 -54
  270. package/templates/node/.dockerignore +1 -2
  271. package/templates/node/.env.example +20 -21
  272. package/templates/node/AGENTS.md +21 -17
  273. package/templates/node/Dockerfile +3 -5
  274. package/templates/node/README.md +133 -58
  275. package/templates/node/docker-compose.yml +11 -12
  276. package/templates/node/package.json +4 -2
  277. package/templates/node/src/connecta.config.ts +119 -0
  278. package/templates/node/src/index.ts +9 -154
  279. package/templates/node/src/provision-token.ts +17 -0
  280. package/AGENTS.md +0 -170
  281. package/dist/artifacts/connector.d.ts +0 -58
  282. package/dist/artifacts/connector.js +0 -730
  283. package/dist/artifacts/document.js +0 -86
  284. package/dist/artifacts/guide.js +0 -140
  285. package/dist/artifacts/html-scan.js +0 -329
  286. package/dist/artifacts/json.js +0 -78
  287. package/dist/artifacts/kv-store.d.ts +0 -20
  288. package/dist/artifacts/kv-store.js +0 -140
  289. package/dist/artifacts/markdown.js +0 -478
  290. package/dist/artifacts/module.d.ts +0 -37
  291. package/dist/artifacts/module.js +0 -66
  292. package/dist/artifacts/operations.d.ts +0 -197
  293. package/dist/artifacts/operations.js +0 -1002
  294. package/dist/artifacts/refresh.d.ts +0 -55
  295. package/dist/artifacts/refresh.js +0 -236
  296. package/dist/artifacts/routes.js +0 -206
  297. package/dist/artifacts/types.d.ts +0 -214
  298. package/dist/artifacts/types.js +0 -22
  299. package/dist/artifacts/validate.d.ts +0 -62
  300. package/dist/artifacts/validate.js +0 -651
  301. package/dist/artifacts.d.ts +0 -6
  302. package/dist/artifacts.js +0 -9
  303. package/dist/auth/bearer.d.ts +0 -11
  304. package/dist/auth/bearer.js +0 -50
  305. package/dist/providers/basecamp.js +0 -445
  306. package/dist/providers/linear.js +0 -217
  307. package/dist/providers/mixpanel.js +0 -247
  308. package/dist/providers/revenuecat.js +0 -317
  309. package/dist/providers/stripe.js +0 -253
  310. package/dist/providers/vercel.js +0 -1253
  311. package/dist/storage/file.d.ts +0 -15
  312. package/dist/storage/file.js +0 -455
  313. package/ethos.md +0 -93
  314. package/examples/worker/src/cloudflare-kv.ts +0 -43
  315. package/examples/worker/src/d1-activity-row.ts +0 -155
  316. package/examples/worker/src/d1-activity.ts +0 -147
  317. package/examples/worker/src/d1-storage.ts +0 -138
  318. package/examples/worker/src/r2-artifact-blobs.ts +0 -29
  319. package/templates/node/src/file-activity.ts +0 -182
@@ -0,0 +1,10 @@
1
+ // Generated by scripts/generate-providers.mjs; do not edit.
2
+ export const skill = {
3
+ "content": "\n## Identity before routing\n\nThis explicit `surface: \"api\"` connection acts as an internal integration.\nIts `integration_*` tools are canonical for bot-scoped/headless operations.\nHosted MCP is the default and acts as its OAuth-authorized user. Authorization\nis not equivalent: an integration token cannot authorize hosted MCP, and user\nOAuth cannot reproduce the integration bot's ownership/sharing boundary.\nConfigure each under a separate connector id and authorize it independently.\nDo not switch to a user identity to recover a failed integration operation.\n\nUse the bot tools when the task requires that integration identity. Use hosted\n`notion-search`, `notion-fetch`, `notion-get-users`, `notion-get-comments`,\n`notion-create-pages`, `notion-update-page`, and `notion-create-comment` for\nuser-owned content work. Hosted MCP additionally covers sessions, agents,\nattachments and broader workspace operations. REST tool names are not MCP aliases.\n\n\n\n## Databases contain data sources\n\nA Notion database is a container; the rows and the schema live in a *data\nsource* inside it. The two ids are not interchangeable and Notion rejects the\nwrong one.\n\n- The id in a Notion database URL is a **database id**.\n- `integration_get_database` turns it into the `data_sources` list — usually one entry.\n- `integration_get_data_source_schema` and `integration_query_data_source` take that\n **data_source_id**, and so does `integration_create_page` when adding a row.\n\nSo the sequence for \"find rows in this database\" is `integration_get_database` →\n`integration_get_data_source_schema` → `integration_query_data_source`. `integration_search` returns data\nsources directly and skips the first step.\n\n## Property quirks that break writes\n\n- Property names in filters, sorts, and writes must match the schema exactly,\n including case. Read `integration_get_data_source_schema` before composing one.\n- `select` and `status` writes must use an existing option name; inventing\n one fails. The schema lists the valid options.\n- Writes **replace** a property. Sending one item to a `multi_select` or\n `relation` drops the others, so send the complete intended value.\n- `rollup`, `formula`, `unique_id`, and the created/edited fields are\n computed. They cannot be written.\n- A page's title column is rarely called \"title\" — pass `title_property` from\n the schema when creating or updating a row.\n- Notion truncates `title`, `rich_text`, `relation`, and `people` at 25\n entries. `integration_get_page` reports each one in `truncated_properties` as\n `{ name, id }`; pass that `id` as `property_id` to\n `integration_get_page_property` for the complete value.\n\n## Reading page content\n\n`integration_get_page` returns properties. `integration_get_page_content` returns the body as flat\nblocks. Nested blocks (toggles, list children, table rows) need `depth`, and\neach level multiplies requests — a deep read stops at an internal ceiling and\nreports `truncated: true` rather than spending the whole rate-limit budget.\n\n## Appending is append-only\n\n`integration_append_blocks` adds children and nothing else. It cannot move, reorder, or\nreplace an existing block, and a block appended through the API can never be\nrelocated by it afterwards. Get the position right the first time with\n`position` and `after_block_id`. Notion caps one call at 100 blocks.\n\n## Lean by default, raw on request\n\nEvery read projects Notion's payload down to ids, plain text, and flattened\nproperty values. Where the dropped detail can matter — `integration_search`, `integration_get_page`,\n`integration_get_page_content`, `integration_get_page_property`, `integration_get_data_source_schema`,\n`integration_query_data_source`, `integration_list_comments` — pass `raw: true` to get Notion's\nuntouched response instead. It is much larger; reach for it only when a\nspecific field is missing. Narrow `integration_query_data_source` and `integration_get_page` with\n`properties` instead whenever the goal is fewer fields, not more.\n\n`integration_get_page_content` with `raw: true` returns one level exactly as Notion\nsent it and does not walk nested children, so `depth` is ignored alongside\nit. A block type this projection does not model keeps its payload under\n`raw` on the block itself, so nothing silently flattens to an empty string.\n\n## Failures worth reading carefully\n\n- **404** means \"no such object\" *or* \"not shared with this integration\", and\n Notion will not say which. Never conclude a page was deleted from it; check\n that the page is shared with the integration in Notion.\n- **403** is not an expired token. The integration is missing a capability\n (comment capabilities are off by default) or the object was never shared.\n Re-authorizing cannot fix it; an operator must change it in Notion.\n- **429** carries a retry window. Notion allows roughly three requests per\n second per integration, so wait it out rather than retrying immediately.\n\n## No escape hatch\n\nThis connection has no guarded raw-REST tool, deliberately. Notion's public\nAPI is small and slow-moving enough for the named surface to cover it, so\nthere is no `notion_api_*` to reach for — an operation absent from the tool\nlist is absent from this connection, not hidden behind a generic call.\n\n## Writes and pagination\n\n- Notion has **no idempotency key**. A retried `integration_create_page` or\n `integration_add_comment` creates a duplicate. Confirm with `integration_search` before repeating\n a write that may have partially succeeded.\n- List tools take `page_size` (max 100) and return `has_more` with\n `next_cursor`. Pass a cursor back verbatim — it is opaque and must never be\n parsed or constructed. Follow pages inside `execute_code` and reduce there.\n# Notion MCP usage\n\nOfficial MCP interface: tool names, descriptions, argument schemas, and result\nschemas come from Notion's live server. Connecta preserves that catalog and\napplies release-reviewed classification. Reviewed writes stay writes even when Notion claims they only read.\n\nWorkspace purpose: \n\n- This connection acts as its OAuth-authorized user. The separately configured\n `surface: \"api\"` connection retains required internal-integration operations\n under `integration_*` names, including headless content writes, cursor-complete\n queries, exact JSON blocks and property items. No identity fallback occurs.\n- Discover the live catalog before assuming a tool exists. Notion can gate\n tools by workspace, account, client, and rollout independently of Connecta.\n- Start broad discovery with `notion-search`, then use `notion-fetch` on\n the exact page, database, data source, or object before changing it.\n- Use the live input schema as the contract. Notion owns these MCP schemas;\n the REST schemas in Connecta's API interface do not apply to MCP tools with\n similar names.\n- Session and agent tools can launch asynchronous work. Read session state and\n events before sending another message, waiting, or stopping a session.\n- File uploads and attachment tools create durable workspace state. Keep\n downloads inside the requested task and do not expose signed attachment URLs.\n- An `auth_required` failure means this connector's OAuth grant is missing or\n expired. Run `authorize_connector` for this connector id, then retry.\n",
4
+ "instructionsHeading": "Workspace instructions",
5
+ "fragments": {
6
+ "guide_0": "\n## Identity before routing\n\nThis explicit `surface: \"api\"` connection acts as an internal integration.\nIts `integration_*` tools are canonical for bot-scoped/headless operations.\nHosted MCP is the default and acts as its OAuth-authorized user. Authorization\nis not equivalent: an integration token cannot authorize hosted MCP, and user\nOAuth cannot reproduce the integration bot's ownership/sharing boundary.\nConfigure each under a separate connector id and authorize it independently.\nDo not switch to a user identity to recover a failed integration operation.\n\nUse the bot tools when the task requires that integration identity. Use hosted\n`notion-search`, `notion-fetch`, `notion-get-users`, `notion-get-comments`,\n`notion-create-pages`, `notion-update-page`, and `notion-create-comment` for\nuser-owned content work. Hosted MCP additionally covers sessions, agents,\nattachments and broader workspace operations. REST tool names are not MCP aliases.\n\n\n\n## Databases contain data sources\n\nA Notion database is a container; the rows and the schema live in a *data\nsource* inside it. The two ids are not interchangeable and Notion rejects the\nwrong one.\n\n- The id in a Notion database URL is a **database id**.\n- `integration_get_database` turns it into the `data_sources` list — usually one entry.\n- `integration_get_data_source_schema` and `integration_query_data_source` take that\n **data_source_id**, and so does `integration_create_page` when adding a row.\n\nSo the sequence for \"find rows in this database\" is `integration_get_database` →\n`integration_get_data_source_schema` → `integration_query_data_source`. `integration_search` returns data\nsources directly and skips the first step.\n\n## Property quirks that break writes\n\n- Property names in filters, sorts, and writes must match the schema exactly,\n including case. Read `integration_get_data_source_schema` before composing one.\n- `select` and `status` writes must use an existing option name; inventing\n one fails. The schema lists the valid options.\n- Writes **replace** a property. Sending one item to a `multi_select` or\n `relation` drops the others, so send the complete intended value.\n- `rollup`, `formula`, `unique_id`, and the created/edited fields are\n computed. They cannot be written.\n- A page's title column is rarely called \"title\" — pass `title_property` from\n the schema when creating or updating a row.\n- Notion truncates `title`, `rich_text`, `relation`, and `people` at 25\n entries. `integration_get_page` reports each one in `truncated_properties` as\n `{ name, id }`; pass that `id` as `property_id` to\n `integration_get_page_property` for the complete value.\n\n## Reading page content\n\n`integration_get_page` returns properties. `integration_get_page_content` returns the body as flat\nblocks. Nested blocks (toggles, list children, table rows) need `depth`, and\neach level multiplies requests — a deep read stops at an internal ceiling and\nreports `truncated: true` rather than spending the whole rate-limit budget.\n\n## Appending is append-only\n\n`integration_append_blocks` adds children and nothing else. It cannot move, reorder, or\nreplace an existing block, and a block appended through the API can never be\nrelocated by it afterwards. Get the position right the first time with\n`position` and `after_block_id`. Notion caps one call at 100 blocks.\n\n## Lean by default, raw on request\n\nEvery read projects Notion's payload down to ids, plain text, and flattened\nproperty values. Where the dropped detail can matter — `integration_search`, `integration_get_page`,\n`integration_get_page_content`, `integration_get_page_property`, `integration_get_data_source_schema`,\n`integration_query_data_source`, `integration_list_comments` — pass `raw: true` to get Notion's\nuntouched response instead. It is much larger; reach for it only when a\nspecific field is missing. Narrow `integration_query_data_source` and `integration_get_page` with\n`properties` instead whenever the goal is fewer fields, not more.\n\n`integration_get_page_content` with `raw: true` returns one level exactly as Notion\nsent it and does not walk nested children, so `depth` is ignored alongside\nit. A block type this projection does not model keeps its payload under\n`raw` on the block itself, so nothing silently flattens to an empty string.\n\n## Failures worth reading carefully\n\n- **404** means \"no such object\" *or* \"not shared with this integration\", and\n Notion will not say which. Never conclude a page was deleted from it; check\n that the page is shared with the integration in Notion.\n- **403** is not an expired token. The integration is missing a capability\n (comment capabilities are off by default) or the object was never shared.\n Re-authorizing cannot fix it; an operator must change it in Notion.\n- **429** carries a retry window. Notion allows roughly three requests per\n second per integration, so wait it out rather than retrying immediately.\n\n## No escape hatch\n\nThis connection has no guarded raw-REST tool, deliberately. Notion's public\nAPI is small and slow-moving enough for the named surface to cover it, so\nthere is no `notion_api_*` to reach for — an operation absent from the tool\nlist is absent from this connection, not hidden behind a generic call.\n\n## Writes and pagination\n\n- Notion has **no idempotency key**. A retried `integration_create_page` or\n `integration_add_comment` creates a duplicate. Confirm with `integration_search` before repeating\n a write that may have partially succeeded.\n- List tools take `page_size` (max 100) and return `has_more` with\n `next_cursor`. Pass a cursor back verbatim — it is opaque and must never be\n parsed or constructed. Follow pages inside `execute_code` and reduce there.\n",
7
+ "guide_1": "# Notion MCP usage\n\nOfficial MCP interface: tool names, descriptions, argument schemas, and result\nschemas come from Notion's live server. Connecta preserves that catalog and\napplies release-reviewed classification. Reviewed writes stay writes even when Notion claims they only read.\n\nWorkspace purpose: ",
8
+ "guide_2": "\n\n- This connection acts as its OAuth-authorized user. The separately configured\n `surface: \"api\"` connection retains required internal-integration operations\n under `integration_*` names, including headless content writes, cursor-complete\n queries, exact JSON blocks and property items. No identity fallback occurs.\n- Discover the live catalog before assuming a tool exists. Notion can gate\n tools by workspace, account, client, and rollout independently of Connecta.\n- Start broad discovery with `notion-search`, then use `notion-fetch` on\n the exact page, database, data source, or object before changing it.\n- Use the live input schema as the contract. Notion owns these MCP schemas;\n the REST schemas in Connecta's API interface do not apply to MCP tools with\n similar names.\n- Session and agent tools can launch asynchronous work. Read session state and\n events before sending another message, waiting, or stopping a session.\n- File uploads and attachment tools create durable workspace state. Keep\n downloads inside the requested task and do not expose signed attachment URLs.\n- An `auth_required` failure means this connector's OAuth grant is missing or\n expired. Run `authorize_connector` for this connector id, then retry.\n"
9
+ }
10
+ };
@@ -1,4 +1,4 @@
1
- import type { Connector, ConnectorCallAdmissionPolicy } from "../types.js";
1
+ import type { ConnectorCallAdmissionPolicy } from "../../types.js";
2
2
  /**
3
3
  * Overflow's two published environments. They are separate deployments with
4
4
  * separate credentials: a staging key works only against staging and a
@@ -57,4 +57,4 @@ export interface OverflowOptions {
57
57
  * cannot infer — Overflow's keys carry no recognizable environment marker — so
58
58
  * the operator states it, and the title and the guide's first line repeat it.
59
59
  */
60
- export declare function overflow(id: string, options: OverflowOptions): Connector;
60
+ export declare const overflow: import("../../provider.js").ProviderFactory<OverflowOptions>;
@@ -1,3 +1,4 @@
1
+ import { skill } from "./skill.generated.js";
1
2
  /**
2
3
  * Overflow (overflow.co) — church and nonprofit giving: cash, stock, crypto,
3
4
  * and donor-advised-fund gifts, the deposits that settle them, the donors who
@@ -7,7 +8,7 @@
7
8
  * header-authenticated JSON over thirty-eight operations, so `fetch` through
8
9
  * the shared guarded transport keeps this subpath Workers-clean and adds
9
10
  * nothing to install. Every tool below is hand-written. Overflow's OpenAPI
10
- * document is drift evidence only — `scripts/drift/overflow-endpoints.json`
11
+ * document is drift evidence only — `src/providers/overflow/drift.json`
11
12
  * records the operations the named tools touch, and
12
13
  * `npm run providers:check -- --provider overflow` compares them with the
13
14
  * published document without a credential. That document is served only by
@@ -27,9 +28,12 @@
27
28
  * exact method, path, and body a human is being asked to allow. Overflow has
28
29
  * no upload endpoint, so there is no upload hatch.
29
30
  */
30
- import { apiConnector as api, defined } from "../connectors/api-connector.js";
31
- import { guardedFetch, retryAfterMs, } from "../connectors/guarded-fetch.js";
32
- import { ConnectorCallError } from "../errors.js";
31
+ import { apiConnector as api, defined } from "../../connectors/api-connector.js";
32
+ import { guardedFetch, retryAfterMs, } from "../../connectors/guarded-fetch.js";
33
+ import { ConnectorCallError } from "../../errors.js";
34
+ import { keys, optionsOf } from "../../config-schema.js";
35
+ import { PROVIDER_COMMON } from "../../connectors/option-shapes.js";
36
+ import { asProviderFactory } from "../../provider.js";
33
37
  /**
34
38
  * Overflow's two published environments. They are separate deployments with
35
39
  * separate credentials: a staging key works only against staging and a
@@ -81,9 +85,7 @@ const OVERFLOW_ADMISSION = {
81
85
  ],
82
86
  };
83
87
  function asRecord(value) {
84
- return value && typeof value === "object" && !Array.isArray(value)
85
- ? value
86
- : {};
88
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
87
89
  }
88
90
  function asArray(value) {
89
91
  return Array.isArray(value) ? value : [];
@@ -94,9 +96,7 @@ function compact(value) {
94
96
  /** A `{ id, name }` reference, or undefined when Overflow sent none. */
95
97
  function reference(value) {
96
98
  const record = asRecord(value);
97
- return Object.keys(record).length === 0
98
- ? undefined
99
- : compact({ id: record["id"], name: record["name"] });
99
+ return Object.keys(record).length === 0 ? undefined : compact({ id: record["id"], name: record["name"] });
100
100
  }
101
101
  // --- Credentials and transport ----------------------------------------------
102
102
  const API_KEY_CREDENTIAL = {
@@ -176,9 +176,7 @@ function windowResetMs(headers) {
176
176
  if (!raw)
177
177
  return undefined;
178
178
  const seconds = Number(raw.trim());
179
- return Number.isFinite(seconds) && seconds >= 0
180
- ? Math.trunc(seconds * 1_000)
181
- : undefined;
179
+ return Number.isFinite(seconds) && seconds >= 0 ? Math.trunc(seconds * 1_000) : undefined;
182
180
  }
183
181
  /**
184
182
  * Map an Overflow failure by what the caller does next (H11).
@@ -203,7 +201,7 @@ function overflowFailure(status, headers, payload, method) {
203
201
  const ambiguity = method !== "GET" && status === 403
204
202
  ? " Overflow also answers 403 when a donor profile owned by a donor account cannot be edited through the API; if reads succeed with this credential, that refusal is the cause and no credential change will fix it."
205
203
  : "";
206
- return new ConnectorCallError("auth_required", `Overflow rejected the request (HTTP ${status}): ${detail} The client id and API key may be wrong, revoked, or issued for the other environment (staging and production credentials are not interchangeable); an operator must replace them.${ambiguity}`);
204
+ return new ConnectorCallError(status === 403 ? "provider_permission_denied" : "auth_required", `Overflow rejected the request (HTTP ${status}): ${detail} The client id and API key may be wrong, revoked, or issued for the other environment (staging and production credentials are not interchangeable); an operator must check them and ask the account administrator to grant resource access if they are valid.${ambiguity}`);
207
205
  }
208
206
  if (status === 404) {
209
207
  return new ConnectorCallError("not_found", `Overflow found no such resource for this nonprofit (HTTP 404): ${detail} Re-resolve the id with its list tool; for a raw call, confirm the path below /api/v3.`);
@@ -221,7 +219,9 @@ function overflowFailure(status, headers, payload, method) {
221
219
  const wait = windowResetMs(headers);
222
220
  return new ConnectorCallError("unavailable", `Overflow is failing upstream (HTTP ${status}): ${detail}`, wait === undefined ? {} : { retryAfterMs: wait });
223
221
  }
224
- return new ConnectorCallError("connector_call_failed", `Overflow request failed (HTTP ${status}): ${detail}`, { retryable: false });
222
+ return new ConnectorCallError("connector_call_failed", `Overflow request failed (HTTP ${status}): ${detail}`, {
223
+ retryable: false,
224
+ });
225
225
  }
226
226
  function parseBody(text) {
227
227
  if (!text.trim())
@@ -311,9 +311,7 @@ function projectContribution(value) {
311
311
  stocks: Object.keys(stocks).length === 0
312
312
  ? undefined
313
313
  : compact({ quantity: stocks["quantity"], tickers: stocks["tickers"] }),
314
- crypto: Object.keys(crypto).length === 0
315
- ? undefined
316
- : compact({ quantity: crypto["quantity"], token: crypto["token"] }),
314
+ crypto: Object.keys(crypto).length === 0 ? undefined : compact({ quantity: crypto["quantity"], token: crypto["token"] }),
317
315
  dedication: contribution["dedication"] ?? undefined,
318
316
  donorNotes: contribution["donorNotes"] ?? undefined,
319
317
  metadata: contribution["metadata"] ?? undefined,
@@ -329,7 +327,7 @@ function projectDonor(value, full) {
329
327
  lastName: donor["lastName"],
330
328
  email: donor["email"],
331
329
  phone: full ? donor["phone"] : undefined,
332
- address: full ? donor["address"] ?? undefined : undefined,
330
+ address: full ? (donor["address"] ?? undefined) : undefined,
333
331
  locationIds: donor["locationIds"],
334
332
  totalContributionsCount: donor["totalContributionsCount"],
335
333
  activeRecurringCount: donor["activeRecurringCount"],
@@ -444,7 +442,9 @@ function projectPaymentMethod(value) {
444
442
  function projectCampaign(value) {
445
443
  const campaign = asRecord(value);
446
444
  const refs = (list) => {
447
- const rows = asArray(list).map(reference).filter((row) => row !== undefined);
445
+ const rows = asArray(list)
446
+ .map(reference)
447
+ .filter((row) => row !== undefined);
448
448
  return rows.length === 0 ? undefined : rows;
449
449
  };
450
450
  return compact({
@@ -840,10 +840,7 @@ function rawQuery(value) {
840
840
  (query[name] ??= []).push(item);
841
841
  }
842
842
  }
843
- return Object.fromEntries(Object.entries(query).map(([name, values]) => [
844
- name,
845
- values.length === 1 ? values[0] : values,
846
- ]));
843
+ return Object.fromEntries(Object.entries(query).map(([name, values]) => [name, values.length === 1 ? values[0] : values]));
847
844
  }
848
845
  function tools(send, defaultPageSize) {
849
846
  const readOnly = { readOnlyHint: true };
@@ -864,7 +861,9 @@ function tools(send, defaultPageSize) {
864
861
  const payload = await callOverflow(send, { method: "GET", path }, ctx);
865
862
  const record = asRecord(payload)["data"];
866
863
  if (record === undefined || record === null) {
867
- throw new ConnectorCallError("connector_call_failed", "Overflow answered without the requested record.", { retryable: false });
864
+ throw new ConnectorCallError("connector_call_failed", "Overflow answered without the requested record.", {
865
+ retryable: false,
866
+ });
868
867
  }
869
868
  return asRecord(record);
870
869
  }
@@ -930,7 +929,11 @@ function tools(send, defaultPageSize) {
930
929
  type: "array",
931
930
  minItems: 1,
932
931
  uniqueItems: true,
933
- items: { type: "string", enum: ["PENDING", "CONFIRMED", "FAILED", "CANCELED"], description: "Status bucket." },
932
+ items: {
933
+ type: "string",
934
+ enum: ["PENDING", "CONFIRMED", "FAILED", "CANCELED"],
935
+ description: "Status bucket.",
936
+ },
934
937
  description: "Overflow's coarse status buckets; matches any.",
935
938
  },
936
939
  campaignId: idProperty("list_campaigns"),
@@ -966,7 +969,9 @@ function tools(send, defaultPageSize) {
966
969
  name: "get_contribution",
967
970
  description: "Get one contribution by id with its status, amount, campaign, deposit, and donor name. Refunds and chargebacks are separate lists.",
968
971
  annotations: readOnly,
969
- inputSchema: namedInput({ contributionId: idProperty("list_contributions"), raw: RAW_PROPERTY }, ["contributionId"]),
972
+ inputSchema: namedInput({ contributionId: idProperty("list_contributions"), raw: RAW_PROPERTY }, [
973
+ "contributionId",
974
+ ]),
970
975
  outputSchema: CONTRIBUTION_SCHEMA,
971
976
  handler: async (args, ctx) => {
972
977
  const record = await one(`/contributions/${segment(args["contributionId"])}`, ctx);
@@ -1189,9 +1194,7 @@ function tools(send, defaultPageSize) {
1189
1194
  outputSchema: listOutput("subscriptions", SUBSCRIPTION_SCHEMA),
1190
1195
  handler: (args, ctx) => list({
1191
1196
  args,
1192
- path: args["donorId"] === undefined
1193
- ? "/subscriptions"
1194
- : `/subscriptions/${segment(args["donorId"])}`,
1197
+ path: args["donorId"] === undefined ? "/subscriptions" : `/subscriptions/${segment(args["donorId"])}`,
1195
1198
  key: "subscriptions",
1196
1199
  project: projectSubscription,
1197
1200
  query: {
@@ -1240,7 +1243,15 @@ function tools(send, defaultPageSize) {
1240
1243
  excludeInactive: { type: "boolean", description: "Exclude campaigns that have ended." },
1241
1244
  sortBy: {
1242
1245
  type: "string",
1243
- enum: ["startDate", "endDate", "name", "displayOrder", "totalContributionCount", "totalContributionValue", "uniqueDonorCount"],
1246
+ enum: [
1247
+ "startDate",
1248
+ "endDate",
1249
+ "name",
1250
+ "displayOrder",
1251
+ "totalContributionCount",
1252
+ "totalContributionValue",
1253
+ "uniqueDonorCount",
1254
+ ],
1244
1255
  description: "Sort field. Defaults to startDate.",
1245
1256
  },
1246
1257
  sortDirection: { ...SORT_DIRECTION, description: "Defaults to DESC." },
@@ -1256,7 +1267,9 @@ function tools(send, defaultPageSize) {
1256
1267
  project: projectCampaign,
1257
1268
  query: {
1258
1269
  isSubcampaign: args["isSubcampaign"] === undefined
1259
- ? args["parentCampaignId"] === undefined ? undefined : "true"
1270
+ ? args["parentCampaignId"] === undefined
1271
+ ? undefined
1272
+ : "true"
1260
1273
  : String(args["isSubcampaign"]),
1261
1274
  parentCampaignId: args["parentCampaignId"],
1262
1275
  search: args["search"],
@@ -1273,7 +1286,11 @@ function tools(send, defaultPageSize) {
1273
1286
  annotations: readOnly,
1274
1287
  inputSchema: namedInput({
1275
1288
  includeArchived: { type: "boolean", description: "Include archived locations." },
1276
- sortBy: { type: "string", enum: ["displayOrder", "name", "createdAt"], description: "Sort field. Defaults to displayOrder." },
1289
+ sortBy: {
1290
+ type: "string",
1291
+ enum: ["displayOrder", "name", "createdAt"],
1292
+ description: "Sort field. Defaults to displayOrder.",
1293
+ },
1277
1294
  sortDirection: { ...SORT_DIRECTION, description: "Defaults to ASC." },
1278
1295
  page: PAGE_PROPERTY,
1279
1296
  limit: LIMIT,
@@ -1395,9 +1412,7 @@ function tools(send, defaultPageSize) {
1395
1412
  const next = info["nextCursor"];
1396
1413
  return compact({
1397
1414
  attempts: args["raw"] === true ? rows : rows.map(projectEventLog),
1398
- nextCursor: info["hasNextPage"] === true && typeof next === "string" && next !== ""
1399
- ? next
1400
- : undefined,
1415
+ nextCursor: info["hasNextPage"] === true && typeof next === "string" && next !== "" ? next : undefined,
1401
1416
  });
1402
1417
  },
1403
1418
  },
@@ -1413,78 +1428,14 @@ function usageGuide(purpose, environment, instructions) {
1413
1428
 
1414
1429
  ${environmentLine}
1415
1430
 
1416
- Nonprofit purpose: ${purpose}
1417
-
1418
- ## Money, units, and status
1419
-
1420
- Overflow does not use one money unit, so read the unit per field:
1421
-
1422
- - Fields named \`…InCents\` are cents: deposits and their line items and
1423
- totals, refunds, chargebacks, and the authorize body.
1424
- - Recurring-gift \`amount\` is dollars: Overflow's create and update bodies
1425
- say so, and its subscription reads carry the same field.
1426
- - Contribution \`amount\` states no unit in Overflow's schema. Check one known
1427
- gift before reporting totals.
1428
- - \`status\` is per asset type. A gift is final at \`PAID_OUT\` (cash),
1429
- \`CONFIRMED\` (manual cash, crypto), \`CONTRIBUTION_RECEIVED\` or \`BILLED\`
1430
- (stock), and \`CONTRIBUTION_RECEIVED\` (DAF). \`statusBucket\` filters by
1431
- Overflow's coarser PENDING / CONFIRMED / FAILED / CANCELED.
1432
- - There is no funds endpoint. A gift reports its campaign and subcampaign;
1433
- \`fundId\` and \`subFundId\` appear only in the payment-authorize body.
1434
- - Poll for changes with \`minimumUpdatedDate\`; \`minimumInitiatedDate\` is
1435
- when the donor gave.
1436
-
1437
- ## Reconcile a deposit
1438
-
1439
- \`list_deposits\` → \`get_deposit_summary\` for totals → \`get_deposit\` for the
1440
- line items, whose \`referenceId\` names the contribution, refund, or
1441
- chargeback. Contributions also carry their \`depositId\`.
1442
-
1443
- ## Donor data is sensitive
1444
-
1445
- Giving history and donor contact details are personal data. Reduce inside
1446
- \`execute_code\` — aggregate, count, or keep ids — and return names, emails,
1447
- phones, or addresses only when the task needs them. Lists and contributions
1448
- already omit contact details; \`get_donor\` is the deliberate lookup.
1449
-
1450
- ## Writes go through \`overflow_api_mutate\`
1451
-
1452
- Every Overflow write moves money or changes a donor record, so none has a
1453
- named tool. Each is one approval-gated \`overflow_api_mutate\` call with
1454
- Overflow's documented body:
1455
-
1456
- - \`POST /contributions\` charges a saved payment method. Its \`amount\`
1457
- states no unit; confirm it against a known gift of the same donor before
1458
- charging. \`POST /payments/authorize\` takes \`amountInCents\` and
1459
- authorizes a new method (\`0\` only saves it).
1460
- - \`POST /contributions/{id}/initiate-refund\` (under $1,000 only).
1461
- - \`POST\` / \`PATCH\` / \`DELETE /subscriptions/{donorId}[/{subscriptionId}]\`
1462
- creates, changes, or cancels a recurring gift. Its \`amount\` is dollars,
1463
- not cents: a $50.00 monthly gift is \`amount: 50\`. Cancel takes a
1464
- \`cancellationReason\` body.
1465
- - \`POST /donors\`, \`PATCH /donors/{id}\`.
1466
-
1467
- Resolve every id first — donors, payment methods (\`list_payment_methods\`),
1468
- campaigns, locations — and never guess one. A write that fails with HTTP 5xx
1469
- may still have happened: read before retrying.
1470
-
1471
- ## Hatches, pages, and limits
1472
-
1473
- - \`overflow_api_get\` reaches the reads without a named tool: single refunds,
1474
- chargebacks, campaigns, locations, webhooks, and recurring gifts
1475
- (\`/subscriptions/{donorId}/{id}\`), and Tap devices, groups, and
1476
- destinations (\`/tap/devices\`, \`/tap/groups\`, \`/tap/destinations\`). Paths
1477
- are below \`/api/v3\`; array filters repeat a \`name[]\` query pair.
1478
- - Lists page by number: pass \`page\` while \`page.hasMore\` is true.
1479
- \`list_webhook_event_logs\` pages by cursor instead.
1480
- - Overflow allows 120 requests per minute per API client. This connection
1481
- admits the same per runtime, four at a time, which approximates rather than
1482
- enforces it; a \`rate_limited\` failure carries the wait.
1483
- ${accountInstructions
1484
- ? `\n## Nonprofit instructions\n\n${accountInstructions}\n`
1485
- : ""}`;
1431
+ Nonprofit purpose: ${purpose}${skill.fragments.guide_0}${accountInstructions ? `\n## ${skill.instructionsHeading}\n\n${accountInstructions}\n` : ""}`;
1486
1432
  }
1487
1433
  // --- Construction --------------------------------------------------------------
1434
+ /** The closed options overflow() accepts; see `assertKnownOptions`. */
1435
+ const OVERFLOW_OPTIONS = optionsOf()({
1436
+ ...PROVIDER_COMMON,
1437
+ ...keys("environment", "defaultPageSize", "baseUrl"),
1438
+ });
1488
1439
  /**
1489
1440
  * A maintained Overflow connection.
1490
1441
  *
@@ -1501,7 +1452,21 @@ ${accountInstructions
1501
1452
  * cannot infer — Overflow's keys carry no recognizable environment marker — so
1502
1453
  * the operator states it, and the title and the guide's first line repeat it.
1503
1454
  */
1504
- export function overflow(id, options) {
1455
+ export const overflow = asProviderFactory({
1456
+ name: "overflow",
1457
+ title: "Overflow",
1458
+ kind: "api",
1459
+ readme: "Overflow",
1460
+ bundle: {
1461
+ "baselineGzip": 20410,
1462
+ "maxGzip": 80410,
1463
+ "note": "./providers/overflow starts at 20,410 B gzip: a hand-written api() surface over the guarded transport with no hosted-MCP interface, so it pulls in neither remoteMcp nor the Effect core the other providers carry. The cap uses the existing baseline + 60,000 B policy.",
1464
+ },
1465
+ skill,
1466
+ options: OVERFLOW_OPTIONS,
1467
+ create: overflowConnector,
1468
+ });
1469
+ function overflowConnector(id, options) {
1505
1470
  const purpose = options.purpose?.trim();
1506
1471
  if (!purpose) {
1507
1472
  throw new Error("overflow() requires a non-empty nonprofit purpose.");
@@ -1511,9 +1476,7 @@ export function overflow(id, options) {
1511
1476
  throw new Error('overflow() requires environment: "production" or "staging". Overflow issues separate credentials for each, and connecta will not guess which one a deployment means.');
1512
1477
  }
1513
1478
  const defaultPageSize = options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
1514
- if (!Number.isInteger(defaultPageSize) ||
1515
- defaultPageSize < 1 ||
1516
- defaultPageSize > MAX_PAGE_SIZE) {
1479
+ if (!Number.isInteger(defaultPageSize) || defaultPageSize < 1 || defaultPageSize > MAX_PAGE_SIZE) {
1517
1480
  throw new Error(`overflow() defaultPageSize must be a whole number between 1 and ${MAX_PAGE_SIZE}.`);
1518
1481
  }
1519
1482
  const send = overflowTransport(options.baseUrl?.trim() || OVERFLOW_API_BASE_URLS[environment]);
@@ -1533,7 +1496,7 @@ export function overflow(id, options) {
1533
1496
  const payload = asRecord(await callOverflow(send, { method: "GET", path: "/locations", query: { limit: 1, sortBy: "displayOrder" } }, {
1534
1497
  ...ctx,
1535
1498
  credential: {
1536
- get: async (field) => (field ? values[field] ?? null : null),
1499
+ get: async (field) => (field ? (values[field] ?? null) : null),
1537
1500
  getAll: async () => values,
1538
1501
  },
1539
1502
  }));
@@ -0,0 +1,8 @@
1
+ // Generated by scripts/generate-providers.mjs; do not edit.
2
+ export const skill = {
3
+ "content": "\n\n## Money, units, and status\n\nOverflow does not use one money unit, so read the unit per field:\n\n- Fields named `…InCents` are cents: deposits and their line items and\n totals, refunds, chargebacks, and the authorize body.\n- Recurring-gift `amount` is dollars: Overflow's create and update bodies\n say so, and its subscription reads carry the same field.\n- Contribution `amount` states no unit in Overflow's schema. Check one known\n gift before reporting totals.\n- `status` is per asset type. A gift is final at `PAID_OUT` (cash),\n `CONFIRMED` (manual cash, crypto), `CONTRIBUTION_RECEIVED` or `BILLED`\n (stock), and `CONTRIBUTION_RECEIVED` (DAF). `statusBucket` filters by\n Overflow's coarser PENDING / CONFIRMED / FAILED / CANCELED.\n- There is no funds endpoint. A gift reports its campaign and subcampaign;\n `fundId` and `subFundId` appear only in the payment-authorize body.\n- Poll for changes with `minimumUpdatedDate`; `minimumInitiatedDate` is\n when the donor gave.\n\n## Reconcile a deposit\n\n`list_deposits` → `get_deposit_summary` for totals → `get_deposit` for the\nline items, whose `referenceId` names the contribution, refund, or\nchargeback. Contributions also carry their `depositId`.\n\n## Donor data is sensitive\n\nGiving history and donor contact details are personal data. Reduce inside\n`execute_code` — aggregate, count, or keep ids — and return names, emails,\nphones, or addresses only when the task needs them. Lists and contributions\nalready omit contact details; `get_donor` is the deliberate lookup.\n\n## Writes go through `overflow_api_mutate`\n\nEvery Overflow write moves money or changes a donor record, so none has a\nnamed tool. Each is one approval-gated `overflow_api_mutate` call with\nOverflow's documented body:\n\n- `POST /contributions` charges a saved payment method. Its `amount`\n states no unit; confirm it against a known gift of the same donor before\n charging. `POST /payments/authorize` takes `amountInCents` and\n authorizes a new method (`0` only saves it).\n- `POST /contributions/{id}/initiate-refund` (under $1,000 only).\n- `POST` / `PATCH` / `DELETE /subscriptions/{donorId}[/{subscriptionId}]`\n creates, changes, or cancels a recurring gift. Its `amount` is dollars,\n not cents: a $50.00 monthly gift is `amount: 50`. Cancel takes a\n `cancellationReason` body.\n- `POST /donors`, `PATCH /donors/{id}`.\n\nResolve every id first — donors, payment methods (`list_payment_methods`),\ncampaigns, locations — and never guess one. A write that fails with HTTP 5xx\nmay still have happened: read before retrying.\n\n## Hatches, pages, and limits\n\n- `overflow_api_get` reaches the reads without a named tool: single refunds,\n chargebacks, campaigns, locations, webhooks, and recurring gifts\n (`/subscriptions/{donorId}/{id}`), and Tap devices, groups, and\n destinations (`/tap/devices`, `/tap/groups`, `/tap/destinations`). Paths\n are below `/api/v3`; array filters repeat a `name[]` query pair.\n- Lists page by number: pass `page` while `page.hasMore` is true.\n `list_webhook_event_logs` pages by cursor instead.\n- Overflow allows 120 requests per minute per API client. This connection\n admits the same per runtime, four at a time, which approximates rather than\n enforces it; a `rate_limited` failure carries the wait.\n",
4
+ "instructionsHeading": "Nonprofit instructions",
5
+ "fragments": {
6
+ "guide_0": "\n\n## Money, units, and status\n\nOverflow does not use one money unit, so read the unit per field:\n\n- Fields named `…InCents` are cents: deposits and their line items and\n totals, refunds, chargebacks, and the authorize body.\n- Recurring-gift `amount` is dollars: Overflow's create and update bodies\n say so, and its subscription reads carry the same field.\n- Contribution `amount` states no unit in Overflow's schema. Check one known\n gift before reporting totals.\n- `status` is per asset type. A gift is final at `PAID_OUT` (cash),\n `CONFIRMED` (manual cash, crypto), `CONTRIBUTION_RECEIVED` or `BILLED`\n (stock), and `CONTRIBUTION_RECEIVED` (DAF). `statusBucket` filters by\n Overflow's coarser PENDING / CONFIRMED / FAILED / CANCELED.\n- There is no funds endpoint. A gift reports its campaign and subcampaign;\n `fundId` and `subFundId` appear only in the payment-authorize body.\n- Poll for changes with `minimumUpdatedDate`; `minimumInitiatedDate` is\n when the donor gave.\n\n## Reconcile a deposit\n\n`list_deposits` → `get_deposit_summary` for totals → `get_deposit` for the\nline items, whose `referenceId` names the contribution, refund, or\nchargeback. Contributions also carry their `depositId`.\n\n## Donor data is sensitive\n\nGiving history and donor contact details are personal data. Reduce inside\n`execute_code` — aggregate, count, or keep ids — and return names, emails,\nphones, or addresses only when the task needs them. Lists and contributions\nalready omit contact details; `get_donor` is the deliberate lookup.\n\n## Writes go through `overflow_api_mutate`\n\nEvery Overflow write moves money or changes a donor record, so none has a\nnamed tool. Each is one approval-gated `overflow_api_mutate` call with\nOverflow's documented body:\n\n- `POST /contributions` charges a saved payment method. Its `amount`\n states no unit; confirm it against a known gift of the same donor before\n charging. `POST /payments/authorize` takes `amountInCents` and\n authorizes a new method (`0` only saves it).\n- `POST /contributions/{id}/initiate-refund` (under $1,000 only).\n- `POST` / `PATCH` / `DELETE /subscriptions/{donorId}[/{subscriptionId}]`\n creates, changes, or cancels a recurring gift. Its `amount` is dollars,\n not cents: a $50.00 monthly gift is `amount: 50`. Cancel takes a\n `cancellationReason` body.\n- `POST /donors`, `PATCH /donors/{id}`.\n\nResolve every id first — donors, payment methods (`list_payment_methods`),\ncampaigns, locations — and never guess one. A write that fails with HTTP 5xx\nmay still have happened: read before retrying.\n\n## Hatches, pages, and limits\n\n- `overflow_api_get` reaches the reads without a named tool: single refunds,\n chargebacks, campaigns, locations, webhooks, and recurring gifts\n (`/subscriptions/{donorId}/{id}`), and Tap devices, groups, and\n destinations (`/tap/devices`, `/tap/groups`, `/tap/destinations`). Paths\n are below `/api/v3`; array filters repeat a `name[]` query pair.\n- Lists page by number: pass `page` while `page.hasMore` is true.\n `list_webhook_event_logs` pages by cursor instead.\n- Overflow allows 120 requests per minute per API client. This connection\n admits the same per runtime, four at a time, which approximates rather than\n enforces it; a `rate_limited` failure carries the wait.\n"
7
+ }
8
+ };
@@ -1,4 +1,4 @@
1
- import type { Connector, ConnectorCallAdmissionPolicy } from "../types.js";
1
+ import type { ConnectorCallAdmissionPolicy } from "../../types.js";
2
2
  /** Planning Center's REST origin. Override only for a proxy or test double. */
3
3
  export declare const PLANNING_CENTER_API_BASE_URL = "https://api.planningcenteronline.com";
4
4
  /** The products behind `/<app>/v2`, in the order their guides list them. */
@@ -56,4 +56,4 @@ export interface PlanningCenterOptions {
56
56
  baseUrl?: string;
57
57
  }
58
58
  /** A maintained Planning Center Online connection over the REST API. */
59
- export declare function planningCenter(id: string, options: PlanningCenterOptions): Connector;
59
+ export declare const planningCenter: import("../../provider.js").ProviderFactory<PlanningCenterOptions>;