@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": "# Google Slides usage\n\nActs as the signed-in person in Google Slides through Workspace delegation: read, create, and edit the decks they can open.\n\nConnection purpose: \n\n## Whose decks\n\nEvery call acts as the Workspace account deployment config maps the caller\nto, and reaches exactly the decks that person can open. No argument names an\naccount. A call with none mapped fails `auth_required`; only an operator can\nchange the mapping. A deck the person cannot see and one that does not exist\nfail alike — Google does not say which.\n\n## Finding a deck\n\nSlides cannot list or search decks; that is Google Drive's job. Take the id\nfrom a Drive search or from a URL (`/presentation/d/<id>/`).\n\n## Reading\n\n- `get_presentation` returns each slide's text in reading order (top to\n bottom, then left to right), speaker notes, and alt text for images,\n videos, and charts. Empty placeholders are listed with their id and type;\n lines and other empty shapes are counted in `omittedElements`. `index`\n is 0-based, the numbering `create_slide` takes.\n- Nothing is cut without a way back. Text past `maxCharsPerSlide`, or past\n what one result can carry (about 192 KB), ends with a marker, and its\n element's `textCursor` (the slide's `notesCursor` for notes) is a\n `get_page` cursor that continues from the cut. A slide too crowded to\n list whole gives `elementsNotShown` and an `elementsCursor`.\n- `get_page` reads any page by objectId — a slide, a layout (its\n placeholders' types and indexes, for `placeholderIdMappings`), a master,\n a notes page (`notesPageId`), or the notes master (`notesMasterId`) —\n listing every element, groups opened,\n and continuing long text across pages from `page.nextCursor`.\n- `list_layouts` lists masters and their layouts; `get_presentation`\n previews them, and `layoutsCursor` continues where the preview stops.\n- `raw: true` returns Slides' own JSON: `get_presentation` a page of\n slides, `get_page` the page and its top-level elements. One too large\n for a result is named (`rawNotShown`) or sent in `rawJson` chunks;\n join the chunks' `json` in order and parse.\n- Every cursor is bound to the deck and the revision it was read at. A\n `conflict` on a cursor means the deck changed: start again without one.\n A page may hold fewer rows than `limit`; follow `page.nextCursor`.\n- `get_slide_thumbnail` returns a link, never the image. The link opens as\n this person for about 30 minutes; do not share it.\n\n## Writing\n\n- `create_presentation` and `create_slide` are additive. A new slide is\n empty: fill it with `batch_update_presentation` (`insertText` into its\n placeholders, after reading their ids with `get_presentation`).\n- `replace_all_text` changes every literal match, case-sensitive unless\n `matchCase: false`. Pass `requiredRevisionId` to refuse a deck that\n changed since you read it.\n- `batch_update_presentation` takes Slides' own Request objects and always\n requires the `revisionId` from the read the requests were built on. All\n requests apply or none do, except comment changes (below). Each write\n returns the new `revisionId` for the next one. A `conflict` means the\n deck changed since that read: re-read it, rebuild the requests, and send\n them with the new `revisionId`. Its replies keep every id at every depth\n whole, in every reply; text is cut first, and a large reply's other fields\n are named in `cut`. Ids too many for this tool's 192 KiB budget are all\n returned anyway, without text; past 256 KiB, more than a program can\n receive, call the tool directly and page with `connecta.result`. A batch\n whose ids pass even that returns its comment state and `repliesNotShown`;\n read what it made with `list_comments`, `get_presentation`, or\n `get_page`, and do not send it again.\n- An id or revision too long to copy into a result is left out and flagged\n (`revisionIdNotShown`), never cut. A failure that says the write\n applied means Google answered 2xx: do not repeat it; re-read instead.\n- Nothing here shares, moves, or deletes a deck.\n\n## Comments\n\n- `list_comments` lists the deck's threads, or one page's with\n `pageObjectId` (a speaker-notes comment is on the notes page). Each has\n its `commentId`, `status` (OPEN or RESOLVED), where it is anchored\n (`pageObjectIds`, `objectIds`), the `quote` it was made on, its\n `headPost`, and `replies`. A post's `author.user` (`users/…`) is\n the stable id; `displayName` is only a name. Slides gives no `user`\n for an anonymous or imported post. Text too long for one result continues\n on the next page: a row with `continued` picks up its thread where the\n last stopped, and `repliesOffset` counts the replies before it. Its\n cursor is bound to the threads and their anchors as well as the revision,\n so a comment added or moved between pages is a `conflict` too. Reading\n comments needs comment access; a view-only account is refused.\n- The projection leaves out each post's HTML (`contentHtml`), whether it\n came from a copied deck, and the text and cell ranges an anchor covers.\n `raw: true` returns each thread as Slides sends it, with\n `commentAnchors` added (its anchors, each with its `pageObjectId`),\n in `rawJson` chunks when one is too large.\n- `create_comment` and `create_comment_reply` are additive and return\n every new id whole. `update_comment_thread` resolves, reopens, or\n reassigns a thread (one at a time; a reassignment needs `content`),\n replacing its status or assignee, and is destructive,\n like `update_comment_post`, `delete_comment`, and\n `delete_comment_reply`; Slides allows the last three only to the\n post's author. Comments notify people the way the Slides editor does.\n- Slides saves comment changes apart from the rest of a write. When it does\n not confirm they saved, the result carries `commentUpdateState` — for\n example `ALL_FAILED_UNKNOWN_REASON` — and the write's other changes may\n still have applied. Do not repeat it: re-read with `list_comments`, then\n decide.\n",
4
+ "instructionsHeading": "Connection instructions",
5
+ "fragments": {
6
+ "guide_0": "# Google Slides usage\n\nActs as the signed-in person in Google Slides through Workspace delegation: read, create, and edit the decks they can open.\n\nConnection purpose: ",
7
+ "guide_1": "\n\n## Whose decks\n\nEvery call acts as the Workspace account deployment config maps the caller\nto, and reaches exactly the decks that person can open. No argument names an\naccount. A call with none mapped fails `auth_required`; only an operator can\nchange the mapping. A deck the person cannot see and one that does not exist\nfail alike — Google does not say which.\n\n## Finding a deck\n\nSlides cannot list or search decks; that is Google Drive's job. Take the id\nfrom a Drive search or from a URL (`/presentation/d/<id>/`).\n\n## Reading\n\n- `get_presentation` returns each slide's text in reading order (top to\n bottom, then left to right), speaker notes, and alt text for images,\n videos, and charts. Empty placeholders are listed with their id and type;\n lines and other empty shapes are counted in `omittedElements`. `index`\n is 0-based, the numbering `create_slide` takes.\n- Nothing is cut without a way back. Text past `maxCharsPerSlide`, or past\n what one result can carry (about 192 KB), ends with a marker, and its\n element's `textCursor` (the slide's `notesCursor` for notes) is a\n `get_page` cursor that continues from the cut. A slide too crowded to\n list whole gives `elementsNotShown` and an `elementsCursor`.\n- `get_page` reads any page by objectId — a slide, a layout (its\n placeholders' types and indexes, for `placeholderIdMappings`), a master,\n a notes page (`notesPageId`), or the notes master (`notesMasterId`) —\n listing every element, groups opened,\n and continuing long text across pages from `page.nextCursor`.\n- `list_layouts` lists masters and their layouts; `get_presentation`\n previews them, and `layoutsCursor` continues where the preview stops.\n- `raw: true` returns Slides' own JSON: `get_presentation` a page of\n slides, `get_page` the page and its top-level elements. One too large\n for a result is named (`rawNotShown`) or sent in `rawJson` chunks;\n join the chunks' `json` in order and parse.\n- Every cursor is bound to the deck and the revision it was read at. A\n `conflict` on a cursor means the deck changed: start again without one.\n A page may hold fewer rows than `limit`; follow `page.nextCursor`.\n- `get_slide_thumbnail` returns a link, never the image. The link opens as\n this person for about 30 minutes; do not share it.\n\n## Writing\n\n- `create_presentation` and `create_slide` are additive. A new slide is\n empty: fill it with `batch_update_presentation` (`insertText` into its\n placeholders, after reading their ids with `get_presentation`).\n- `replace_all_text` changes every literal match, case-sensitive unless\n `matchCase: false`. Pass `requiredRevisionId` to refuse a deck that\n changed since you read it.\n- `batch_update_presentation` takes Slides' own Request objects and always\n requires the `revisionId` from the read the requests were built on. All\n requests apply or none do, except comment changes (below). Each write\n returns the new `revisionId` for the next one. A `conflict` means the\n deck changed since that read: re-read it, rebuild the requests, and send\n them with the new `revisionId`. Its replies keep every id at every depth\n whole, in every reply; text is cut first, and a large reply's other fields\n are named in `cut`. Ids too many for this tool's 192 KiB budget are all\n returned anyway, without text; past 256 KiB, more than a program can\n receive, call the tool directly and page with `connecta.result`. A batch\n whose ids pass even that returns its comment state and `repliesNotShown`;\n read what it made with `list_comments`, `get_presentation`, or\n `get_page`, and do not send it again.\n- An id or revision too long to copy into a result is left out and flagged\n (`revisionIdNotShown`), never cut. A failure that says the write\n applied means Google answered 2xx: do not repeat it; re-read instead.\n- Nothing here shares, moves, or deletes a deck.\n\n## Comments\n\n- `list_comments` lists the deck's threads, or one page's with\n `pageObjectId` (a speaker-notes comment is on the notes page). Each has\n its `commentId`, `status` (OPEN or RESOLVED), where it is anchored\n (`pageObjectIds`, `objectIds`), the `quote` it was made on, its\n `headPost`, and `replies`. A post's `author.user` (`users/…`) is\n the stable id; `displayName` is only a name. Slides gives no `user`\n for an anonymous or imported post. Text too long for one result continues\n on the next page: a row with `continued` picks up its thread where the\n last stopped, and `repliesOffset` counts the replies before it. Its\n cursor is bound to the threads and their anchors as well as the revision,\n so a comment added or moved between pages is a `conflict` too. Reading\n comments needs comment access; a view-only account is refused.\n- The projection leaves out each post's HTML (`contentHtml`), whether it\n came from a copied deck, and the text and cell ranges an anchor covers.\n `raw: true` returns each thread as Slides sends it, with\n `commentAnchors` added (its anchors, each with its `pageObjectId`),\n in `rawJson` chunks when one is too large.\n- `create_comment` and `create_comment_reply` are additive and return\n every new id whole. `update_comment_thread` resolves, reopens, or\n reassigns a thread (one at a time; a reassignment needs `content`),\n replacing its status or assignee, and is destructive,\n like `update_comment_post`, `delete_comment`, and\n `delete_comment_reply`; Slides allows the last three only to the\n post's author. Comments notify people the way the Slides editor does.\n- Slides saves comment changes apart from the rest of a write. When it does\n not confirm they saved, the result carries `commentUpdateState` — for\n example `ALL_FAILED_UNKNOWN_REASON` — and the write's other changes may\n still have applied. Do not repeat it: re-read with `list_comments`, then\n decide.\n"
8
+ }
9
+ };
@@ -1,5 +1,4 @@
1
- import { type RemoteMcpAuth } from "../connectors/remote-mcp.js";
2
- import type { Connector } from "../types.js";
1
+ import { type RemoteMcpAuth } from "../../connectors/remote-mcp.js";
3
2
  /** Which Stripe environment a static credential reaches. */
