@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.
- package/CHANGELOG.md +788 -5
- package/README.md +62 -42
- package/bin/connecta.mjs +110 -139
- package/bin/meta-tool-names.d.mts +2 -0
- package/bin/meta-tool-names.mjs +9 -0
- package/bin/version.mjs +3 -0
- package/dist/access-tokens.js +53 -63
- package/dist/activity-request.js +8 -0
- package/dist/activity.d.ts +76 -42
- package/dist/activity.js +110 -42
- package/dist/auth/clerk-sdk/client.d.ts +65 -0
- package/dist/auth/clerk-sdk/client.js +5 -0
- package/dist/auth/clerk-sdk/generated.js +1845 -0
- package/dist/auth/clerk-transport.js +19 -0
- package/dist/auth/clerk.d.ts +17 -7
- package/dist/auth/clerk.js +189 -107
- package/dist/auth/cloudflare-access.js +25 -26
- package/dist/auth/downstream-client-metadata.d.ts +29 -0
- package/dist/auth/downstream-client-metadata.js +133 -0
- package/dist/auth/downstream-oauth.js +1584 -1580
- package/dist/auth/downstream-revocation.js +106 -0
- package/dist/auth/oauth-token-response.js +231 -0
- package/dist/auth/oauth-v2-migration.js +116 -0
- package/dist/auth/static-oauth.js +93 -71
- package/dist/auth-elicitation.js +283 -0
- package/dist/branding.d.ts +8 -16
- package/dist/branding.js +26 -59
- package/dist/byte-read-response.js +39 -0
- package/dist/call-admission.js +12 -16
- package/dist/catalog-cache.js +739 -0
- package/dist/catalog-drift.d.ts +50 -15
- package/dist/catalog-drift.js +365 -128
- package/dist/catalog-fingerprint.js +2 -4
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-limits.js +2 -2
- package/dist/catalog-service.js +368 -224
- package/dist/catalog.js +43 -84
- package/dist/concurrency.js +3 -4
- package/dist/config-defaults.d.ts +39 -0
- package/dist/config-defaults.js +46 -0
- package/dist/config-schema.d.ts +181 -0
- package/dist/config-schema.js +435 -0
- package/dist/config-value-sources.js +102 -0
- package/dist/config.d.ts +631 -0
- package/dist/config.js +503 -0
- package/dist/connector-access.d.ts +1 -0
- package/dist/connector-access.js +13 -15
- package/dist/connector-caller.d.ts +8 -0
- package/dist/connector-caller.js +15 -6
- package/dist/connector-scope.js +1 -1
- package/dist/connectors/api-connector.d.ts +59 -7
- package/dist/connectors/api-connector.js +106 -19
- package/dist/connectors/api.js +4 -1
- package/dist/connectors/guarded-fetch.d.ts +144 -0
- package/dist/connectors/guarded-fetch.js +84 -35
- package/dist/connectors/negotiation-cache.js +46 -0
- package/dist/connectors/option-shapes.js +27 -0
- package/dist/connectors/remote-mcp.d.ts +30 -4
- package/dist/connectors/remote-mcp.js +1774 -320
- package/dist/connectors/resource-uri.js +335 -0
- package/dist/credential-contract.d.ts +10 -0
- package/dist/credential-rules.js +1 -3
- package/dist/credential-url.js +108 -0
- package/dist/credentials.d.ts +6 -1
- package/dist/credentials.js +27 -18
- package/dist/d1.d.ts +130 -0
- package/dist/d1.js +290 -0
- package/dist/describe-config.d.ts +165 -0
- package/dist/describe-config.js +315 -0
- package/dist/described.js +106 -0
- package/dist/downstream-input-context.js +119 -0
- package/dist/downstream-input.js +323 -0
- package/dist/errors.d.ts +97 -20
- package/dist/errors.js +158 -80
- package/dist/execute.js +453 -305
- package/dist/executor-admission.d.ts +1 -0
- package/dist/executor-admission.js +11 -15
- package/dist/executor-contract.d.ts +20 -0
- package/dist/executor-contract.js +48 -0
- package/dist/executor-result.js +24 -9
- package/dist/executors/quickjs-child.js +3 -4
- package/dist/executors/quickjs-runtime.js +147 -59
- package/dist/executors/quickjs.js +29 -35
- package/dist/favicon.js +1 -2
- package/dist/fix-prompt.js +1 -3
- package/dist/guest-runtime.js +188 -0
- package/dist/guest-types.d.ts +176 -0
- package/dist/guest-types.js +1 -0
- package/dist/html-security.js +24 -0
- package/dist/identity.js +1 -3
- package/dist/inbound-credential.js +52 -0
- package/dist/index.d.ts +24 -256
- package/dist/index.js +65 -478
- package/dist/invocation-auth.js +69 -0
- package/dist/invocation.js +230 -73
- package/dist/json-schema.d.ts +1 -1
- package/dist/mcp-client-context.js +35 -0
- package/dist/mcp-result.js +11 -7
- package/dist/meta-output.js +76 -0
- package/dist/meta-tool-names.d.ts +2 -0
- package/dist/meta-tool-names.js +3 -0
- package/dist/meta-tools.js +285 -209
- package/dist/module-contracts.d.ts +8 -23
- package/dist/node.d.ts +0 -2
- package/dist/node.js +2 -2
- package/dist/oauth-callback-outcome.js +1 -3
- package/dist/oauth-handoff.js +117 -0
- package/dist/oauth-sealing.js +20 -0
- package/dist/operator-record.d.ts +137 -0
- package/dist/operator-record.js +420 -0
- package/dist/operator-ui/assets.js +31 -0
- package/dist/operator-ui/contract.d.ts +72 -0
- package/dist/operator-ui/contract.js +1 -0
- package/dist/operator-ui/fix-prompts.d.ts +18 -0
- package/dist/operator-ui/generated.js +5 -4
- package/dist/operator-ui/model.d.ts +12 -87
- package/dist/operator-ui/model.js +2 -9
- package/dist/operator-ui/setup-commands.js +1 -3
- package/dist/operator-ui/view.d.ts +323 -0
- package/dist/operator-ui/view.js +62 -189
- package/dist/page-styles.js +2 -3
- package/dist/program-source.js +14 -2
- package/dist/{exempt-writes.js → program-writes.js} +55 -18
- package/dist/provider.d.ts +142 -0
- package/dist/provider.js +187 -0
- package/dist/providers/_shared/google/result-size.js +57 -0
- package/dist/providers/_shared/google/service-account.d.ts +58 -0
- package/dist/providers/_shared/google/service-account.js +500 -0
- package/dist/providers/_shared/google/workspace.d.ts +276 -0
- package/dist/providers/_shared/google/workspace.js +518 -0
- package/dist/providers/{basecamp.d.ts → basecamp/index.d.ts} +6 -28
- package/dist/providers/basecamp/index.js +997 -0
- package/dist/providers/basecamp/skill.generated.js +9 -0
- package/dist/providers/{breeze.d.ts → breeze/index.d.ts} +2 -2
- package/dist/providers/{breeze.js → breeze/index.js} +184 -140
- package/dist/providers/breeze/skill.generated.js +9 -0
- package/dist/providers/{ccb.d.ts → ccb/index.d.ts} +2 -2
- package/dist/providers/{ccb.js → ccb/index.js} +271 -116
- package/dist/providers/ccb/skill.generated.js +9 -0
- package/dist/providers/{cloudflare.d.ts → cloudflare/index.d.ts} +8 -7
- package/dist/providers/{cloudflare.js → cloudflare/index.js} +171 -738
- package/dist/providers/cloudflare/skill.generated.js +10 -0
- package/dist/providers/docs/index.d.ts +54 -0
- package/dist/providers/docs/index.js +1237 -0
- package/dist/providers/docs/skill.generated.js +9 -0
- package/dist/providers/drive/index.d.ts +53 -0
- package/dist/providers/drive/index.js +1523 -0
- package/dist/providers/drive/skill.generated.js +9 -0
- package/dist/providers/forms/index.d.ts +54 -0
- package/dist/providers/forms/index.js +1217 -0
- package/dist/providers/forms/skill.generated.js +11 -0
- package/dist/providers/github/auth.d.ts +32 -0
- package/dist/providers/github/auth.js +361 -0
- package/dist/providers/github/hosted.js +218 -0
- package/dist/providers/github/index.d.ts +13 -0
- package/dist/providers/github/index.js +140 -0
- package/dist/providers/github/key.js +100 -0
- package/dist/providers/github/rest.js +321 -0
- package/dist/providers/github/scope.d.ts +29 -0
- package/dist/providers/github/scope.js +124 -0
- package/dist/providers/github/skill.generated.js +8 -0
- package/dist/providers/gmail/index.d.ts +51 -0
- package/dist/providers/gmail/index.js +1436 -0
- package/dist/providers/gmail/skill.generated.js +9 -0
- package/dist/providers/{linear.d.ts → linear/index.d.ts} +11 -14
- package/dist/providers/linear/index.js +352 -0
- package/dist/providers/linear/skill.generated.js +8 -0
- package/dist/providers/{mixpanel.d.ts → mixpanel/index.d.ts} +5 -10
- package/dist/providers/mixpanel/index.js +416 -0
- package/dist/providers/mixpanel/skill.generated.js +9 -0
- package/dist/providers/{notion.d.ts → notion/index.d.ts} +8 -8
- package/dist/providers/{notion.js → notion/index.js} +226 -239
- package/dist/providers/notion/skill.generated.js +10 -0
- package/dist/providers/{overflow.d.ts → overflow/index.d.ts} +2 -2
- package/dist/providers/{overflow.js → overflow/index.js} +74 -111
- package/dist/providers/overflow/skill.generated.js +8 -0
- package/dist/providers/{planning-center.d.ts → planning-center/index.d.ts} +2 -2
- package/dist/providers/{planning-center.js → planning-center/index.js} +264 -104
- package/dist/providers/planning-center/skill.generated.js +10 -0
- package/dist/providers/{revenuecat.d.ts → revenuecat/index.d.ts} +5 -10
- package/dist/providers/revenuecat/index.js +557 -0
- package/dist/providers/revenuecat/skill.generated.js +11 -0
- package/dist/providers/sheets/index.d.ts +54 -0
- package/dist/providers/sheets/index.js +1237 -0
- package/dist/providers/sheets/skill.generated.js +10 -0
- package/dist/providers/slides/index.d.ts +53 -0
- package/dist/providers/slides/index.js +2645 -0
- package/dist/providers/slides/skill.generated.js +9 -0
- package/dist/providers/{stripe.d.ts → stripe/index.d.ts} +5 -12
- package/dist/providers/stripe/index.js +266 -0
- package/dist/providers/stripe/skill.generated.js +11 -0
- package/dist/providers/{tithely.d.ts → tithely/index.d.ts} +2 -2
- package/dist/providers/{tithely.js → tithely/index.js} +60 -91
- package/dist/providers/tithely/skill.generated.js +8 -0
- package/dist/providers/{vercel.d.ts → vercel/index.d.ts} +8 -8
- package/dist/providers/vercel/index.js +1038 -0
- package/dist/providers/vercel/mcp-ownership.js +212 -0
- package/dist/providers/vercel/skill.generated.js +11 -0
- package/dist/registry.d.ts +108 -171
- package/dist/registry.js +513 -925
- package/dist/request-state.js +21 -0
- package/dist/resource-read.js +100 -0
- package/dist/result-shapes.js +17 -49
- package/dist/retired-toolkits.js +1 -1
- package/dist/routes/access-tokens.js +3 -6
- package/dist/routes/activity-context.js +21 -0
- package/dist/routes/activity-disclosure.js +40 -0
- package/dist/routes/activity.js +59 -21
- package/dist/routes/actor-labels.js +3 -50
- package/dist/routes/connect.js +221 -0
- package/dist/routes/credentials.js +15 -27
- package/dist/routes/mcp.js +356 -130
- package/dist/routes/oauth-client-metadata.js +15 -0
- package/dist/routes/oauth-management.js +45 -107
- package/dist/routes/oauth.js +110 -57
- package/dist/routes/operator.js +3 -3
- package/dist/routes/shared.d.ts +25 -64
- package/dist/routes/shared.js +160 -80
- package/dist/routes/skills.js +30 -0
- package/dist/routes/ui-config.js +225 -0
- package/dist/routes/ui-data.js +40 -35
- package/dist/routes/ui.js +66 -56
- package/dist/runtime/admission.js +4 -2
- package/dist/runtime/call-admission.js +3 -1
- package/dist/runtime/connector-scope.js +1 -4
- package/dist/runtime/run.js +10 -39
- package/dist/runtime/services.js +12 -117
- package/dist/runtime/shared-read.js +1 -3
- package/dist/runtime/storage.js +8 -1
- package/dist/sent-secrets.d.ts +52 -0
- package/dist/sent-secrets.js +474 -0
- package/dist/server.js +28 -71
- package/dist/skills.js +406 -174
- package/dist/sqlite.d.ts +48 -0
- package/dist/sqlite.js +166 -0
- package/dist/storage/keys.js +428 -0
- package/dist/storage/memory.js +7 -0
- package/dist/storage/sql.d.ts +66 -0
- package/dist/storage/sql.js +534 -0
- package/dist/timeout.js +4 -2
- package/dist/tool-name.js +17 -0
- package/dist/tool-safety.d.ts +9 -35
- package/dist/tool-safety.js +20 -40
- package/dist/types.d.ts +320 -54
- package/dist/types.js +0 -1
- package/dist/ui.d.ts +7 -19
- package/dist/ui.js +76 -90
- package/dist/url-safety.js +3 -6
- package/dist/usage-guide.js +137 -0
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +309 -47
- package/dist/version.d.ts +1 -7
- package/dist/version.js +3 -7
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +434 -0
- package/documentation/architecture.md +664 -231
- package/documentation/auth.md +936 -546
- package/documentation/code-mode.md +501 -312
- package/documentation/meta-tools.md +449 -200
- package/documentation/operator-tests.md +93 -0
- package/documentation/provider-migration-0.29.md +60 -0
- package/examples/worker/AGENTS.md +34 -12
- package/examples/worker/README.md +286 -255
- package/examples/worker/kv-to-d1.wrangler.jsonc +26 -0
- package/examples/worker/scripts/copy-kv-to-d1.mjs +162 -0
- package/examples/worker/src/connecta.config.ts +151 -0
- package/examples/worker/src/index.ts +14 -197
- package/examples/worker/wrangler.jsonc +15 -35
- package/package.json +120 -54
- package/templates/node/.dockerignore +1 -2
- package/templates/node/.env.example +20 -21
- package/templates/node/AGENTS.md +21 -17
- package/templates/node/Dockerfile +3 -5
- package/templates/node/README.md +133 -58
- package/templates/node/docker-compose.yml +11 -12
- package/templates/node/package.json +4 -2
- package/templates/node/src/connecta.config.ts +119 -0
- package/templates/node/src/index.ts +9 -154
- package/templates/node/src/provision-token.ts +17 -0
- package/AGENTS.md +0 -170
- package/dist/artifacts/connector.d.ts +0 -58
- package/dist/artifacts/connector.js +0 -730
- package/dist/artifacts/document.js +0 -86
- package/dist/artifacts/guide.js +0 -140
- package/dist/artifacts/html-scan.js +0 -329
- package/dist/artifacts/json.js +0 -78
- package/dist/artifacts/kv-store.d.ts +0 -20
- package/dist/artifacts/kv-store.js +0 -140
- package/dist/artifacts/markdown.js +0 -478
- package/dist/artifacts/module.d.ts +0 -37
- package/dist/artifacts/module.js +0 -66
- package/dist/artifacts/operations.d.ts +0 -197
- package/dist/artifacts/operations.js +0 -1002
- package/dist/artifacts/refresh.d.ts +0 -55
- package/dist/artifacts/refresh.js +0 -236
- package/dist/artifacts/routes.js +0 -206
- package/dist/artifacts/types.d.ts +0 -214
- package/dist/artifacts/types.js +0 -22
- package/dist/artifacts/validate.d.ts +0 -62
- package/dist/artifacts/validate.js +0 -651
- package/dist/artifacts.d.ts +0 -6
- package/dist/artifacts.js +0 -9
- package/dist/auth/bearer.d.ts +0 -11
- package/dist/auth/bearer.js +0 -50
- package/dist/providers/basecamp.js +0 -445
- package/dist/providers/linear.js +0 -217
- package/dist/providers/mixpanel.js +0 -247
- package/dist/providers/revenuecat.js +0 -317
- package/dist/providers/stripe.js +0 -253
- package/dist/providers/vercel.js +0 -1253
- package/dist/storage/file.d.ts +0 -15
- package/dist/storage/file.js +0 -455
- package/ethos.md +0 -93
- package/examples/worker/src/cloudflare-kv.ts +0 -43
- package/examples/worker/src/d1-activity-row.ts +0 -155
- package/examples/worker/src/d1-activity.ts +0 -147
- package/examples/worker/src/d1-storage.ts +0 -138
- package/examples/worker/src/r2-artifact-blobs.ts +0 -29
- 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 "
|
|
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
|
|
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 {
|
|
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
|
|
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 (
|
|
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
|
-
* `
|
|
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 "
|
|
52
|
-
import { guardedFetch, retryAfterMs, } from "
|
|
53
|
-
import { ConnectorCallError } from "
|
|
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("
|
|
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)) ??
|
|
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: {
|
|
793
|
-
|
|
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, {
|
|
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: {
|
|
843
|
-
|
|
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: {
|
|
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
|
|
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);
|