@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,9 @@
1
+ // Generated by scripts/generate-providers.mjs; do not edit.
2
+ export const skill = {
3
+ "content": ". Basecamp's hosted server acts in the single account the grant was made for, and no tool takes an account id. Another account is another connector, never an argument.\n\n- Confirm the account before relying on it: `get_me` returns the signed-in person and a label naming the connected account. A record id from another account's URL answers \"not found\" here — read that as wrong connector, not missing data.\n- Basecamp's own reference notes are a read away: `get_basecamp_guide` with `topic` `concepts`, `finding-things`, `my-work`, `posting-and-mentions`, or `deleting-and-trash`. Read the topic before an unfamiliar sequence instead of guessing one.\n- Resolve ids before acting; never guess one. A Basecamp URL carries the account, project, and record ids, and `get_by_url` reads any link into those ids, the record, and where a reply goes. `search` finds records by text; `list_projects` then `get_project` returns a project's dock, whose tool ids (to-do set, message board, schedule, vault, card table, chat) the create tools take. People are ids from `list_people`, `list_project_people`, or `list_pingable_people`; mention someone by passing ids as `create_comment`'s `mentions`, not by writing markup.\n- Rich-text bodies (messages, documents, comments, cards, to-do descriptions) are HTML; a to-do's title and a chat line are plain text. Dates are `YYYY-MM-DD`.\n- Lists return up to `limit` items (100 by default). Continue with the `next_cursor` or `next_page` a result carries rather than raising the limit; `truncated: true` means the server cut a result to fit and says where to resume. The event feeds page by `position` instead. `get_card_table`, `get_document`, and the account-wide `list_everything_*` reads are large — reduce inside `execute_code` and return only the fields the question needs.\n- `trash_*` is restorable for 25 days; `delete_*`, `destroy_*`, and `remove_account_logo` are permanent. `update_cloud_file` and `update_google_document` are full replaces: an omitted title or description is erased, so read the record and resend every field you keep. `update_project_access` can invite people into the account.\n- Do not call `create_stream_ticket`: it mints a WebSocket credential for a client that holds a live connection, which a connecta call cannot. Poll `list_feed_events` instead. `get_event_inbox` serves agent accounts only; a person's grant gets `agents_only` back.\n- This connector's tool list is not a fixed set. Basecamp has not published this server and changes it without notice, and timesheets, gauges, hill charts, the Lineup, and check-ins depend on account features, so search this connector for what it actually exposes rather than assuming a tool exists.\n- Basecamp answers a rate limit with `429` and a `Retry-After` in seconds, and adjusts its limits dynamically. Back off for that long rather than retrying at once, and avoid speculative fan-out.\n- Treat every create, update, trash, delete, destroy, move, reposition, complete, pin, archive, enable, disable, watch, and mark operation as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n- An `auth_required` failure means this connector's Basecamp authorization is missing or expired: run `authorize_connector` for this connector id, then retry the same call unchanged. A rejected argument, a permission gap, or a missing feature comes back in Basecamp's own words instead — read it rather than re-authorizing.\n",
4
+ "instructionsHeading": "Account instructions",
5
+ "fragments": {
6
+ "guide_0": ". Basecamp's hosted server acts in the single account the grant was made for, and no tool takes an account id. ",
7
+ "guide_1": " Another account is another connector, never an argument.\n\n- Confirm the account before relying on it: `get_me` returns the signed-in person and a label naming the connected account. A record id from another account's URL answers \"not found\" here — read that as wrong connector, not missing data.\n- Basecamp's own reference notes are a read away: `get_basecamp_guide` with `topic` `concepts`, `finding-things`, `my-work`, `posting-and-mentions`, or `deleting-and-trash`. Read the topic before an unfamiliar sequence instead of guessing one.\n- Resolve ids before acting; never guess one. A Basecamp URL carries the account, project, and record ids, and `get_by_url` reads any link into those ids, the record, and where a reply goes. `search` finds records by text; `list_projects` then `get_project` returns a project's dock, whose tool ids (to-do set, message board, schedule, vault, card table, chat) the create tools take. People are ids from `list_people`, `list_project_people`, or `list_pingable_people`; mention someone by passing ids as `create_comment`'s `mentions`, not by writing markup.\n- Rich-text bodies (messages, documents, comments, cards, to-do descriptions) are HTML; a to-do's title and a chat line are plain text. Dates are `YYYY-MM-DD`.\n- Lists return up to `limit` items (100 by default). Continue with the `next_cursor` or `next_page` a result carries rather than raising the limit; `truncated: true` means the server cut a result to fit and says where to resume. The event feeds page by `position` instead. `get_card_table`, `get_document`, and the account-wide `list_everything_*` reads are large — reduce inside `execute_code` and return only the fields the question needs.\n- `trash_*` is restorable for 25 days; `delete_*`, `destroy_*`, and `remove_account_logo` are permanent. `update_cloud_file` and `update_google_document` are full replaces: an omitted title or description is erased, so read the record and resend every field you keep. `update_project_access` can invite people into the account.\n- Do not call `create_stream_ticket`: it mints a WebSocket credential for a client that holds a live connection, which a connecta call cannot. Poll `list_feed_events` instead. `get_event_inbox` serves agent accounts only; a person's grant gets `agents_only` back.\n- This connector's tool list is not a fixed set. Basecamp has not published this server and changes it without notice, and timesheets, gauges, hill charts, the Lineup, and check-ins depend on account features, so search this connector for what it actually exposes rather than assuming a tool exists.\n- Basecamp answers a rate limit with `429` and a `Retry-After` in seconds, and adjusts its limits dynamically. Back off for that long rather than retrying at once, and avoid speculative fan-out.\n- Treat every create, update, trash, delete, destroy, move, reposition, complete, pin, archive, enable, disable, watch, and mark operation as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n- An `auth_required` failure means this connector's Basecamp authorization is missing or expired: run `authorize_connector` for this connector id, then retry the same call unchanged. A rejected argument, a permission gap, or a missing feature comes back in Basecamp's own words instead — read it rather than re-authorizing.\n"
8
+ }
9
+ };
@@ -1,4 +1,4 @@
1
- import type { Connector, ConnectorCallAdmissionPolicy } from "../types.js";
1
+ import type { ConnectorCallAdmissionPolicy } from "../../types.js";
2
2
  /** Every church's API lives at `https://<subdomain>.breezechms.com/api`. */