4
3
  export type StripeMode = "production" | "sandbox";
5
4
  /** Stripe publishes one hosted MCP endpoint for every account and mode. */
@@ -34,21 +33,15 @@ export interface StripeOAuthOptions extends StripeCommonOptions {
34
33
  */
35
34
  export interface StripeHeaderOptions extends StripeCommonOptions {
36
35
  auth: Exclude<RemoteMcpAuth, {
37
- type: "oauth";
36
+ type: "oauth" | "request";
38
37
  }>;
39
38
  mode: StripeMode;
40
39
  /** Act as one Connect account by sending Stripe's `Stripe-Account` header. */
41
40
  connectedAccount?: string;
42
41
  }
43
42
  export type StripeOptions = StripeOAuthOptions | StripeHeaderOptions;
44
- /**
45
- * One release-reviewed manifest, used both to classify a live tool and as the
46
- * baseline the drift check compares against, so the annotation a caller gets and
47
- * the verdict a check reads can never disagree. Stripe publishes no stability or
48
- * deprecation policy for this tool set, so treat it as unversioned: an
49
- * unclassified, unannotated name fails closed onto the approval path (P5).
50
- */
51
- export declare const STRIPE_VETTED_CATALOG: import("../catalog-drift.js").VettedCatalog;
52
43
  /** A maintained Stripe hosted-MCP connection. */
53
- export declare function stripe(id: string, options: StripeOptions): Connector;
44
+ export declare const stripe: import("../../provider.js").ProviderFactory<StripeOptions>;
45
+ /** @deprecated Read `stripe.definition.classify` instead. Kept for existing imports. */
46
+ export declare const STRIPE_VETTED_CATALOG: import("../../catalog-drift.js").VettedCatalog;
54
47
  export {};
@@ -0,0 +1,266 @@
1
+ import { skill } from "./skill.generated.js";
2
+ import { remoteMcp, withCredentialDefaults } from "../../connectors/remote-mcp.js";
3
+ import { reviewedCatalog } from "../../catalog-drift.js";
4
+ import { keys, optionsOf, strings, variants } from "../../config-schema.js";
5
+ import { CREDENTIAL } from "../../connectors/option-shapes.js";
6
+ import { defineProvider } from "../../provider.js";
7
+ /** Stripe publishes one hosted MCP endpoint for every account and mode. */
8
+ export const STRIPE_MCP_ENDPOINT = "https://mcp.stripe.com/";
9
+ /**
10
+ * Stripe documents no MCP-specific limit, so these transcribe the account limit
11
+ * MCP traffic spends: 100 requests per second live, 25 in a sandbox. OAuth takes
12
+ * the sandbox rule (`mode ?? "sandbox"` below) because one session can reach
13
+ * either mode and the policy is fixed before the account-scoped call begins.
14
+ * The `maxConcurrency` figures are Connecta's own choice — Stripe says
15
+ * per-account and per-endpoint concurrency limits exist but publishes no number.
16
+ */
17
+ const STRIPE_ADMISSION = {
18
+ production: {
19
+ rules: [
20
+ {
21
+ maxConcurrency: 8,
22
+ queueTimeoutMs: 5_000,
23
+ retryAfterMs: 1_000,
24
+ budget: { kind: "rolling-window", maxCalls: 100, windowMs: 1_000 },
25
+ },
26
+ ],
27
+ },
28
+ sandbox: {
29
+ rules: [
30
+ {
31
+ maxConcurrency: 4,
32
+ queueTimeoutMs: 5_000,
33
+ retryAfterMs: 1_000,
34
+ budget: { kind: "rolling-window", maxCalls: 25, windowMs: 1_000 },
35
+ },
36
+ ],
37
+ },
38
+ };
39
+ /**
40
+ * Reviewed in #705's provider audit against https://docs.stripe.com/mcp.
41
+ * Retains the release-reviewed inventory, including names absent from today's
42
+ * public reference. Live annotations were not reverified without credentials.
43
+ * No schema digest is asserted without a captured schema to review.
44
+ */
45
+ const STRIPE_CLASSIFICATION = {
46
+ tools: {
47
+ "stripe_api_search": {
48
+ "verdict": "read",
49
+ "reason": "Searches API method contracts and object records; it does not invoke mutating methods.",
50
+ },
51
+ "stripe_api_details": {
52
+ "verdict": "read",
53
+ "reason": "Reads the parameter contract for an API method; it does not execute that method.",
54
+ },
55
+ "stripe_api_read": {
56
+ "verdict": "read",
57
+ "reason": "The vendor restricts this generic tool to HTTP GET; all mutating methods use stripe_api_write.",
58
+ },
59
+ "get_stripe_account_info": { "verdict": "read", "reason": "Reads metadata for the selected Stripe account." },
60
+ "get_balance_summary": {
61
+ "verdict": "read",
62
+ "reason": "Retrieves Stripe balance summary information without changing vendor state.",
63
+ },
64
+ "list_metrics": {
65
+ "verdict": "read",
66
+ "reason": "Retrieves Stripe metrics information without changing vendor state.",
67
+ },
68
+ "explain_metric": {
69
+ "verdict": "read",
70
+ "reason": "Explains an existing metric definition without creating an analytics run.",
71
+ },
72
+ "metric_drilldown": { "verdict": "read", "reason": "Reads the breakdown of an existing metric." },
73
+ "show_metric_app": { "verdict": "read", "reason": "Displays an existing metric app without changing its data." },
74
+ "list_available_accounts_or_orgs": {
75
+ "verdict": "read",
76
+ "reason": "Retrieves Stripe available accounts or orgs information without changing vendor state.",
77
+ },
78
+ "manage_stripe_accounts": {
79
+ "verdict": "read",
80
+ "reason": "Selects account context for the session; the reviewed contract does not mutate payment or account records.",
81
+ },
82
+ "search_stripe_documentation": {
83
+ "verdict": "read",
84
+ "reason": "Searches Stripe reference documentation without accessing payment mutations.",
85
+ },
86
+ "stripe_api_write": {
87
+ "verdict": "destructive",
88
+ "reason": "Can dispatch POST, PATCH, PUT, or DELETE across the Stripe API, including edits and cancellations.",
89
+ },
90
+ "create_refund": {
91
+ "verdict": "destructive",
92
+ "reason": "Moves money and changes an existing payment; a refund cannot be undone.",
93
+ },
94
+ "stripe_implementation_planner": {
95
+ "verdict": "write",
96
+ "reason": "Creates or continues provider-side planning state.",
97
+ },
98
+ "stripe_analytics": {
99
+ "verdict": "write",
100
+ "reason": "Mixes retrieval with durable query-run creation, so the whole tool is a write.",
101
+ },
102
+ "stripe_report": {
103
+ "verdict": "write",
104
+ "reason": "May create durable report-run state, so retrieval paths cannot make the whole tool read-only.",
105
+ },
106
+ "send_stripe_mcp_feedback": {
107
+ "verdict": "write",
108
+ "reason": "Submits feedback to Stripe, creating provider-side state.",
109
+ },
110
+ },
111
+ };
112
+ /** Stripe key prefixes carry their own mode; only a clear reading counts. */
113
+ const LIVE_KEY = /\b(?:sk|rk|pk)_live_/;
114
+ const TEST_KEY = /\b(?:sk|rk|pk)_test_/;
115
+ /**
116
+ * Refuse a recognizable key/mode mismatch. This is the only mode guard Connecta
117
+ * can offer, and it needs the key as a literal header to read the prefix: an
118
+ * operator-managed key is not in the deployment file, so its declared mode
119
+ * stands alone and a key pointed at the other environment fails at Stripe.
120
+ */
121
+ function assertModeMatchesKey(id, mode, auth) {
122
+ if (auth.type !== "headers")
123
+ return;
124
+ for (const value of Object.values(auth.headers)) {
125
+ const keyMode = LIVE_KEY.test(value) ? "production" : TEST_KEY.test(value) ? "sandbox" : undefined;
126
+ if (keyMode !== undefined && keyMode !== mode) {
127
+ throw new Error(`stripe("${id}") declares mode "${mode}" but its auth headers carry a ` +
128
+ `${keyMode === "production" ? "live" : "test"}-mode Stripe key.`);
129
+ }
130
+ }
131
+ }
132
+ function resolveAuth(id, options) {
133
+ const auth = withCredentialDefaults(options.auth ?? { type: "oauth" }, {
134
+ credential: {
135
+ label: "Secret or restricted API key",
136
+ description: "A Stripe secret or restricted API key for this connector's declared mode. Stripe sends it as a bearer token; it is stored encrypted and never displayed.",
137
+ placeholder: "sk_… or rk_…",
138
+ },
139
+ });
140
+ const connectedAccount = options.connectedAccount?.trim();
141
+ if (connectedAccount === undefined || connectedAccount === "")
142
+ return auth;
143
+ if (!connectedAccount.startsWith("acct_")) {
144
+ throw new Error(`stripe("${id}") connectedAccount must be a Stripe account id ("acct_...").`);
145
+ }
146
+ if (auth.type === "oauth") {
147
+ throw new Error(`stripe("${id}") cannot reach a connected account over OAuth; Stripe ` +
148
+ `requires a restricted API key for Stripe-Account calls.`);
149
+ }
150
+ if (auth.type === "credential" || auth.type === "request") {
151
+ // `Stripe-Account` is a second header beside the credential's own, and the
152
+ // credential shape assembles exactly one. A Connect connector therefore
153
+ // still takes its restricted key as a literal header.
154
+ throw new Error(`stripe("${id}") cannot reach a connected account with an ` +
155
+ `operator-managed credential; Stripe-Account is a second static ` +
156
+ `header, so declare auth: { type: "headers" } for this connector.`);
157
+ }
158
+ return {
159
+ type: "headers",
160
+ headers: { ...auth.headers, "Stripe-Account": connectedAccount },
161
+ };
162
+ }
163
+ function oauthUsageGuide(purpose, instructions) {
164
+ const accountInstructions = instructions?.trim();
165
+ return `# Stripe usage
166
+
167
+ Scope: live and sandbox accounts. Connector purpose: ${purpose}${skill.fragments.guide_0}${sharedUsageGuide("100 requests per second in live mode and 25 in sandbox mode")}
168
+ ${accountInstructions ? `\n## ${skill.instructionsHeading}\n\n${accountInstructions}\n` : ""}`;
169
+ }
170
+ const MODE_COPY = {
171
+ production: {
172
+ title: "Stripe (production)",
173
+ blurb: "production — live money and real customers",
174
+ warning: "This is a PRODUCTION Stripe connection. Every write moves real money against real customers, and a refund cannot be undone. If a request could plausibly be a rehearsal, route it to a sandbox connector instead.",
175
+ },
176
+ sandbox: {
177
+ title: "Stripe (sandbox)",
178
+ blurb: "sandbox — test data, no real money",
179
+ warning: "This is a SANDBOX Stripe connection. Nothing here is real money and none of these objects exist in production, so never answer a question about live revenue, payouts, or a named customer from this connector.",
180
+ },
181
+ };
182
+ function fixedModeUsageGuide(mode, purpose, instructions) {
183
+ const copy = MODE_COPY[mode];
184
+ const accountInstructions = instructions?.trim();
185
+ return `# Stripe usage
186
+
187
+ Mode: ${mode}. Connector purpose: ${purpose}
188
+
189
+ ${copy.warning}${skill.fragments.guide_1}${sharedUsageGuide(`${mode === "production" ? "100" : "25"} requests per second`)}
190
+ ${accountInstructions ? `\n## ${skill.instructionsHeading}\n\n${accountInstructions}\n` : ""}`;
191
+ }
192
+ function sharedUsageGuide(rate) {
193
+ return `${skill.fragments.guide_2}${rate}${skill.fragments.guide_3}`;
194
+ }
195
+ /** The closed options stripe() accepts; see `assertKnownOptions`. */
196
+ const STRIPE_OPTIONS = optionsOf()({
197
+ ...keys("title", "authScope", "purpose", "instructions", "maxResultBytes", "mode", "connectedAccount"),
198
+ auth: variants("type", {
199
+ oauth: keys("type"),
200
+ headers: optionsOf()({ ...keys("type"), headers: strings() }).shape,
201
+ credential: optionsOf()({
202
+ ...keys("type", "header", "scheme"),
203
+ credential: CREDENTIAL,
204
+ }).shape,
205
+ }),
206
+ });
207
+ /** A maintained Stripe hosted-MCP connection. */
208
+ export const stripe = defineProvider({
209
+ name: "stripe",
210
+ title: "Stripe",
211
+ kind: "mcp",
212
+ readme: "Stripe",
213
+ bundle: { "baselineGzip": 129103, "maxGzip": 189103 },
214
+ skill,
215
+ options: STRIPE_OPTIONS,
216
+ classify: STRIPE_CLASSIFICATION,
217
+ create: stripeConnector,
218
+ });
219
+ function stripeConnector(id, options, provider) {
220
+ const purpose = options.purpose.trim();
221
+ const auth = resolveAuth(id, options);
222
+ const mode = "mode" in options ? options.mode : undefined;
223
+ if (auth.type === "oauth" && mode !== undefined) {
224
+ throw new Error(`stripe("${id}") cannot declare a connector-wide mode for OAuth; Stripe returns mode with each account.`);
225
+ }
226
+ if (auth.type !== "oauth" && mode !== "production" && mode !== "sandbox") {
227
+ throw new Error(`stripe("${id}") with headers or credential auth requires mode ` + `"production" or "sandbox".`);
228
+ }
229
+ // Only a literal header can be inspected. An operator-managed credential is
230
+ // not readable at construction — there is nothing in the deployment file to
231
+ // read — so the declared mode stands alone, and a key pointed at the other
232
+ // one is Stripe's own refusal to report.
233
+ if (auth.type === "headers") {
234
+ assertModeMatchesKey(id, mode, auth);
235
+ }
236
+ const copy = mode === undefined ? undefined : MODE_COPY[mode];
237
+ const connector = remoteMcp(id, {
238
+ url: STRIPE_MCP_ENDPOINT,
239
+ ...provider.connectorOptions,
240
+ title: options.title ?? copy?.title ?? "Stripe",
241
+ description: mode === undefined
242
+ ? `Stripe payments (live and sandbox accounts) — ${purpose}`
243
+ : `Stripe payments (${copy?.blurb}) — ${purpose}`,
244
+ auth,
245
+ requireHttps: true,
246
+ classify: provider.classify,
247
+ callAdmission: STRIPE_ADMISSION[mode ?? "sandbox"],
248
+ usageGuide: {
249
+ content: mode === undefined
250
+ ? oauthUsageGuide(purpose, options.instructions)
251
+ : fixedModeUsageGuide(mode, purpose, options.instructions),
252
+ // Explicit rather than derived: fixed credentials must lead with mode,
253
+ // while OAuth must lead with its account-scoped selector pair.
254
+ summary: mode === undefined
255
+ ? "Live and sandbox Stripe accounts. List accounts; carry the returned stripe_context and livemode before acting."
256
+ : mode === "production"
257
+ ? "PRODUCTION: real money. This static credential has one fixed live-mode scope."
258
+ : "Sandbox: test data only. This static credential has one fixed sandbox scope.",
259
+ // Not `required`. The four generic tools are the routing decision; a
260
+ // guide forced into every call would pay for the same prose repeatedly.
261
+ },
262
+ });
263
+ return connector;
264
+ }
265
+ /** @deprecated Read `stripe.definition.classify` instead. Kept for existing imports. */
266
+ export const STRIPE_VETTED_CATALOG = reviewedCatalog(stripe.definition.classify, 'defineProvider("stripe")');
@@ -0,0 +1,11 @@
1
+ // Generated by scripts/generate-providers.mjs; do not edit.
2
+ export const skill = {
3
+ "content": "\n\nThis OAuth session may expose both live and sandbox Stripe accounts. Discover the live account-selection tools, select the intended account and mode, then carry exact context fields such as `stripe_context` and `livemode` wherever the schema requires them. A live-mode write moves real money; a sandbox write changes test data. Never infer the account or mode from connector metadata.\n\n- Discover account and mode with the live account-selection tools before every account-scoped read or write. The published inventory has changed account-tool names; do not assume `list_available_accounts_or_orgs` is available. Select the intended result and carry the exact context fields required by the live schema unchanged. If the account, mode, or supported selector is ambiguous, stop and ask; never guess.\n- Organization accounts are not Stripe Connect connected accounts. A Connect call requires a separate connector with a deployment-configured restricted key plus Stripe's documented `Stripe-Account` header; OAuth does not support that path.\n\n\n- Organization accounts are not Stripe Connect connected accounts. A Connect call requires a deployment-configured restricted key plus Stripe's documented `Stripe-Account` header; OAuth does not support that path. Do not try to turn an organization-account call into a Connect call inside tool arguments.\n\n- Four generic tools reach any Stripe API method. Find the method with `stripe_api_search`, read its parameters with `stripe_api_details`, then call `stripe_api_read` (GET) or `stripe_api_write` (POST/PATCH/PUT/DELETE). Never guess a path or a parameter name — `stripe_api_details` is cheaper than a rejected write.\n- Prefer a dedicated tool when one covers the task: `get_balance_summary` for balances and `stripe_analytics` for Sigma or Metrics reporting. Use `stripe_api_search` for everything else instead of assuming a retired dedicated tool still exists.\n- `stripe_implementation_planner` and the query-execution intents of `stripe_analytics` create provider-side planning or query-run state. Connecta therefore classifies both as writes and enforces the configured pool trust policy; their retrieval paths stay behind the same tool boundary.\n- `stripe_api_write` carries the blast radius of the entire write API — every POST, PATCH, PUT, and DELETE, from a customer edit to a subscription cancellation. State the method and path explicitly; execution follows the configured pool trust policy.\n- Lists are cursor-paginated: `limit` defaults to 10 and caps at 100, `starting_after` and `ending_before` take an object id and are mutually exclusive, and `has_more` says whether to continue. Page inside `execute_code`.\n- Any `stripe_api_read` list or `stripe_api_search` that returns full objects belongs inside `execute_code`, projected to the fields the question needs before `return`. Neither `limit` nor `expand` substitutes for that: an unprojected list of customers or invoices truncates long before it answers, and a projected one keeps the customer's name, email, and address out of the transcript.\n- Search filters on a documented per-resource field set, not on arbitrary attributes. Charges search takes `amount`, `created`, `currency`, `customer`, `status`, `refunded`, `disputed`, `metadata`, `billing_details.address.postal_code`, and `payment_method_details.<source>.*` card fields — there is no `payment_intent` field. When the field you want is not searchable, retrieve the parent object and follow its reference (the PaymentIntent's `latest_charge`) instead of retrying the search with another spelling.\n- The account → `stripe_api_search` → `stripe_api_details` → `stripe_api_read` sequence is one program, not four turns: resolve the method once, then call that method as many times as the investigation needs in the same run.\n- Decline outcomes live on the charge — `outcome`, `failure_code`, `failure_message` — reached from the PaymentIntent's `latest_charge`, so \"why did this payment fail\" is a PaymentIntent read followed by one charge read.\n- Resolve ids before acting; never guess one. Stripe ids are typed prefixes — `cus_` customer, `sub_` subscription, `ch_` charge, `pi_` payment intent, `in_` invoice, `acct_` account — and a plausible-looking id belongs to a different object or to nobody. Find the object with `stripe_api_search` (or a list endpoint through `stripe_api_read`) and carry the `id` it returned into the write.\n- This connection's tool list is not a fixed set. Stripe gates parts of its MCP catalog by account, integration, and beta enrollment, so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- Amounts are integers in the currency's minor unit: `1099` is 10.99 USD, and zero-decimal currencies like JPY take `10` for 10 JPY. Never send a decimal.\n- Send an `Idempotency-Key` on every write you might retry, if the tool accepts it, and reuse the same key for the retry. A retry with a fresh key is a second charge, not a second attempt.\n- Stripe answers a rate limit with `429` and a `Stripe-Rate-Limited-Reason` header; back off on that rather than retrying immediately. Stripe documents an account ceiling of , and any single endpoint is capped at 25 per second regardless of mode, so paging one list is the real constraint.\n- Use `search_stripe_documentation` when the shape of an object or a flow is unclear; it is a read and costs nothing but a call.\n- Treat every create, update, delete, refund, and report run as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n- An `auth_required` failure means this connector's Stripe authorization is missing or expired: run `authorize_connector` for this connector id, then retry the same call unchanged. A rejected argument or a plan restriction comes back in Stripe's own words instead — read it rather than re-authorizing.\n",
4
+ "instructionsHeading": "Account instructions",
5
+ "fragments": {
6
+ "guide_0": "\n\nThis OAuth session may expose both live and sandbox Stripe accounts. Discover the live account-selection tools, select the intended account and mode, then carry exact context fields such as `stripe_context` and `livemode` wherever the schema requires them. A live-mode write moves real money; a sandbox write changes test data. Never infer the account or mode from connector metadata.\n\n- Discover account and mode with the live account-selection tools before every account-scoped read or write. The published inventory has changed account-tool names; do not assume `list_available_accounts_or_orgs` is available. Select the intended result and carry the exact context fields required by the live schema unchanged. If the account, mode, or supported selector is ambiguous, stop and ask; never guess.\n- Organization accounts are not Stripe Connect connected accounts. A Connect call requires a separate connector with a deployment-configured restricted key plus Stripe's documented `Stripe-Account` header; OAuth does not support that path.\n",
7
+ "guide_1": "\n\n- Organization accounts are not Stripe Connect connected accounts. A Connect call requires a deployment-configured restricted key plus Stripe's documented `Stripe-Account` header; OAuth does not support that path. Do not try to turn an organization-account call into a Connect call inside tool arguments.\n",
8
+ "guide_2": "\n- Four generic tools reach any Stripe API method. Find the method with `stripe_api_search`, read its parameters with `stripe_api_details`, then call `stripe_api_read` (GET) or `stripe_api_write` (POST/PATCH/PUT/DELETE). Never guess a path or a parameter name — `stripe_api_details` is cheaper than a rejected write.\n- Prefer a dedicated tool when one covers the task: `get_balance_summary` for balances and `stripe_analytics` for Sigma or Metrics reporting. Use `stripe_api_search` for everything else instead of assuming a retired dedicated tool still exists.\n- `stripe_implementation_planner` and the query-execution intents of `stripe_analytics` create provider-side planning or query-run state. Connecta therefore classifies both as writes and enforces the configured pool trust policy; their retrieval paths stay behind the same tool boundary.\n- `stripe_api_write` carries the blast radius of the entire write API — every POST, PATCH, PUT, and DELETE, from a customer edit to a subscription cancellation. State the method and path explicitly; execution follows the configured pool trust policy.\n- Lists are cursor-paginated: `limit` defaults to 10 and caps at 100, `starting_after` and `ending_before` take an object id and are mutually exclusive, and `has_more` says whether to continue. Page inside `execute_code`.\n- Any `stripe_api_read` list or `stripe_api_search` that returns full objects belongs inside `execute_code`, projected to the fields the question needs before `return`. Neither `limit` nor `expand` substitutes for that: an unprojected list of customers or invoices truncates long before it answers, and a projected one keeps the customer's name, email, and address out of the transcript.\n- Search filters on a documented per-resource field set, not on arbitrary attributes. Charges search takes `amount`, `created`, `currency`, `customer`, `status`, `refunded`, `disputed`, `metadata`, `billing_details.address.postal_code`, and `payment_method_details.<source>.*` card fields — there is no `payment_intent` field. When the field you want is not searchable, retrieve the parent object and follow its reference (the PaymentIntent's `latest_charge`) instead of retrying the search with another spelling.\n- The account → `stripe_api_search` → `stripe_api_details` → `stripe_api_read` sequence is one program, not four turns: resolve the method once, then call that method as many times as the investigation needs in the same run.\n- Decline outcomes live on the charge — `outcome`, `failure_code`, `failure_message` — reached from the PaymentIntent's `latest_charge`, so \"why did this payment fail\" is a PaymentIntent read followed by one charge read.\n- Resolve ids before acting; never guess one. Stripe ids are typed prefixes — `cus_` customer, `sub_` subscription, `ch_` charge, `pi_` payment intent, `in_` invoice, `acct_` account — and a plausible-looking id belongs to a different object or to nobody. Find the object with `stripe_api_search` (or a list endpoint through `stripe_api_read`) and carry the `id` it returned into the write.\n- This connection's tool list is not a fixed set. Stripe gates parts of its MCP catalog by account, integration, and beta enrollment, so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- Amounts are integers in the currency's minor unit: `1099` is 10.99 USD, and zero-decimal currencies like JPY take `10` for 10 JPY. Never send a decimal.\n- Send an `Idempotency-Key` on every write you might retry, if the tool accepts it, and reuse the same key for the retry. A retry with a fresh key is a second charge, not a second attempt.\n- Stripe answers a rate limit with `429` and a `Stripe-Rate-Limited-Reason` header; back off on that rather than retrying immediately. Stripe documents an account ceiling of ",
9
+ "guide_3": ", and any single endpoint is capped at 25 per second regardless of mode, so paging one list is the real constraint.\n- Use `search_stripe_documentation` when the shape of an object or a flow is unclear; it is a read and costs nothing but a call.\n- Treat every create, update, delete, refund, and report run as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n- An `auth_required` failure means this connector's Stripe authorization is missing or expired: run `authorize_connector` for this connector id, then retry the same call unchanged. A rejected argument or a plan restriction comes back in Stripe's own words instead — read it rather than re-authorizing.\n"
10
+ }
11
+ };
@@ -1,4 +1,4 @@
1
- import type { Connector, ConnectorCallAdmissionPolicy } from "../types.js";
1
+ import type { ConnectorCallAdmissionPolicy } from "../../types.js";
2
2
  /** Tithe.ly's two published v1 endpoints, one per environment. */
3
3
  export declare const TITHELY_API_BASE_URLS: {
4
4
  readonly live: "https://tithe.ly/api/v1";
@@ -46,4 +46,4 @@ export interface TithelyOptions {
46
46
  baseUrl?: string;
47
47
  }
48
48
  /** A maintained Tithe.ly giving connection over the v1 REST API. */
49
- export declare function tithely(id: string, options: TithelyOptions): Connector;
49
+ export declare const tithely: import("../../provider.js").ProviderFactory<TithelyOptions>;
@@ -1,8 +1,9 @@
1
+ import { skill } from "./skill.generated.js";
1
2
  /**
2
3
  * Tithe.ly's giving API (v1), hand-written against the per-operation reference
3
4
  * at https://docs.tithe.ly/reference/introduction. No SDK exists to depend on,
4
5
  * and none would be welcome: this is a payments API, and every tool below was
5
- * chosen and written, not generated (ethos: OpenAPI ingestion is refused).
6
+ * chosen and written, not generated (decision record 0001: hand-authored tools).
6
7
  *
7
8
  * Facts the code leans on, each from the reference unless marked otherwise:
8
9
  *
@@ -44,13 +45,16 @@
44
45
  * Drift: there is no combined specification, but each reference page served
45
46
  * as `.md` embeds a one-operation OpenAPI 3.1 snippet. `scripts/drift-check.mjs`
46
47
  * assembles those snippets for the pages listed in
47
- * `scripts/drift/tithely-endpoints.json` and digests each operation like any
48
+ * `src/providers/tithely/drift.json` and digests each operation like any
48
49
  * other provider's, so `npm run providers:check -- --provider tithely` runs
49
50
  * credential-free.
50
51
  */
51
- import { apiConnector as api, defined } from "../connectors/api-connector.js";
52
- import { guardedFetch, retryAfterMs, } from "../connectors/guarded-fetch.js";
53
- import { ConnectorCallError } from "../errors.js";
52
+ import { apiConnector as api, defined } from "../../connectors/api-connector.js";
53
+ import { guardedFetch, retryAfterMs, } from "../../connectors/guarded-fetch.js";
54
+ import { ConnectorCallError } from "../../errors.js";
55
+ import { keys, optionsOf } from "../../config-schema.js";
56
+ import { PROVIDER_COMMON } from "../../connectors/option-shapes.js";
57
+ import { asProviderFactory } from "../../provider.js";
54
58
  /** Tithe.ly's two published v1 endpoints, one per environment. */
55
59
  export const TITHELY_API_BASE_URLS = {
56
60
  live: "https://tithe.ly/api/v1",
@@ -68,9 +72,7 @@ const DEFAULT_PAGE_SIZE = 25;
68
72
  /** A full list page of fully embedded organizations stays far below this. */
69
73
  const TITHELY_MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
70
74
  function asRecord(value) {
71
- return value && typeof value === "object" && !Array.isArray(value)
72
- ? value
73
- : {};
75
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
74
76
  }
75
77
  function asArray(value) {
76
78
  return Array.isArray(value) ? value : [];
@@ -140,10 +142,7 @@ const operatorKeyPair = async (ctx) => {
140
142
  function detailFor(payload, status) {
141
143
  const root = asRecord(payload);
142
144
  const error = root["error"];
143
- const message = text(root["reason"]) ??
144
- text(root["message"]) ??
145
- text(error) ??
146
- text(asRecord(error)["message"]);
145
+ const message = text(root["reason"]) ?? text(root["message"]) ?? text(error) ?? text(asRecord(error)["message"]);
147
146
  return message ? `Tithe.ly: ${message.trim()}` : `Tithe.ly returned HTTP ${status}.`;
148
147
  }
149
148
  /**
@@ -161,7 +160,7 @@ function tithelyFailure(status, headers, payload) {
161
160
  return new ConnectorCallError("auth_required", `${detail} Tithe.ly rejected the key pair. It may be revoked, mistyped, or issued for the other environment — test keys work only on tithelydev.com, live keys only on tithe.ly. An operator must fix the keys on this connection.`);
162
161
  }
163
162
  if (status === 403) {
164
- return new ConnectorCallError("connector_call_failed", `${detail} Tithe.ly refused the request: the key pair may not reach this resource, or API access may not be enabled for this environment. Tithe.ly does not say which.`, { retryable: false });
163
+ return new ConnectorCallError("provider_permission_denied", `${detail} Tithe.ly refused the request: the key pair may not reach this resource, or API access may not be enabled for this environment. Tithe.ly does not say which. Ask the account administrator to grant API access to this resource and environment.`, { retryable: false });
165
164
  }
166
165
  if (status === 404) {
167
166
  // Not `not_found`: nothing documents whether an id outside this key's
@@ -275,7 +274,7 @@ function projectFunds(organization) {
275
274
  return compact({
276
275
  id: fund["id"] === undefined ? undefined : String(fund["id"]),
277
276
  name: String(fund["name"] ?? ""),
278
- status: status === undefined ? undefined : FUND_STATUS[status] ?? status,
277
+ status: status === undefined ? undefined : (FUND_STATUS[status] ?? status),
279
278
  });
280
279
  });
281
280
  }
@@ -691,7 +690,8 @@ function tools(send, defaultPageSize) {
691
690
  inputSchema: namedInput({ path: PATH_PROPERTY, query: QUERY_PROPERTY }, ["path"]),
692
691
  outputSchema: UNTOUCHED_RESULT,
693
692
  handler: async (args, ctx) => ({
694
- result: (await callTithely(send, { method: "GET", path: String(args["path"]), query: pairs(args["query"]) }, ctx)) ?? null,
693
+ result: (await callTithely(send, { method: "GET", path: String(args["path"]), query: pairs(args["query"]) }, ctx)) ??
694
+ null,
695
695
  }),
696
696
  },
697
697
  {
@@ -789,8 +789,14 @@ function tools(send, defaultPageSize) {
789
789
  description: "List gifts (charges) to one organization or from one donor, optionally within a created-date range, with gross, net, and fees in cents.",
790
790
  annotations: readOnly,
791
791
  inputSchema: namedInput({
792
- organizationId: { ...ORGANIZATION_ID_PROPERTY, description: "Organization (org_…) the gifts went to. This or accountId is required." },
793
- accountId: { ...ACCOUNT_ID_PROPERTY, description: "Donor account (user_…) that gave. This or organizationId is required." },
792
+ organizationId: {
793
+ ...ORGANIZATION_ID_PROPERTY,
794
+ description: "Organization (org_…) the gifts went to. This or accountId is required.",
795
+ },
796
+ accountId: {
797
+ ...ACCOUNT_ID_PROPERTY,
798
+ description: "Donor account (user_…) that gave. This or organizationId is required.",
799
+ },
794
800
  createdAfter: {
795
801
  type: ["integer", "string"],
796
802
  minimum: 0,
@@ -815,7 +821,11 @@ function tools(send, defaultPageSize) {
815
821
  if (after !== undefined && before !== undefined && after > before) {
816
822
  throw new ConnectorCallError("invalid_args", "createdAfter is later than createdBefore, so no charge can match.");
817
823
  }
818
- const { rows, page } = await listPage(send, ctx, "/charges-list", "charge_id", args, defaultPageSize, { ...scopeFilters(args, "charges"), created_after: after, created_before: before });
824
+ const { rows, page } = await listPage(send, ctx, "/charges-list", "charge_id", args, defaultPageSize, {
825
+ ...scopeFilters(args, "charges"),
826
+ created_after: after,
827
+ created_before: before,
828
+ });
819
829
  return { charges: raw(args) ? rows : rows.map(projectCharge), page };
820
830
  },
821
831
  },
@@ -839,8 +849,14 @@ function tools(send, defaultPageSize) {
839
849
  description: "List recurring gifts for one organization or one donor: amount, fund, term, and start date. Does not list the charges they produced.",
840
850
  annotations: readOnly,
841
851
  inputSchema: namedInput({
842
- organizationId: { ...ORGANIZATION_ID_PROPERTY, description: "Organization (org_…) receiving the gifts. This or accountId is required." },
843
- accountId: { ...ACCOUNT_ID_PROPERTY, description: "Donor account (user_…) giving. This or organizationId is required." },
852
+ organizationId: {
853
+ ...ORGANIZATION_ID_PROPERTY,
854
+ description: "Organization (org_…) receiving the gifts. This or accountId is required.",
855
+ },
856
+ accountId: {
857
+ ...ACCOUNT_ID_PROPERTY,
858
+ description: "Donor account (user_…) giving. This or organizationId is required.",
859
+ },
844
860
  limit,
845
861
  cursor: CURSOR_PROPERTY,
846
862
  order: ORDER_PROPERTY,
@@ -857,7 +873,11 @@ function tools(send, defaultPageSize) {
857
873
  description: "Get one recurring gift by id: amount in cents, fund, term, start date, organization, and donor name and email.",
858
874
  annotations: readOnly,
859
875
  inputSchema: namedInput({
860
- recurringId: { type: "string", minLength: 1, description: "Recurring gift id (rc_…) from list_recurring_charges." },
876
+ recurringId: {
877
+ type: "string",
878
+ minLength: 1,
879
+ description: "Recurring gift id (rc_…) from list_recurring_charges.",
880
+ },
861
881
  raw: RAW_PROPERTY,
862
882
  }, ["recurringId"]),
863
883
  outputSchema: RECURRING_SCHEMA,
@@ -895,71 +915,7 @@ function usageGuide(purpose, environment, instructions) {
895
915
 
896
916
  ${where}
897
917
 
898
- Account purpose: ${purpose}
899
-
900
- ## Giving data is sensitive
901
-
902
- - Donor names, emails, addresses, and gift amounts are pastoral data. Reduce
903
- inside \`execute_code\`: page, filter, and total there, and return the
904
- aggregate (sums by fund, month, or donor count), not donor rows, unless the
905
- task needs named people.
906
- - Organization projections omit the payout bank's last four and the legal
907
- contact's date of birth. \`raw: true\` and \`tithely_api_get\` return them;
908
- do not reach for either to answer a giving question.
909
-
910
- ## Ids and vocabulary
911
-
912
- - Ids carry their kind: \`org_\` organization, \`user_\` donor account,
913
- \`ch_\` charge, \`rc_\` recurring gift, \`pm_\` payment method. Read them
914
- from \`list_organizations\` and \`list_accounts\`; never guess one.
915
- - A fund is a Tithe.ly giving type: \`giving_type\` becomes \`fund\`.
916
- - Projections rename snake_case to camelCase, return amounts as integer
917
- \`…Cents\`, and Unix-second dates as ISO 8601 UTC \`…At\`.
918
- \`depositedAt\` may be \`"pending"\`. There is no deposits resource: net
919
- amount and fees live on each charge.
920
-
921
- ## Paging
922
-
923
- List tools return \`page.hasMore\` and \`page.nextCursor\`; pass the cursor
924
- back unchanged with the same \`order\`. \`list_charges\` and
925
- \`list_recurring_charges\` need \`organizationId\` or \`accountId\`.
926
- \`list_payment_methods\` is not paged.
927
-
928
- ## Failures
929
-
930
- Tithe.ly answers most refusals with HTTP 200, \`status: "fail"\`, and a
931
- reason but no code. They arrive as \`connector_call_failed\`; read the reason
932
- rather than retrying blind.
933
-
934
- ## Writes go through the mutate hatch
935
-
936
- Every Tithe.ly write moves money or changes a donor's payment state, so none
937
- has a named tool. \`tithely_api_mutate\` sends form fields as name/value pairs
938
- and always needs approval. Amounts are integer cents; card and bank tokens
939
- come only from Tithely.js in a browser, never from an agent.
940
-
941
- - \`POST /charges\`: account_id, pm_id, organization_id, amount,
942
- giving_type, optional memo. \`POST /charge-once\` charges a Tithely.js token
943
- without an account.
944
- - \`POST /refunds/{charge_id}\` refunds a charge.
945
- - \`POST /recurring\`: account_id, pm_id, organization_id, amount,
946
- giving_type, term (weekly, monthly, bimonthly, fortnightly), start_date
947
- (Unix seconds). \`DELETE /recurring/{recurring_id}\` stops future charges and
948
- refunds nothing.
949
- - \`POST /payment-methods\` (account_id, token), \`POST
950
- /payment-methods/{pm_id}\` (account_id plus fields to change), \`DELETE
951
- /payment-methods/{pm_id}\` with query account_id.
952
- - \`POST /accounts\` and \`POST /accounts/{account_id}\` create and update
953
- donor accounts.
954
-
955
- ## Limits
956
-
957
- Tithe.ly publishes no rate limit, so this connection declares no call budget.
958
- An operator who knows one sets \`callAdmission\` on the connection. Only v1 is
959
- reachable; Tithe.ly's undocumented v2 is not.
960
- ${accountInstructions
961
- ? `\n## Account instructions\n\n${accountInstructions}\n`
962
- : ""}`;
918
+ Account purpose: ${purpose}${skill.fragments.guide_0}${accountInstructions ? `\n## ${skill.instructionsHeading}\n\n${accountInstructions}\n` : ""}`;
963
919
  }
964
920
  // --- Construction -----------------------------------------------------------------
965
921
  async function testKeyPair(send, environment, values, ctx) {
@@ -971,7 +927,7 @@ async function testKeyPair(send, environment, values, ctx) {
971
927
  const payload = await callTithely(send, { method: "GET", path: "/organizations-list", query: { limit: 1 } }, {
972
928
  ...ctx,
973
929
  credential: {
974
- get: async (field) => (field ? values[field] ?? null : null),
930
+ get: async (field) => (field ? (values[field] ?? null) : null),
975
931
  getAll: async () => values,
976
932
  },
977
933
  });
@@ -992,8 +948,23 @@ async function testKeyPair(send, environment, values, ctx) {
992
948
  };
993
949
  }
994
950
  }
951
+ /** The closed options tithely() accepts; see `assertKnownOptions`. */
952
+ const TITHELY_OPTIONS = optionsOf()({
953
+ ...PROVIDER_COMMON,
954
+ ...keys("environment", "defaultPageSize", "baseUrl"),
955
+ });
995
956
  /** A maintained Tithe.ly giving connection over the v1 REST API. */
996
- export function tithely(id, options) {
957
+ export const tithely = asProviderFactory({
958
+ name: "tithely",
959
+ title: "Tithe.ly",
960
+ kind: "api",
961
+ readme: "Tithe.ly",
962
+ bundle: { "baselineGzip": 18076, "maxGzip": 78076 },
963
+ skill,
964
+ options: TITHELY_OPTIONS,
965
+ create: tithelyConnector,
966
+ });
967
+ function tithelyConnector(id, options) {
997
968
  const purpose = options.purpose?.trim();
998
969
  if (!purpose) {
999
970
  throw new Error("tithely() requires a non-empty account purpose.");
@@ -1003,9 +974,7 @@ export function tithely(id, options) {
1003
974
  throw new Error('tithely() requires environment: "live" (tithe.ly) or "test" (tithelydev.com). Tithe.ly keys are environment-specific and unrecognizable, so connecta will not guess.');
1004
975
  }
1005
976
  const defaultPageSize = options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
1006
- if (!Number.isInteger(defaultPageSize) ||
1007
- defaultPageSize < 1 ||
1008
- defaultPageSize > MAX_PAGE_SIZE) {
977
+ if (!Number.isInteger(defaultPageSize) || defaultPageSize < 1 || defaultPageSize > MAX_PAGE_SIZE) {
1009
978
  throw new Error(`tithely() defaultPageSize must be a whole number between 1 and ${MAX_PAGE_SIZE}.`);
1010
979
  }
1011
980
  const send = tithelyTransport(options.baseUrl?.trim() || TITHELY_API_BASE_URLS[environment], operatorKeyPair);