3
3
  export declare const BREEZE_HOST_SUFFIX = ".breezechms.com";
4
4
  export interface BreezeOptions {
@@ -31,4 +31,4 @@ export interface BreezeOptions {
31
31
  defaultPageSize?: number;
32
32
  }
33
33
  /** A maintained Breeze ChMS connection for one church's subdomain. */
34
- export declare function breeze(id: string, options: BreezeOptions): Connector;
34
+ export declare const breeze: import("../../provider.js").ProviderFactory<BreezeOptions>;
@@ -1,3 +1,4 @@
1
+ import { skill } from "./skill.generated.js";
1
2
  /**
2
3
  * Breeze ChMS — rebranded "Tithely Church Management" on 2025-11-11 — as a
3
4
  * hand-written `api()` surface. No SDK exists worth wrapping: Breeze's API is
@@ -52,9 +53,12 @@
52
53
  * distinct paths: the Tags section's "List People" points back at /people)
53
54
  * plus the 2023 giving, funds, and pledges sections.
54
55
  */
55
- import { apiConnector as api, defined } from "../connectors/api-connector.js";
56
- import { guardedFetch, retryAfterMs, } from "../connectors/guarded-fetch.js";
57
- import { ConnectorCallError } from "../errors.js";
56
+ import { apiConnector as api, defined } from "../../connectors/api-connector.js";
57
+ import { guardedFetch, retryAfterMs, } from "../../connectors/guarded-fetch.js";
58
+ import { ConnectorCallError } from "../../errors.js";
59
+ import { keys, optionsOf } from "../../config-schema.js";
60
+ import { PROVIDER_COMMON } from "../../connectors/option-shapes.js";
61
+ import { asProviderFactory } from "../../provider.js";
58
62
  /** Every church's API lives at `https://<subdomain>.breezechms.com/api`. */
59
63
  export const BREEZE_HOST_SUFFIX = ".breezechms.com";
60
64
  const DEFAULT_PAGE_SIZE = 100;
@@ -71,9 +75,7 @@ const BREEZE_MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
71
75
  /** One DNS label: what `<subdomain>.breezechms.com` can actually be. */
72
76
  const SUBDOMAIN_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
73
77
  function asRecord(value) {
74
- return value && typeof value === "object" && !Array.isArray(value)
75
- ? value
76
- : {};
78
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
77
79
  }
78
80
  function asArray(value) {
79
81
  return Array.isArray(value) ? value : [];
@@ -96,9 +98,7 @@ function flag(value) {
96
98
  return value === true || value === 1 || value === "1";
97
99
  }
98
100
  function idString(value) {
99
- return value === undefined || value === null || value === ""
100
- ? undefined
101
- : String(value);
101
+ return value === undefined || value === null || value === "" ? undefined : String(value);
102
102
  }
103
103
  // --- Transport and failures ---------------------------------------------------
104
104
  /**
@@ -158,7 +158,9 @@ function breezeFailure(status, headers, text) {
158
158
  const wait = retryAfterMs(headers);
159
159
  return new ConnectorCallError("unavailable", `Breeze is failing upstream (HTTP ${status}).`, wait === undefined ? {} : { retryAfterMs: wait });
160
160
  }
161
- return new ConnectorCallError("connector_call_failed", `Breeze request failed (HTTP ${status}).${detail}`, { retryable: false });
161
+ return new ConnectorCallError("connector_call_failed", `Breeze request failed (HTTP ${status}).${detail}`, {
162
+ retryable: false,
163
+ });
162
164
  }
163
165
  function shortText(value) {
164
166
  if (typeof value === "string")
@@ -211,7 +213,9 @@ async function callBreeze(send, request, ctx, options = {}) {
211
213
  // only the parse does. A hatch hands back what arrived.
212
214
  if (options.raw)
213
215
  return text;
214
- throw new ConnectorCallError("connector_call_failed", "Breeze returned a non-JSON successful response.", { retryable: false });
216
+ throw new ConnectorCallError("connector_call_failed", "Breeze returned a non-JSON successful response.", {
217
+ retryable: false,
218
+ });
215
219
  }
216
220
  const failure = reportedFailure(payload);
217
221
  if (failure)
@@ -253,7 +257,23 @@ const READ_ENDPOINTS = new Map([
253
257
  ["/account/summary", []],
254
258
  ["/account/list_log", ["action", "start", "end", "user_id", "details", "limit"]],
255
259
  // Undocumented since ~2023-09; parameters from the 2023-06-07 reference.
256
- ["/giving/list", ["start", "end", "person_id", "include_family", "amount_min", "amount_max", "method_ids", "fund_ids", "envelope_number", "batches", "forms", "pledge_ids"]],
260
+ [
261
+ "/giving/list",
262
+ [
263
+ "start",
264
+ "end",
265
+ "person_id",
266
+ "include_family",
267
+ "amount_min",
268
+ "amount_max",
269
+ "method_ids",
270
+ "fund_ids",
271
+ "envelope_number",
272
+ "batches",
273
+ "forms",
274
+ "pledge_ids",
275
+ ],
276
+ ],
257
277
  ["/giving/view", ["payment_id"]],
258
278
  ["/funds/list", ["include_totals"]],
259
279
  ["/pledges/list_campaigns", []],
@@ -289,8 +309,7 @@ function queryPairs(value) {
289
309
  throw new ConnectorCallError("invalid_args", `Query parameter ${name} appears more than once; Breeze reads one value per name.`);
290
310
  }
291
311
  const item = pair["value"];
292
- query[name] =
293
- item !== null && typeof item === "object" ? JSON.stringify(item) : item;
312
+ query[name] = item !== null && typeof item === "object" ? JSON.stringify(item) : item;
294
313
  }
295
314
  return query;
296
315
  }
@@ -325,9 +344,7 @@ function projectPerson(value) {
325
344
  nickName: person["nick_name"] || undefined,
326
345
  middleName: person["middle_name"] || undefined,
327
346
  maidenName: person["maiden_name"] || undefined,
328
- details: details && typeof details === "object" && !Array.isArray(details)
329
- ? details
330
- : undefined,
347
+ details: details && typeof details === "object" && !Array.isArray(details) ? details : undefined,
331
348
  family: family.length ? family.map(projectFamilyMember) : undefined,
332
349
  });
333
350
  }
@@ -610,8 +627,12 @@ function tools(send, defaultPageSize) {
610
627
  outputSchema: {
611
628
  type: "object",
612
629
  properties: {
613
- id: { type: "string" }, name: { type: "string" }, subdomain: { type: "string" },
614
- timezone: { type: "string" }, country: { type: "string" }, currency: { type: "string" },
630
+ id: { type: "string" },
631
+ name: { type: "string" },
632
+ subdomain: { type: "string" },
633
+ timezone: { type: "string" },
634
+ country: { type: "string" },
635
+ currency: { type: "string" },
615
636
  },
616
637
  required: ["name"],
617
638
  },
@@ -637,11 +658,16 @@ function tools(send, defaultPageSize) {
637
658
  tagId: idProperty("Only people carrying this tag id from list_tags."),
638
659
  filter: {
639
660
  type: "object",
640
- description: "Breeze filter_json: profile fieldId to option ids joined by '-', e.g. {\"2000138015\":\"226-227\"}.",
661
+ description: 'Breeze filter_json: profile fieldId to option ids joined by \'-\', e.g. {"2000138015":"226-227"}.',
662
+ },
663
+ includeDetails: {
664
+ type: "boolean",
665
+ description: "Include every profile field (slower, larger). Defaults to false.",
641
666
  },
642
- includeDetails: { type: "boolean", description: "Include every profile field (slower, larger). Defaults to false." },
643
667
  limit: {
644
- type: "integer", minimum: 1, maximum: MAX_PAGE_SIZE,
668
+ type: "integer",
669
+ minimum: 1,
670
+ maximum: MAX_PAGE_SIZE,
645
671
  description: `People per page, 1 to connecta's ${MAX_PAGE_SIZE}. Defaults to ${defaultPageSize}.`,
646
672
  },
647
673
  cursor: { type: "string", pattern: ID_PATTERN, description: "Opaque nextCursor from the previous page." },
@@ -712,8 +738,11 @@ function tools(send, defaultPageSize) {
712
738
  items: {
713
739
  type: "object",
714
740
  properties: {
715
- fieldId: { type: "string" }, name: { type: "string" }, type: { type: "string" },
716
- section: { type: "string" }, options: { type: "array", items: { type: "object" } },
741
+ fieldId: { type: "string" },
742
+ name: { type: "string" },
743
+ type: { type: "string" },
744
+ section: { type: "string" },
745
+ options: { type: "array", items: { type: "object" } },
717
746
  },
718
747
  required: ["fieldId", "name", "type"],
719
748
  },
@@ -763,7 +792,9 @@ function tools(send, defaultPageSize) {
763
792
  end: dateProperty("Last day, YYYY-MM-DD. Defaults to the end of this month."),
764
793
  calendarId: idProperty("Only events on this calendar from list_calendars."),
765
794
  limit: {
766
- type: "integer", minimum: 1, maximum: MAX_EVENT_LIMIT,
795
+ type: "integer",
796
+ minimum: 1,
797
+ maximum: MAX_EVENT_LIMIT,
767
798
  description: `Instances returned, 1 to Breeze's ${MAX_EVENT_LIMIT}. Defaults to ${DEFAULT_EVENT_LIMIT}.`,
768
799
  },
769
800
  raw: RAW_PROPERTY,
@@ -776,9 +807,12 @@ function tools(send, defaultPageSize) {
776
807
  items: {
777
808
  type: "object",
778
809
  properties: {
779
- id: { type: "string", description: "Instance id." }, eventId: { type: "string", description: "Series id." },
780
- name: { type: "string" }, calendarId: { type: "string" },
781
- startsAt: { type: ["string", "null"] }, endsAt: { type: ["string", "null"] },
810
+ id: { type: "string", description: "Instance id." },
811
+ eventId: { type: "string", description: "Series id." },
812
+ name: { type: "string" },
813
+ calendarId: { type: "string" },
814
+ startsAt: { type: ["string", "null"] },
815
+ endsAt: { type: ["string", "null"] },
782
816
  },
783
817
  required: ["id", "name"],
784
818
  },
@@ -790,7 +824,10 @@ function tools(send, defaultPageSize) {
790
824
  handler: async (args, ctx) => {
791
825
  const limit = args["limit"] ?? DEFAULT_EVENT_LIMIT;
792
826
  const rows = asArray(await get("/events", {
793
- start: args["start"], end: args["end"], category_id: args["calendarId"], limit,
827
+ start: args["start"],
828
+ end: args["end"],
829
+ category_id: args["calendarId"],
830
+ limit,
794
831
  }, ctx));
795
832
  return {
796
833
  events: args["raw"] === true ? rows : rows.map(projectEvent),
@@ -841,8 +878,11 @@ function tools(send, defaultPageSize) {
841
878
  items: {
842
879
  type: "object",
843
880
  properties: {
844
- personId: { type: "string" }, firstName: { type: "string" }, lastName: { type: "string" },
845
- checkedInAt: { type: ["string", "null"] }, checkedOutAt: { type: ["string", "null"] },
881
+ personId: { type: "string" },
882
+ firstName: { type: "string" },
883
+ lastName: { type: "string" },
884
+ checkedInAt: { type: ["string", "null"] },
885
+ checkedOutAt: { type: ["string", "null"] },
846
886
  },
847
887
  required: ["personId"],
848
888
  },
@@ -853,7 +893,9 @@ function tools(send, defaultPageSize) {
853
893
  handler: async (args, ctx) => {
854
894
  const wantDetails = args["includeNames"] === true || args["raw"] === true;
855
895
  const rows = asArray(await get("/events/attendance/list", {
856
- instance_id: args["instanceId"], type: "person", details: wantDetails ? "true" : "false",
896
+ instance_id: args["instanceId"],
897
+ type: "person",
898
+ details: wantDetails ? "true" : "false",
857
899
  }, ctx));
858
900
  if (args["raw"] === true)
859
901
  return { attendance: rows };
@@ -887,8 +929,11 @@ function tools(send, defaultPageSize) {
887
929
  items: {
888
930
  type: "object",
889
931
  properties: {
890
- id: { type: "string" }, name: { type: "string" }, slug: { type: "string" },
891
- archived: { type: "boolean" }, createdAt: { type: "string" },
932
+ id: { type: "string" },
933
+ name: { type: "string" },
934
+ slug: { type: "string" },
935
+ archived: { type: "boolean" },
936
+ createdAt: { type: "string" },
892
937
  },
893
938
  required: ["id", "name"],
894
939
  },
@@ -900,8 +945,11 @@ function tools(send, defaultPageSize) {
900
945
  forms: asArray(await get("/forms/list_forms", { is_archived: args["archived"] === true ? 1 : 0 }, ctx)).map((value) => {
901
946
  const form = asRecord(value);
902
947
  return compact({
903
- id: idString(form["id"]), name: form["name"], slug: form["url_slug"],
904
- archived: flag(form["is_archived"]), createdAt: form["created_on"],
948
+ id: idString(form["id"]),
949
+ name: form["name"],
950
+ slug: form["url_slug"],
951
+ archived: flag(form["is_archived"]),
952
+ createdAt: form["created_on"],
905
953
  });
906
954
  }),
907
955
  }),
@@ -919,7 +967,9 @@ function tools(send, defaultPageSize) {
919
967
  items: {
920
968
  type: "object",
921
969
  properties: {
922
- fieldId: { type: "string" }, name: { type: "string" }, type: { type: "string" },
970
+ fieldId: { type: "string" },
971
+ name: { type: "string" },
972
+ type: { type: "string" },
923
973
  options: { type: "array", items: { type: "object" } },
924
974
  },
925
975
  required: ["fieldId", "name"],
@@ -951,8 +1001,10 @@ function tools(send, defaultPageSize) {
951
1001
  items: {
952
1002
  type: "object",
953
1003
  properties: {
954
- id: { type: "string" }, personId: { type: ["string", "null"] },
955
- createdAt: { type: "string" }, responses: { type: "object" },
1004
+ id: { type: "string" },
1005
+ personId: { type: ["string", "null"] },
1006
+ createdAt: { type: "string" },
1007
+ responses: { type: "object" },
956
1008
  },
957
1009
  required: ["id"],
958
1010
  },
@@ -962,7 +1014,8 @@ function tools(send, defaultPageSize) {
962
1014
  },
963
1015
  handler: async (args, ctx) => ({
964
1016
  entries: asArray(await get("/forms/list_form_entries", {
965
- form_id: args["formId"], details: args["includeResponses"] === true ? 1 : 0,
1017
+ form_id: args["formId"],
1018
+ details: args["includeResponses"] === true ? 1 : 0,
966
1019
  }, ctx)).map((value) => {
967
1020
  const entry = asRecord(value);
968
1021
  return compact({
@@ -987,8 +1040,11 @@ function tools(send, defaultPageSize) {
987
1040
  items: {
988
1041
  type: "object",
989
1042
  properties: {
990
- personId: { type: "string" }, roleIds: { type: "array", items: { type: "string" } },
991
- response: { type: "string" }, comment: { type: "string" }, rsvpedAt: { type: ["string", "null"] },
1043
+ personId: { type: "string" },
1044
+ roleIds: { type: "array", items: { type: "string" } },
1045
+ response: { type: "string" },
1046
+ comment: { type: "string" },
1047
+ rsvpedAt: { type: ["string", "null"] },
992
1048
  },
993
1049
  required: ["personId", "roleIds"],
994
1050
  },
@@ -1033,7 +1089,8 @@ function tools(send, defaultPageSize) {
1033
1089
  const role = asRecord(value);
1034
1090
  const quantity = Number(role["quantity"]);
1035
1091
  return compact({
1036
- id: idString(role["id"]), name: role["name"],
1092
+ id: idString(role["id"]),
1093
+ name: role["name"],
1037
1094
  quantity: Number.isFinite(quantity) ? quantity : undefined,
1038
1095
  });
1039
1096
  }),
@@ -1045,7 +1102,9 @@ function tools(send, defaultPageSize) {
1045
1102
  annotations: readOnly,
1046
1103
  inputSchema: namedInput({
1047
1104
  action: {
1048
- type: "string", pattern: "^[a-z_]+$", maxLength: 40,
1105
+ type: "string",
1106
+ pattern: "^[a-z_]+$",
1107
+ maxLength: 40,
1049
1108
  description: "Logged action, e.g. person_updated, tag_assign, contribution_added; the guide lists all.",
1050
1109
  },
1051
1110
  start: dateProperty("Only actions on or after this day, YYYY-MM-DD."),
@@ -1053,7 +1112,9 @@ function tools(send, defaultPageSize) {
1053
1112
  userId: idProperty("Only actions by this Breeze user id."),
1054
1113
  includeDetails: { type: "boolean", description: "Include Breeze's unstandardized description." },
1055
1114
  limit: {
1056
- type: "integer", minimum: 1, maximum: MAX_LOG_LIMIT,
1115
+ type: "integer",
1116
+ minimum: 1,
1117
+ maximum: MAX_LOG_LIMIT,
1057
1118
  description: `Rows returned, 1 to Breeze's ${MAX_LOG_LIMIT}. Defaults to ${DEFAULT_LOG_LIMIT}.`,
1058
1119
  },
1059
1120
  }, ["action"]),
@@ -1065,9 +1126,12 @@ function tools(send, defaultPageSize) {
1065
1126
  items: {
1066
1127
  type: "object",
1067
1128
  properties: {
1068
- id: { type: "string" }, action: { type: "string" }, userId: { type: "string" },
1129
+ id: { type: "string" },
1130
+ action: { type: "string" },
1131
+ userId: { type: "string" },
1069
1132
  object: { description: "Parsed object_json, usually the affected id." },
1070
- createdAt: { type: "string" }, details: {},
1133
+ createdAt: { type: "string" },
1134
+ details: {},
1071
1135
  },
1072
1136
  required: ["id", "action"],
1073
1137
  },
@@ -1079,8 +1143,12 @@ function tools(send, defaultPageSize) {
1079
1143
  handler: async (args, ctx) => {
1080
1144
  const limit = args["limit"] ?? DEFAULT_LOG_LIMIT;
1081
1145
  const rows = asArray(await get("/account/list_log", {
1082
- action: args["action"], start: args["start"], end: args["end"], user_id: args["userId"],
1083
- details: args["includeDetails"] === true ? 1 : 0, limit,
1146
+ action: args["action"],
1147
+ start: args["start"],
1148
+ end: args["end"],
1149
+ user_id: args["userId"],
1150
+ details: args["includeDetails"] === true ? 1 : 0,
1151
+ limit,
1084
1152
  }, ctx));
1085
1153
  return {
1086
1154
  entries: rows.map((value) => {
@@ -1123,9 +1191,15 @@ function tools(send, defaultPageSize) {
1123
1191
  items: {
1124
1192
  type: "object",
1125
1193
  properties: {
1126
- id: { type: "string" }, paidOn: { type: "string" }, amount: { type: "string" },
1127
- method: { type: "string" }, personId: { type: "string" }, firstName: { type: "string" },
1128
- lastName: { type: "string" }, batchNumber: { type: "string" }, funds: { type: "array", items: { type: "object" } },
1194
+ id: { type: "string" },
1195
+ paidOn: { type: "string" },
1196
+ amount: { type: "string" },
1197
+ method: { type: "string" },
1198
+ personId: { type: "string" },
1199
+ firstName: { type: "string" },
1200
+ lastName: { type: "string" },
1201
+ batchNumber: { type: "string" },
1202
+ funds: { type: "array", items: { type: "object" } },
1129
1203
  },
1130
1204
  required: ["id", "amount"],
1131
1205
  },
@@ -1139,16 +1213,21 @@ function tools(send, defaultPageSize) {
1139
1213
  if (args["includeFamily"] === true && args["personId"] === undefined) {
1140
1214
  throw new ConnectorCallError("invalid_args", "includeFamily needs a personId.");
1141
1215
  }
1142
- const joined = (value) => Array.isArray(value) ? value.join("-") : undefined;
1216
+ const joined = (value) => (Array.isArray(value) ? value.join("-") : undefined);
1143
1217
  // Breeze's 2023 table says DD-MM-YYYY while its own example request
1144
1218
  // sends 2022-1-15; the server parses both, so the unambiguous ISO day
1145
1219
  // is what the schema accepts and what goes on the wire.
1146
1220
  const rows = asArray(await get("/giving/list", {
1147
- start: args["start"], end: args["end"], person_id: args["personId"],
1221
+ start: args["start"],
1222
+ end: args["end"],
1223
+ person_id: args["personId"],
1148
1224
  include_family: args["includeFamily"] === true ? 1 : undefined,
1149
- amount_min: args["amountMin"], amount_max: args["amountMax"],
1150
- fund_ids: joined(args["fundIds"]), method_ids: joined(args["methodIds"]),
1151
- batches: joined(args["batchNumbers"]), envelope_number: args["envelopeNumber"],
1225
+ amount_min: args["amountMin"],
1226
+ amount_max: args["amountMax"],
1227
+ fund_ids: joined(args["fundIds"]),
1228
+ method_ids: joined(args["methodIds"]),
1229
+ batches: joined(args["batchNumbers"]),
1230
+ envelope_number: args["envelopeNumber"],
1152
1231
  }, ctx));
1153
1232
  return {
1154
1233
  contributions: args["raw"] === true ? rows : rows.map(projectContribution),
@@ -1170,8 +1249,10 @@ function tools(send, defaultPageSize) {
1170
1249
  items: {
1171
1250
  type: "object",
1172
1251
  properties: {
1173
- id: { type: "string" }, name: { type: "string" },
1174
- taxDeductible: { type: "boolean" }, isDefault: { type: "boolean" },
1252
+ id: { type: "string" },
1253
+ name: { type: "string" },
1254
+ taxDeductible: { type: "boolean" },
1255
+ isDefault: { type: "boolean" },
1175
1256
  },
1176
1257
  required: ["id", "name"],
1177
1258
  },
@@ -1183,8 +1264,10 @@ function tools(send, defaultPageSize) {
1183
1264
  funds: asArray(await get("/funds/list", {}, ctx)).map((value) => {
1184
1265
  const fund = asRecord(value);
1185
1266
  return compact({
1186
- id: idString(fund["id"]), name: fund["name"],
1187
- taxDeductible: flag(fund["tax_deductible"]), isDefault: flag(fund["is_default"]),
1267
+ id: idString(fund["id"]),
1268
+ name: fund["name"],
1269
+ taxDeductible: flag(fund["tax_deductible"]),
1270
+ isDefault: flag(fund["is_default"]),
1188
1271
  });
1189
1272
  }),
1190
1273
  }),
@@ -1201,7 +1284,8 @@ function tools(send, defaultPageSize) {
1201
1284
  outputSchema: PERSON_SCHEMA,
1202
1285
  handler: async (args, ctx) => {
1203
1286
  const payload = await get("/people/add", {
1204
- first: args["firstName"], last: args["lastName"],
1287
+ first: args["firstName"],
1288
+ last: args["lastName"],
1205
1289
  fields_json: args["fields"] === undefined ? undefined : fieldsJson(args["fields"]),
1206
1290
  }, ctx);
1207
1291
  const person = Array.isArray(payload) ? payload[0] : payload;
@@ -1229,7 +1313,10 @@ function tools(send, defaultPageSize) {
1229
1313
  name: "assign_tag",
1230
1314
  description: "Add one tag to one person. Tag ids come from list_tags; adding a tag the person already has changes nothing.",
1231
1315
  annotations: additive,
1232
- inputSchema: namedInput({ personId: PERSON_ID, tagId: idProperty("Tag id from list_tags.") }, ["personId", "tagId"]),
1316
+ inputSchema: namedInput({ personId: PERSON_ID, tagId: idProperty("Tag id from list_tags.") }, [
1317
+ "personId",
1318
+ "tagId",
1319
+ ]),
1233
1320
  outputSchema: WROTE_SCHEMA("assigned"),
1234
1321
  handler: async (args, ctx) => {
1235
1322
  requireTrue(await get("/tags/assign", { person_id: args["personId"], tag_id: args["tagId"] }, ctx), "the tag assignment");
@@ -1240,7 +1327,10 @@ function tools(send, defaultPageSize) {
1240
1327
  name: "unassign_tag",
1241
1328
  description: "Remove one tag from one person. The tag itself and its other people are untouched.",
1242
1329
  annotations: destructive,
1243
- inputSchema: namedInput({ personId: PERSON_ID, tagId: idProperty("Tag id from list_tags.") }, ["personId", "tagId"]),
1330
+ inputSchema: namedInput({ personId: PERSON_ID, tagId: idProperty("Tag id from list_tags.") }, [
1331
+ "personId",
1332
+ "tagId",
1333
+ ]),
1244
1334
  outputSchema: WROTE_SCHEMA("unassigned"),
1245
1335
  handler: async (args, ctx) => {
1246
1336
  requireTrue(await get("/tags/unassign", { person_id: args["personId"], tag_id: args["tagId"] }, ctx), "the tag removal");
@@ -1255,7 +1345,9 @@ function tools(send, defaultPageSize) {
1255
1345
  outputSchema: WROTE_SCHEMA("checkedIn"),
1256
1346
  handler: async (args, ctx) => {
1257
1347
  requireTrue(await get("/events/attendance/add", {
1258
- person_id: args["personId"], instance_id: args["instanceId"], direction: "in",
1348
+ person_id: args["personId"],
1349
+ instance_id: args["instanceId"],
1350
+ direction: "in",
1259
1351
  }, ctx), "the check-in");
1260
1352
  return { checkedIn: true };
1261
1353
  },
@@ -1267,75 +1359,7 @@ function usageGuide(subdomain, purpose, instructions) {
1267
1359
  const churchInstructions = instructions?.trim();
1268
1360
  return `# Breeze ChMS usage
1269
1361
 
1270
- Church database for ${subdomain}.breezechms.com (Breeze ChMS, now Tithely Church Management): people, tags, events, attendance, forms, volunteers, and giving.
1271
-
1272
- Church purpose: ${purpose}
1273
-
1274
- ## Sensitive data: reduce in code
1275
-
1276
- People records carry home addresses, phones, birthdates, and family ties, and
1277
- giving records reveal who gives and how much — religious affiliation by proxy.
1278
- Read them inside \`execute_code\` and return only the counts, totals, or the
1279
- few fields the task needs. Never echo whole profiles or donor lists.
1280
-
1281
- ## Ids and profile fields
1282
-
1283
- - Ids are numeric strings. Read them from a list tool; never guess one.
1284
- - Events have two ids: an instance id (\`list_events\` \`id\`) for one
1285
- occurrence, and a series \`eventId\`. Attendance, volunteers, and
1286
- check-in take the instance id.
1287
- - Person \`details\` are keyed by profile \`fieldId\`, not by name. Call
1288
- \`list_profile_fields\` once and map ids to names in code. Multiple-choice
1289
- values and \`list_people\` filters use option ids from the same call;
1290
- join several with \`-\`.
1291
- - \`list_people\` has no name search. List without details and match names
1292
- in code, then \`get_person\` the hits.
1293
- - Projections rename Breeze's snake_case fields to camelCase and drop photo
1294
- paths and internal columns; \`raw: true\` returns Breeze's rows.
1295
-
1296
- ## Pagination
1297
-
1298
- \`list_people\` pages by \`cursor\`: continue while \`page.hasMore\`. Breeze
1299
- reports no total, so a full page sets \`hasMore\` and the next may be empty.
1300
- \`list_events\` and \`list_account_log\` do not page: \`truncated: true\`
1301
- means narrow the date range. Other lists return everything.
1302
-
1303
- ## Giving
1304
-
1305
- \`list_contributions\` and \`list_funds\` call endpoints Breeze removed from
1306
- its public reference in 2023 but still serves. They may change or vanish
1307
- without notice; a failure there is not your argument's fault. Amounts are
1308
- decimal strings; \`totalAmount\` is an exact sum. Online gifts processed by
1309
- Tithe.ly arrive through its sync, batched per deposit, so the latest days
1310
- may be missing. Read the currency from \`get_account_summary\`.
1311
-
1312
- ## Writes and the hatches
1313
-
1314
- - Every write, \`add_person\`, \`assign_tag\`, and \`record_check_in\`
1315
- included, is a \`call_destructive_tool\` call unless the deployment exempts
1316
- it in \`execute.approval\`; a program inside \`execute_code\` is refused
1317
- the rest. Those three only add, and \`add_person\` never deduplicates.
1318
- - \`update_person\` field values: text, date (M/D/YYYY), or option id in
1319
- \`response\`; email, phone, address, and family_role values in \`details\`.
1320
- - Every Breeze call is a GET, writes included, so the hatches split by
1321
- endpoint. \`breeze_api_get\` admits only reviewed read endpoints and their
1322
- documented parameters, such as \`/tags/list_folders\`,
1323
- \`/events/list_event\`, \`/events/attendance/eligible\`, \`/giving/view\`,
1324
- and \`/pledges/list_campaigns\`; it refuses anything else. Everything else
1325
- — event, family, volunteer, and form writes, giving writes, check-out,
1326
- undocumented parameters — is \`breeze_api_mutate\`. Paths are below
1327
- \`/api\`; \`*_json\` values may be passed as objects.
1328
- - \`list_account_log\` actions include person_created, person_updated,
1329
- person_deleted, tag_assign, tag_unassign, event_created,
1330
- attendance_deleted, form_entry_updated, contribution_added,
1331
- contribution_updated, contribution_deleted, and batch_updated.
1332
-
1333
- ## Rate limits
1334
-
1335
- Breeze publishes no limit and sends no rate headers; a third party reports
1336
- about 20 requests per minute. Keep loops sequential and prefer one list call
1337
- over many single reads.
1338
- ${churchInstructions ? `\n## Church instructions\n\n${churchInstructions}\n` : ""}`;
1362
+ Church database for ${subdomain}${skill.fragments.guide_0}${purpose}${skill.fragments.guide_1}${churchInstructions ? `\n## ${skill.instructionsHeading}\n\n${churchInstructions}\n` : ""}`;
1339
1363
  }
1340
1364
  // --- Construction --------------------------------------------------------------
1341
1365
  function normalizeSubdomain(raw) {
@@ -1345,17 +1369,31 @@ function normalizeSubdomain(raw) {
1345
1369
  }
1346
1370
  return value;
1347
1371
  }
1372
+ /** The closed options breeze() accepts; see `assertKnownOptions`. */
1373
+ const BREEZE_OPTIONS = optionsOf()({ ...PROVIDER_COMMON, ...keys("subdomain", "defaultPageSize") });
1348
1374
  /** A maintained Breeze ChMS connection for one church's subdomain. */
1349
- export function breeze(id, options) {
1375
+ export const breeze = asProviderFactory({
1376
+ name: "breeze",
1377
+ title: "Breeze ChMS",
1378
+ kind: "api",
1379
+ readme: "Breeze ChMS",
1380
+ bundle: {
1381
+ "baselineGzip": 20637,
1382
+ "maxGzip": 80637,
1383
+ "note": "./providers/breeze starts at 20,637 B gzip: a hand-written api() surface with no hosted-MCP interface, so it carries no MCP SDK. The cap uses the existing baseline + 60,000 B policy.",
1384
+ },
1385
+ skill,
1386
+ options: BREEZE_OPTIONS,
1387
+ create: breezeConnector,
1388
+ });
1389
+ function breezeConnector(id, options) {
1350
1390
  const purpose = typeof options.purpose === "string" ? options.purpose.trim() : "";
1351
1391
  if (!purpose) {
1352
1392
  throw new Error("breeze() requires a non-empty church purpose.");
1353
1393
  }
1354
1394
  const subdomain = normalizeSubdomain(options.subdomain);
1355
1395
  const defaultPageSize = options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
1356
- if (!Number.isInteger(defaultPageSize) ||
1357
- defaultPageSize < 1 ||
1358
- defaultPageSize > MAX_PAGE_SIZE) {
1396
+ if (!Number.isInteger(defaultPageSize) || defaultPageSize < 1 || defaultPageSize > MAX_PAGE_SIZE) {
1359
1397
  throw new Error(`breeze() defaultPageSize must be a whole number between 1 and ${MAX_PAGE_SIZE}.`);
1360
1398
  }
1361
1399
  const send = breezeTransport(subdomain);
@@ -1377,9 +1415,15 @@ export function breeze(id, options) {
1377
1415
  const account = asRecord(await callBreeze(send, { method: "GET", path: "/account/summary" }, { ...ctx, credential: { get: async () => value, getAll: async () => ({ value }) } }));
1378
1416
  const reported = typeof account["subdomain"] === "string" ? account["subdomain"].toLowerCase() : undefined;
1379
1417
  if (reported && reported !== subdomain) {
1380
- return { ok: false, message: `The key belongs to ${reported}${BREEZE_HOST_SUFFIX}, not ${subdomain}${BREEZE_HOST_SUFFIX}.` };
1418
+ return {
1419
+ ok: false,
1420
+ message: `The key belongs to ${reported}${BREEZE_HOST_SUFFIX}, not ${subdomain}${BREEZE_HOST_SUFFIX}.`,
1421
+ };
1381
1422
  }
1382
- return { ok: true, message: `Authenticated to ${String(account["name"] ?? "the church account")} (${subdomain}${BREEZE_HOST_SUFFIX}).` };
1423
+ return {
1424
+ ok: true,
1425
+ message: `Authenticated to ${String(account["name"] ?? "the church account")} (${subdomain}${BREEZE_HOST_SUFFIX}).`,
1426
+ };
1383
1427
  }
1384
1428
  catch (error) {
1385
1429
  return {