@zackbart/connecta 0.33.0 → 0.34.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 +35 -0
- package/README.md +99 -128
- package/SECURITY.md +1 -1
- package/bin/version.mjs +1 -1
- package/dist/activity.d.ts +3 -0
- package/dist/branding.d.ts +7 -0
- package/dist/branding.js +76 -9
- package/dist/catalog-service.js +15 -1
- package/dist/config-value-sources.js +6 -1
- package/dist/config.d.ts +13 -2
- package/dist/config.js +36 -2
- package/dist/connector-caller.js +2 -1
- package/dist/describe-config.d.ts +3 -1
- package/dist/describe-config.js +1 -0
- package/dist/execute.js +1 -11
- package/dist/fix-prompt.js +2 -2
- package/dist/index.js +1 -0
- package/dist/membership.d.ts +2 -0
- package/dist/oauth-callback-outcome.js +2 -2
- package/dist/operator-ui/agents.d.ts +1 -1
- package/dist/operator-ui/agents.js +16 -16
- package/dist/operator-ui/config-summary.js +562 -0
- package/dist/operator-ui/connection-map.js +375 -0
- package/dist/operator-ui/endpoints.js +44 -15
- package/dist/operator-ui/fix-prompts.js +1 -1
- package/dist/operator-ui/generated.js +4 -4
- package/dist/operator-ui/member-page.js +13 -28
- package/dist/operator-ui/overview.js +134 -0
- package/dist/operator-ui/view.d.ts +27 -22
- package/dist/operator-ui/view.js +104 -88
- package/dist/page-styles.js +5 -3
- package/dist/providers/_shared/google/workspace.d.ts +6 -4
- package/dist/providers/_shared/google/workspace.js +6 -2
- package/dist/providers/ccb/index.js +1 -1
- package/dist/providers/docs/index.js +1 -1
- package/dist/providers/docs/skill.generated.js +2 -2
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/drive/skill.generated.js +2 -2
- package/dist/providers/forms/index.js +1 -1
- package/dist/providers/forms/skill.generated.js +2 -2
- package/dist/providers/gmail/index.js +1 -1
- package/dist/providers/gmail/skill.generated.js +2 -2
- package/dist/providers/mixpanel/skill.generated.js +3 -3
- package/dist/providers/notion/skill.generated.js +1 -1
- package/dist/providers/overflow/index.js +1 -1
- package/dist/providers/overflow/skill.generated.js +2 -2
- package/dist/providers/planning-center/index.js +1 -1
- package/dist/providers/planning-center/skill.generated.js +4 -4
- package/dist/providers/revenuecat/skill.generated.js +2 -2
- package/dist/providers/sheets/index.js +1 -1
- package/dist/providers/sheets/skill.generated.js +2 -2
- package/dist/providers/slides/index.js +1 -1
- package/dist/providers/slides/skill.generated.js +2 -2
- package/dist/providers/tithely/index.js +1 -1
- package/dist/providers/vercel/index.js +1 -1
- package/dist/providers/vercel/skill.generated.js +1 -1
- package/dist/registry.js +15 -0
- package/dist/routes/mcp.js +20 -13
- package/dist/routes/oauth.js +2 -2
- package/dist/skills.js +10 -0
- package/dist/storage/sql.js +5 -1
- package/dist/types.d.ts +18 -7
- package/dist/ui.js +9 -4
- package/dist/usage-guide.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/README.md +2 -2
- package/documentation/architecture.md +18 -13
- package/documentation/auth.md +183 -67
- package/documentation/code-mode.md +23 -5
- package/documentation/deploying.md +5 -1
- package/documentation/integrating.md +9 -6
- package/documentation/meta-tools.md +36 -22
- package/documentation/migration-0.33.md +21 -13
- package/documentation/migration-0.34.md +67 -0
- package/documentation/operator-tests.md +46 -20
- package/documentation/operator-ui.md +294 -104
- package/examples/worker/AGENTS.md +15 -9
- package/examples/worker/README.md +41 -14
- package/examples/worker/src/connecta.config.ts +5 -4
- package/examples/worker/wrangler.jsonc +1 -1
- package/package.json +19 -19
- package/templates/node/AGENTS.md +13 -7
- package/templates/node/README.md +14 -11
- package/templates/node/package.json +1 -1
- package/templates/node/src/connecta.config.ts +2 -2
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Google Drive usage\n\nActs as the signed-in person's own Google Drive through Workspace delegation: what they can open, and nothing they cannot.\n\nConnection purpose: \n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No\nargument names an account. A call with none mapped
|
|
3
|
+
"content": "# Google Drive usage\n\nActs as the signed-in person's own Google Drive through Workspace delegation: what they can open, and nothing they cannot.\n\nConnection purpose: \n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No\nargument names an account. A call with none mapped fails `auth_required`; only an operator can change\nthat. A missing file\nand one this person cannot see fail alike — Google does not distinguish them.\n\n## Finding files\n\n- `search_files` takes Drive query syntax: `name contains 'x'`,\n `fullText contains 'x'`, `mimeType = '…'`, `'<folderId>' in parents`,\n `modifiedTime > '2026-01-01T00:00:00'`. Quote values with single quotes.\n Trashed files are left out unless `includeTrashed`. `incompleteSearch:\n true` means Drive gave up before searching everything; narrow the query or\n the corpus. Shared drives: `list_shared_drives`, then `corpora: \"drive\"`\n with its `driveId`.\n- Page with `page.nextCursor` and the same arguments; a cursor from another\n tool or other arguments is refused. A page can stop short of `limit` to\n stay under `maxBytes`; the cursor resumes at the first row left out, and\n fails `conflict` if that page changed meanwhile — start over then.\n- Ids come from these reads or a Drive URL (`/d/<id>/`, `/folders/<id>`);\n never guess one.\n\n## Reading content\n\n- `get_file_content` exports Docs as Markdown, Sheets as CSV of the\n **first sheet only**, and Slides as plain text; reads text files as text;\n and returns other files as base64 when the encoded file fits the result. Anything else — a\n larger binary, a drawing, a form, a folder, a shortcut — comes back as\n `format: \"unavailable\"` with a `note`, never as an empty success.\n- Text is capped by `maxChars`; a cut ends with a truncation marker and\n `contentTruncated: true`. Drive exports at most 10 MB.\n- Every read stays under `maxBytes` of JSON. The default is what one result\n can carry into `execute_code`, so a program always receives it. A larger\n `maxBytes` (up to 4 MiB — a binary of up to 1 MiB) reaches only a direct\n `call_tool`, which pages it with `connecta.result`.\n\n## Writing\n\n- `create_folder` and `restore_file` add without losing or exposing\n anything.\n- A new file takes its folder's sharing, not its source's: `create_file`\n and `copy_file` into a shared folder disclose that content to everyone\n the folder is shared with, so both are approved like any destructive write.\n Check the destination with `list_permissions` first. `convertTo`\n imports content as a Google Doc, Sheet, or Slides file.\n- `update_file_content` replaces the whole content; send all of it.\n `update_file` renames; `move_file` changes the folder and with it who\n inherits access.\n- `trash_file` is recoverable for 30 days with `restore_file`. There is no\n permanent delete and no ownership transfer.\n- `share_file` emails no one unless `sendNotificationEmail: true`. Check\n `list_permissions` before and after changing access, and confirm with the\n person before sharing with `anyone` or a whole domain.\n",
|
|
4
4
|
"instructionsHeading": "Connection instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": "# Google Drive usage\n\nActs as the signed-in person's own Google Drive through Workspace delegation: what they can open, and nothing they cannot.\n\nConnection purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No\nargument names an account. A call with none mapped
|
|
7
|
+
"guide_1": "\n\n## Whose Drive\n\nEvery call acts as the Workspace address deployment config maps the caller\nto, and reaches exactly the files that person can open, shared drives\nincluded; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No\nargument names an account. A call with none mapped fails `auth_required`; only an operator can change\nthat. A missing file\nand one this person cannot see fail alike — Google does not distinguish them.\n\n## Finding files\n\n- `search_files` takes Drive query syntax: `name contains 'x'`,\n `fullText contains 'x'`, `mimeType = '…'`, `'<folderId>' in parents`,\n `modifiedTime > '2026-01-01T00:00:00'`. Quote values with single quotes.\n Trashed files are left out unless `includeTrashed`. `incompleteSearch:\n true` means Drive gave up before searching everything; narrow the query or\n the corpus. Shared drives: `list_shared_drives`, then `corpora: \"drive\"`\n with its `driveId`.\n- Page with `page.nextCursor` and the same arguments; a cursor from another\n tool or other arguments is refused. A page can stop short of `limit` to\n stay under `maxBytes`; the cursor resumes at the first row left out, and\n fails `conflict` if that page changed meanwhile — start over then.\n- Ids come from these reads or a Drive URL (`/d/<id>/`, `/folders/<id>`);\n never guess one.\n\n## Reading content\n\n- `get_file_content` exports Docs as Markdown, Sheets as CSV of the\n **first sheet only**, and Slides as plain text; reads text files as text;\n and returns other files as base64 when the encoded file fits the result. Anything else — a\n larger binary, a drawing, a form, a folder, a shortcut — comes back as\n `format: \"unavailable\"` with a `note`, never as an empty success.\n- Text is capped by `maxChars`; a cut ends with a truncation marker and\n `contentTruncated: true`. Drive exports at most 10 MB.\n- Every read stays under `maxBytes` of JSON. The default is what one result\n can carry into `execute_code`, so a program always receives it. A larger\n `maxBytes` (up to 4 MiB — a binary of up to 1 MiB) reaches only a direct\n `call_tool`, which pages it with `connecta.result`.\n\n## Writing\n\n- `create_folder` and `restore_file` add without losing or exposing\n anything.\n- A new file takes its folder's sharing, not its source's: `create_file`\n and `copy_file` into a shared folder disclose that content to everyone\n the folder is shared with, so both are approved like any destructive write.\n Check the destination with `list_permissions` first. `convertTo`\n imports content as a Google Doc, Sheet, or Slides file.\n- `update_file_content` replaces the whole content; send all of it.\n `update_file` renames; `move_file` changes the folder and with it who\n inherits access.\n- `trash_file` is recoverable for 30 days with `restore_file`. There is no\n permanent delete and no ownership transfer.\n- `share_file` emails no one unless `sendNotificationEmail: true`. Check\n `list_permissions` before and after changing access, and confirm with the\n person before sharing with `anyone` or a whole domain.\n"
|
|
8
8
|
}
|
|
9
9
|
};
|
|
@@ -22,7 +22,7 @@ import { skill } from "./skill.generated.js";
|
|
|
22
22
|
*
|
|
23
23
|
* Drift. Google publishes a credential-free Discovery document per API.
|
|
24
24
|
* `src/providers/forms/drift.json` records the five methods the tools
|
|
25
|
-
* call, and `
|
|
25
|
+
* call, and `pnpm run providers:check --provider forms` reports a touched
|
|
26
26
|
* contract that moved or a method that stopped accepting the scopes below.
|
|
27
27
|
*/
|
|
28
28
|
import { apiConnector as api, defined } from "../../connectors/api-connector.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Google Forms usage\n\nActs as the signed-in person in Google Forms through Workspace delegation: read and edit their forms, read responses, never submit or delete.\n\nConnection purpose: \n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names a user. A call with none mapped
|
|
3
|
+
"content": "# Google Forms usage\n\nActs as the signed-in person in Google Forms through Workspace delegation: read and edit their forms, read responses, never submit or delete.\n\nConnection purpose: \n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names a user. A call with none mapped fails `auth_required`; only an operator can change\nthat. A form id\nthat does not exist and one not shared with that user fail alike, as\n`connector_call_failed`: Google does not say which.\n\n## Finding forms\n\nThere is no list or search here: the Forms API has none. Find a form in\nDrive, or take its id from an edit URL (`/forms/d/<formId>/edit`). The\n`/forms/d/e/…` responder link is not the form id.\n\n## Reading\n\n- Every result stays inside KiB, so it crosses into\n `execute_code` whole. A page that would outgrow that ends early with\n `page.hasMore`; follow `page.nextCursor` rather than raising `limit`.\n- `get_form` lists items with their `questions`: one for a question,\n one per row for a grid, none for sections and media. Answers are keyed by\n those `questionId`s. A large form pages its items (`itemCount` is the\n total), and a grid too large for one page continues on the next: the item\n repeats with `questionsFrom`, the index of its first row there, and\n `moreQuestions` counts rows still to come. A cursor from a form that has\n since changed fails `conflict`.\n Long text is cut with a marker; `raw: true` returns Google's whole Form\n resource, or refuses one too large for a result.\n- `list_responses` labels each answer with its question's title, in form\n order; an answer to a since-deleted question has no title. Answers longer\n than characters end with a truncation marker; `get_response`\n reads one whole. Page with `page.nextCursor` and the same\n `submittedAfter`. To read only new responses, pass the newest\n `lastSubmittedTime` already seen as `submittedAfter`. A cursor\n continues after the last response it returned; if Google's page has\n changed since (new, deleted, or reordered responses), it fails\n `conflict` — start again, or use `submittedAfter`.\n- A response too large for one result comes back cut, with the answers left\n out named in `omittedQuestionIds`; pass them to `get_response` as\n `questionIds`.\n\n## Editing\n\n- `create_form` makes an empty form: Google takes only its title and Drive\n file name at creation. Add a description and questions afterwards.\n- `update_form_info` replaces the title or description.\n- `batch_update_form` is the escape hatch for everything else, and always\n needs approval. It takes Google's own Request objects, applied all or none,\n with the form's current `revisionId` as `requiredRevisionId`: a form\n someone changed since you read it is refused as `conflict`, so re-read it\n and rebuild the edit. Its reply carries the new `revisionId` and each created item's id.\n A reply too large to return whole comes back with `truncated: true`: the\n batch still applied; read the rest with `get_form`. An `updateItem` needs an `updateMask`; read `raw: true` first so the\n item it replaces keeps its grading, images, and navigation.\n- Publishing, sharing, deleting, and watches are not in this connection; the\n owner does those in Forms or Drive. Responses are read-only.\n",
|
|
4
4
|
"instructionsHeading": "Connection instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": "# Google Forms usage\n\nActs as the signed-in person in Google Forms through Workspace delegation: read and edit their forms, read responses, never submit or delete.\n\nConnection purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names a user. A call with none mapped
|
|
7
|
+
"guide_1": "\n\n## Whose forms\n\nEvery call reaches only the forms the Workspace user deployment config maps\nthe caller to can open; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument names a user. A call with none mapped fails `auth_required`; only an operator can change\nthat. A form id\nthat does not exist and one not shared with that user fail alike, as\n`connector_call_failed`: Google does not say which.\n\n## Finding forms\n\nThere is no list or search here: the Forms API has none. Find a form in\nDrive, or take its id from an edit URL (`/forms/d/<formId>/edit`). The\n`/forms/d/e/…` responder link is not the form id.\n\n## Reading\n\n- Every result stays inside ",
|
|
8
8
|
"guide_2": " KiB, so it crosses into\n `execute_code` whole. A page that would outgrow that ends early with\n `page.hasMore`; follow `page.nextCursor` rather than raising `limit`.\n- `get_form` lists items with their `questions`: one for a question,\n one per row for a grid, none for sections and media. Answers are keyed by\n those `questionId`s. A large form pages its items (`itemCount` is the\n total), and a grid too large for one page continues on the next: the item\n repeats with `questionsFrom`, the index of its first row there, and\n `moreQuestions` counts rows still to come. A cursor from a form that has\n since changed fails `conflict`.\n Long text is cut with a marker; `raw: true` returns Google's whole Form\n resource, or refuses one too large for a result.\n- `list_responses` labels each answer with its question's title, in form\n order; an answer to a since-deleted question has no title. Answers longer\n than ",
|
|
9
9
|
"guide_3": " characters end with a truncation marker; `get_response`\n reads one whole. Page with `page.nextCursor` and the same\n `submittedAfter`. To read only new responses, pass the newest\n `lastSubmittedTime` already seen as `submittedAfter`. A cursor\n continues after the last response it returned; if Google's page has\n changed since (new, deleted, or reordered responses), it fails\n `conflict` — start again, or use `submittedAfter`.\n- A response too large for one result comes back cut, with the answers left\n out named in `omittedQuestionIds`; pass them to `get_response` as\n `questionIds`.\n\n## Editing\n\n- `create_form` makes an empty form: Google takes only its title and Drive\n file name at creation. Add a description and questions afterwards.\n- `update_form_info` replaces the title or description.\n- `batch_update_form` is the escape hatch for everything else, and always\n needs approval. It takes Google's own Request objects, applied all or none,\n with the form's current `revisionId` as `requiredRevisionId`: a form\n someone changed since you read it is refused as `conflict`, so re-read it\n and rebuild the edit. Its reply carries the new `revisionId` and each created item's id.\n A reply too large to return whole comes back with `truncated: true`: the\n batch still applied; read the rest with `get_form`. An `updateItem` needs an `updateMask`; read `raw: true` first so the\n item it replaces keeps its grading, images, and navigation.\n- Publishing, sharing, deleting, and watches are not in this connection; the\n owner does those in Forms or Drive. Responses are read-only.\n"
|
|
10
10
|
}
|
|
@@ -21,7 +21,7 @@ import { skill } from "./skill.generated.js";
|
|
|
21
21
|
*
|
|
22
22
|
* Drift. Google publishes a credential-free Discovery document per API.
|
|
23
23
|
* `src/providers/gmail/drift.json` records the nine methods the tools
|
|
24
|
-
* call, and `
|
|
24
|
+
* call, and `pnpm run providers:check --provider gmail` reports a touched
|
|
25
25
|
* contract that moved or a method that stopped accepting the scopes below.
|
|
26
26
|
*/
|
|
27
27
|
import { apiConnector as api, defined } from "../../connectors/api-connector.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Gmail usage (draft-only)\n\nActs as the signed-in person's own Gmail through Workspace delegation: read mail and write drafts, never send.\n\nConnection purpose: \n\n## Whose mailbox\n\nEvery call reads and writes the mailbox deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared mailbox that every caller, agent or person, acts as. No argument\nnames a mailbox. A call with none mapped
|
|
3
|
+
"content": "# Gmail usage (draft-only)\n\nActs as the signed-in person's own Gmail through Workspace delegation: read mail and write drafts, never send.\n\nConnection purpose: \n\n## Whose mailbox\n\nEvery call reads and writes the mailbox deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared mailbox that every caller, agent or person, acts as. No argument\nnames a mailbox. A call with none mapped fails `auth_required`; only an operator can change\nthat.\n\n## Reading\n\n- `search_threads` takes Gmail search syntax; reuse the person's own\n phrasing (`from:`, `subject:`, `newer_than:7d`, `is:unread`,\n `label:`). Page with `page.nextCursor` and the same query.\n- `get_thread` returns messages oldest first with bodies capped by\n `maxBodyChars`; a cut body ends with a truncation marker. A long thread\n pages: follow `page.nextCursor` until `hasMore` is false, with the same\n arguments. `conflict` means the thread changed under the cursor; read it\n again from the start. Reduce inside\n `execute_code` before returning a long thread.\n- Results default to at most 192 KiB; `list_labels` pages the same way.\n- `get_message` and `get_thread` list attachment metadata. An attached or\n forwarded email is an attachment, not the body.\n- `bodyFormat: \"unavailable\"` means Gmail stored the body apart from the\n message and it is too large to read here; say so rather than summarizing.\n- `get_message`, each `get_thread` message, and `get_draft` return `bodyNote`\n when decoding falls back to UTF-8 for an unsupported charset. The note\n survives character and result-byte truncation, including `maxBodyChars: 0`\n when inline body data is decoded. It counts toward the result's byte limit.\n\n## Attachments\n\n- `get_attachment` takes `messageId` and the listing's immutable `partId`.\n Prefer `partId`, which stays stable across message reads, including an\n empty id for a root attachment. `attachmentId` is an alternative that\n Gmail can rotate between reads. When both are supplied, `partId` wins and\n the fresh message's download id is used. Read the listing again after\n an unknown-id refusal; never store an attachmentId for later use.\n- Small attachments can have inline data and no attachmentId. Use their\n partId too. Only carried attachment parts can be read, never arbitrary\n message body parts or blind download ids.\n- Text, JSON, XML, calendar invites, CSV, and `message/rfc822` return\n `format: \"text\"` and `content` decoded in the declared charset. An unsupported\n charset produces an explicit `note` that decoding used UTF-8. Message bodies\n return the notice separately as `bodyNote`.\n Parsed forwarded emails render From, To, Date, Subject, and their own\n plain body, else HTML converted to text. Raw forwarded data is preferred\n when present. A forwarded email without readable text returns an\n `unavailable` note.\n `maxChars` defaults to 12,000 code points, at most 100,000. A character\n or byte cut ends with a truncation marker and `contentTruncated: true`.\n Reduce attachment text inside `execute_code` before returning it.\n- Binary attachments return `format: \"base64\"` and standard base64 in\n `content`. Pass it unchanged to Drive `create_file` as `contentBase64`,\n with `name: filename` and `mimeType`. File creation is a write: use\n `call_destructive_tool` or a program on a writable endpoint for that step.\n- Downloads, text and binary, are limited to 1 MiB. Declared size is\n checked before a separate download. Parsed forwarded emails check the sum\n of descendant sizes before downloading any stored body, and return that\n sum as `size`. Attachment data must match both the message part's declared\n size and the download response's size when supplied; inconsistent payloads\n fail rather than return incomplete files. Binary content must also fit the\n whole-result `maxBytes`, which defaults to 192 KiB. `format: \"unavailable\"`\n returns a note and no content when it cannot fit. Raising `maxChars`\n or `maxBytes` cannot bypass the download cap.\n- Raise `maxBytes` only for a direct `call_tool` read, from 64 KiB up to\n 4 MiB, as with Drive `get_file_content`. `execute_code` cannot receive\n large results; keep the default there. Direct-call results may be paged.\n\n## Drafting\n\n- `create_draft` saves a draft and never sends it; tell the person it is\n waiting in Drafts. There is no send, delete, or label tool.\n- To reply, pass `replyToMessageId`: the draft joins that thread with\n In-Reply-To and References set, and subject (Re: …) and `to` default\n from that message's Reply-To, else its From. Keep the Re: subject or Gmail\n may start a new thread.\n- `update_draft` replaces the body entirely; send the full new text.\n From, To, Cc, Bcc, Reply-To, Subject, the thread, and reply headers are\n kept unless restated; no other header is. A draft with attachments or\n inline images is refused, unchanged — Gmail's update would delete them.\n- Gmail sets From to the mailbox itself; there is no From argument.\n",
|
|
4
4
|
"instructionsHeading": "Connection instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": "# Gmail usage (draft-only)\n\nActs as the signed-in person's own Gmail through Workspace delegation: read mail and write drafts, never send.\n\nConnection purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Whose mailbox\n\nEvery call reads and writes the mailbox deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared mailbox that every caller, agent or person, acts as. No argument\nnames a mailbox. A call with none mapped
|
|
7
|
+
"guide_1": "\n\n## Whose mailbox\n\nEvery call reads and writes the mailbox deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared mailbox that every caller, agent or person, acts as. No argument\nnames a mailbox. A call with none mapped fails `auth_required`; only an operator can change\nthat.\n\n## Reading\n\n- `search_threads` takes Gmail search syntax; reuse the person's own\n phrasing (`from:`, `subject:`, `newer_than:7d`, `is:unread`,\n `label:`). Page with `page.nextCursor` and the same query.\n- `get_thread` returns messages oldest first with bodies capped by\n `maxBodyChars`; a cut body ends with a truncation marker. A long thread\n pages: follow `page.nextCursor` until `hasMore` is false, with the same\n arguments. `conflict` means the thread changed under the cursor; read it\n again from the start. Reduce inside\n `execute_code` before returning a long thread.\n- Results default to at most 192 KiB; `list_labels` pages the same way.\n- `get_message` and `get_thread` list attachment metadata. An attached or\n forwarded email is an attachment, not the body.\n- `bodyFormat: \"unavailable\"` means Gmail stored the body apart from the\n message and it is too large to read here; say so rather than summarizing.\n- `get_message`, each `get_thread` message, and `get_draft` return `bodyNote`\n when decoding falls back to UTF-8 for an unsupported charset. The note\n survives character and result-byte truncation, including `maxBodyChars: 0`\n when inline body data is decoded. It counts toward the result's byte limit.\n\n## Attachments\n\n- `get_attachment` takes `messageId` and the listing's immutable `partId`.\n Prefer `partId`, which stays stable across message reads, including an\n empty id for a root attachment. `attachmentId` is an alternative that\n Gmail can rotate between reads. When both are supplied, `partId` wins and\n the fresh message's download id is used. Read the listing again after\n an unknown-id refusal; never store an attachmentId for later use.\n- Small attachments can have inline data and no attachmentId. Use their\n partId too. Only carried attachment parts can be read, never arbitrary\n message body parts or blind download ids.\n- Text, JSON, XML, calendar invites, CSV, and `message/rfc822` return\n `format: \"text\"` and `content` decoded in the declared charset. An unsupported\n charset produces an explicit `note` that decoding used UTF-8. Message bodies\n return the notice separately as `bodyNote`.\n Parsed forwarded emails render From, To, Date, Subject, and their own\n plain body, else HTML converted to text. Raw forwarded data is preferred\n when present. A forwarded email without readable text returns an\n `unavailable` note.\n `maxChars` defaults to 12,000 code points, at most 100,000. A character\n or byte cut ends with a truncation marker and `contentTruncated: true`.\n Reduce attachment text inside `execute_code` before returning it.\n- Binary attachments return `format: \"base64\"` and standard base64 in\n `content`. Pass it unchanged to Drive `create_file` as `contentBase64`,\n with `name: filename` and `mimeType`. File creation is a write: use\n `call_destructive_tool` or a program on a writable endpoint for that step.\n- Downloads, text and binary, are limited to 1 MiB. Declared size is\n checked before a separate download. Parsed forwarded emails check the sum\n of descendant sizes before downloading any stored body, and return that\n sum as `size`. Attachment data must match both the message part's declared\n size and the download response's size when supplied; inconsistent payloads\n fail rather than return incomplete files. Binary content must also fit the\n whole-result `maxBytes`, which defaults to 192 KiB. `format: \"unavailable\"`\n returns a note and no content when it cannot fit. Raising `maxChars`\n or `maxBytes` cannot bypass the download cap.\n- Raise `maxBytes` only for a direct `call_tool` read, from 64 KiB up to\n 4 MiB, as with Drive `get_file_content`. `execute_code` cannot receive\n large results; keep the default there. Direct-call results may be paged.\n\n## Drafting\n\n- `create_draft` saves a draft and never sends it; tell the person it is\n waiting in Drafts. There is no send, delete, or label tool.\n- To reply, pass `replyToMessageId`: the draft joins that thread with\n In-Reply-To and References set, and subject (Re: …) and `to` default\n from that message's Reply-To, else its From. Keep the Re: subject or Gmail\n may start a new thread.\n- `update_draft` replaces the body entirely; send the full new text.\n From, To, Cc, Bcc, Reply-To, Subject, the thread, and reply headers are\n kept unless restated; no other header is. A draft with attachments or\n inline images is refused, unchanged — Gmail's update would delete them.\n- Gmail sets From to the mailbox itself; there is no From argument.\n"
|
|
8
8
|
}
|
|
9
9
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "\n\n- This connection uses Connecta's REST tools with a service account. Hosted MCP is the default `mixpanel()` mode; `via: \"api\"` explicitly selects REST. Declare two connection ids when both are useful. The operator pastes `username:secret` into the API connection, which sends ordinary HTTP Basic, unlike hosted MCP's `Bearer Basic` scheme. OAuth and static headers are refused in API mode.\n- One connection stays in one residency region, across all projects the account can reach. US uses `mixpanel.com` for query/app, `data.mixpanel.com` for export and `api.mixpanel.com` for ingestion. EU uses `eu.mixpanel.com`, `data-eu.mixpanel.com` and `api-eu.mixpanel.com`. India uses `in.mixpanel.com`, `data-in.mixpanel.com` and `api-in.mixpanel.com`. Wrong residency can look like missing data; do not interpret an empty response without checking the project region.\n- Start with `list_projects`, which returns redacted account metadata from `/api/app/me`. Its public reference does not specify the response schema. Resolve ids from that metadata or project settings, never guess one. Every project-scoped tool accepts `project_id`; an omitted id uses `defaultProjectId` when configured. An explicit id overrides that default and still requires service-account membership. Projects with Data Views may also require `workspace_id
|
|
3
|
+
"content": "\n\n- This connection uses Connecta's REST tools with a service account. Hosted MCP is the default `mixpanel()` mode; `via: \"api\"` explicitly selects REST. Declare two connection ids when both are useful. The operator pastes `username:secret` into the API connection, which sends ordinary HTTP Basic, unlike hosted MCP's `Bearer Basic` scheme. OAuth and static headers are refused in API mode.\n- One connection stays in one residency region, across all projects the account can reach. US uses `mixpanel.com` for query/app, `data.mixpanel.com` for export and `api.mixpanel.com` for ingestion. EU uses `eu.mixpanel.com`, `data-eu.mixpanel.com` and `api-eu.mixpanel.com`. India uses `in.mixpanel.com`, `data-in.mixpanel.com` and `api-in.mixpanel.com`. Wrong residency can look like missing data; do not interpret an empty response without checking the project region.\n- Start with `list_projects`, which returns redacted account metadata from `/api/app/me`. Its public reference does not specify the response schema. Resolve ids from that metadata or project settings, never guess one. Every project-scoped tool accepts `project_id`; an omitted id uses `defaultProjectId` when configured. An explicit id overrides that default and still requires service-account membership. Projects with Data Views may also require `workspace_id`, which the query and report tools accept; `export_events` and `import_events` do not.\n- Use REST for raw event export, profile queries and individual event sequences. Use hosted MCP for schema discovery, business context, aggregate analytics and dashboard work. Discover exact event and property names there before filtering raw data.\n- Export has no documented cursor. Begin with a short date range and an event or `where` filter. Every export returns `truncated` and a notice. A true flag means events may be missing at the byte or event cap; narrow ranges or filters, rather than inferring \"no later event B\" from a sample. Dates are inclusive. Mixpanel documents UTC for newer projects and the project timezone for older projects; check project settings around the January 2023 transition. Event timestamps default to seconds, or milliseconds with `time_in_ms`.\n- `get_profile_activity` sorts a user's returned events by timestamp, but `/stream/query` is in maintenance mode and has no documented pagination/completeness guarantee. For absence or sequence claims use complete, filtered raw export and sort/reduce inside `execute_code`.\n- `query_profiles` is a read-only POST to the Query API's `/engage`, distinct from ingestion `/engage` profile mutations. Pagination is vendor-owned: there is no documented caller `page_size`. Keep the same selector and output properties, pass the returned `session_id`, and increment `page` while `page.hasMore` is true. Do not treat `total` as the page length. `raw: true` preserves the redacted vendor envelope when needed.\n- Saved Insights uses `bookmark_id` from a report URL. Saved Funnels uses `funnel_id`, discoverable with `mixpanel_api_read` on `/api/query/funnels/list`. The documented Retention endpoint takes events and date parameters, not a saved-report id; default/birth retention also requires `born_event`. Use hosted MCP for an existing saved Retention report; `query_retention` recomputes retention from events and dates. Reduce nested report results before returning them.\n- Retention `interval` is capped at 90 only for `unit: \"day\"`, including the default when unit is omitted. Week and month intervals have no documented 90-unit cap. Both named and generic retention reads enforce the day limit.\n- `mixpanel_api_search` and `mixpanel_api_details` inspect the reviewed operation subset locally. `mixpanel_api_read` permits GET only on those operations, with fixed residency hosts and no caller URL or headers. It cannot reach service-account administration, arbitrary app APIs, profile mutation, JQL or ingestion. Generic export retains the named export's byte/event bounds. Use named tools for Engage and import.\n- `import_events` is an additive write and requires an Owner or Admin service account on the target project. Connecta enforces 2000 events, 10MB per batch, less than 1MB per event and Mixpanel's documented property limits. Supply stable `$insert_id` values and use strict validation. A 400 can still ingest the valid events; never retry a whole batch automatically. Inspect the target and failed records through a trusted operator workflow before deciding what to send again.\n- Profile writes are deferred. The official ingestion `/engage` contract requires `$token` in each update; service-account Basic alone does not satisfy it, and the service-account reference documents no project-token retrieval operation. Profile delete and other irreversible mutations are absent.\n- Query permits 60 queries per hour and 5 concurrent queries per project. Export permits 60 per hour, 3 per second and 100 concurrent queries. Ingestion permits 2GB uncompressed JSON per minute or about 30000 events per second. The API connection conservatively shares a 60-call/hour, 5-concurrent default across all tools and projects; it is a per-runtime approximation, not a distributed quota or a per-second export limiter. Operators may override `callAdmission`, especially for import-heavy deployments or multiple runtimes sharing an account. A vendor 429 still wins; respect `Retry-After`.\n- Successful responses recursively redact credential-named properties and bearer URLs. Arbitrary analytics properties remain user data; do not put secrets into free-text event names or property values. HTTP failures expose status and recovery guidance rather than vendor text that could echo a credential.\n",
|
|
4
4
|
"instructionsHeading": "Account instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": " endpoint. A project created in another residency is not reachable from here at all, so an empty result may mean wrong connector rather than no data.",
|
|
7
|
-
"guide_1": "\n\n- Start with `List-Organizations`, then supply the returned organization id where the live `Get-Projects` schema requires it. Resolve the project and workspace, then read `Get-Business-Context` before interpreting events or metrics.\n- Resolve ids before acting; never guess one. `Get-Projects` yields the project id every other call is scoped by, and `List-Dashboards`, `List-Cohorts`, `List-Metrics`, `List-Experiments`, and `List-Feature-Flags` yield the ids their `Get-`, `Update-`, and `Delete-` counterparts expect.\n- Discover names with `Get-Events`, `List-Properties`, and `Get-Property-Values`; do not guess event or property spelling.\n- `Get-Business-Context` requires either `project_id` or `organization_id`. Its schema marks both optional, but the hosted tool rejects a call with neither.\n- `Get-Property-Values` requires `properties` or the deprecated `property` alias. Event property values also require `event`; prefer `properties` and never send both property forms with conflicting values.\n- `List-Properties` accepts `names` or `query`, never both. Use exact `names` for known properties and `query` for substring discovery.\n- One analysis is one `execute_code` program: fetch `Get-Query-Schema` once, run every `Run-Query` of the analysis in that program, and return the reduced table. The schema is tens of kilobytes and the same for every report type, so re-fetching it per query buys nothing. Never return raw `Run-Query` output.\n- Insights, funnels, and retention answer aggregate questions. A per-user ordered event timeline, or a sequence question such as \"event A with no later event B\", is not answerable with `Run-Query` in a reasonable number of calls, and this hosted catalog has no per-`distinct_id` event timeline — `Get-User-Replays-Data` covers one user's replays with their events only where session replay is enabled and present. If the deployment exposes a Mixpanel export or activity-feed connector, use that; if it does not, tell the user the question is out of reach here rather than approximating it with hourly buckets and hundreds of empty rows.\n- `false` on a boolean property may be an absent property: Mixpanel renders a missing value as `false` in boolean breakdowns, and server-imported events often lack client-side properties entirely. Confirm the property is present with `List-Properties` or `Get-Property-Values` before treating `false` as a signal, and say when a conclusion rests on that ambiguity.\n- Breakdown responses nest `$overall` and per-segment series objects. Flatten to one row per complete breakdown combination inside `execute_code` before returning, and drop `$overall` unless the question asks for the total.\n- Use `Get-Report` when the request names an existing saved report. Use `Run-Query` for a new question.\n- Dashboard row and cell ids are opaque. Read the full layout before editing and preserve grouping and order. Report cells need a project-local `query_id`; duplicate within the same project when possible. Cross-project reconstruction needs new queries in the target project.\n- Query ids can expire. Build the dashboard promptly and rerun a query if its id is rejected as expired; no fixed TTL is guaranteed.\n- This account's tool list is not a fixed set. Mixpanel gates parts of its MCP catalog by plan and beta enrollment — experiments, feature flags, session replay, and issue triage are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- Mixpanel meters MCP traffic per user per hour, shared with everything else that credential does. Reuse discovery results within a run and avoid speculative fan-out.\n- An `auth_required` failure means this connector's Mixpanel 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 Mixpanel's own words instead — read it rather than re-authorizing.\n- Treat every create, update, edit, merge, dismiss, duplicate, or delete operation as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n",
|
|
8
|
-
"api": "\n\n- This connection uses Connecta's REST tools with a service account. Hosted MCP is the default `mixpanel()` mode; `via: \"api\"` explicitly selects REST. Declare two connection ids when both are useful. The operator pastes `username:secret` into the API connection, which sends ordinary HTTP Basic, unlike hosted MCP's `Bearer Basic` scheme. OAuth and static headers are refused in API mode.\n- One connection stays in one residency region, across all projects the account can reach. US uses `mixpanel.com` for query/app, `data.mixpanel.com` for export and `api.mixpanel.com` for ingestion. EU uses `eu.mixpanel.com`, `data-eu.mixpanel.com` and `api-eu.mixpanel.com`. India uses `in.mixpanel.com`, `data-in.mixpanel.com` and `api-in.mixpanel.com`. Wrong residency can look like missing data; do not interpret an empty response without checking the project region.\n- Start with `list_projects`, which returns redacted account metadata from `/api/app/me`. Its public reference does not specify the response schema. Resolve ids from that metadata or project settings, never guess one. Every project-scoped tool accepts `project_id`; an omitted id uses `defaultProjectId` when configured. An explicit id overrides that default and still requires service-account membership. Projects with Data Views may also require `workspace_id
|
|
7
|
+
"guide_1": "\n\n- Start with `List-Organizations`, then supply the returned organization id where the live `Get-Projects` schema requires it. Resolve the project and workspace, then read `Get-Business-Context` before interpreting events or metrics.\n- Resolve ids before acting; never guess one. `Get-Projects` yields the project id every other call is scoped by, and `List-Dashboards`, `List-Cohorts`, `List-Metrics`, `List-Experiments`, and `List-Feature-Flags` yield the ids their `Get-`, `Update-`, and `Delete-` counterparts expect.\n- Discover names with `Get-Events`, `List-Properties`, and `Get-Property-Values`; do not guess event or property spelling.\n- `Get-Business-Context` requires either `project_id` or `organization_id`. Its schema marks both optional, but the hosted tool rejects a call with neither.\n- `Get-Property-Values` requires `properties` or the deprecated `property` alias. Event property values also require `event`; prefer `properties` and never send both property forms with conflicting values.\n- `List-Properties` accepts `names` or `query`, never both. Use exact `names` for known properties and `query` for substring discovery.\n- One analysis is one `execute_code` program: fetch `Get-Query-Schema` once, run every `Run-Query` of the analysis in that program, and return the reduced table. The schema is tens of kilobytes and the same for every report type, so re-fetching it per query buys nothing. Never return raw `Run-Query` output.\n- Insights, funnels, and retention answer aggregate questions. A per-user ordered event timeline, or a sequence question such as \"event A with no later event B\", is not answerable with `Run-Query` in a reasonable number of calls, and this hosted catalog has no per-`distinct_id` event timeline — `Get-User-Replays-Data` covers one user's replays with their events only where session replay is enabled and present. If the deployment exposes a Mixpanel export or activity-feed connector, use that; if it does not, tell the user the question is out of reach here rather than approximating it with hourly buckets and hundreds of empty rows.\n- `false` on a boolean property may be an absent property: Mixpanel renders a missing value as `false` in boolean breakdowns, and server-imported events often lack client-side properties entirely. Confirm the property is present with `List-Properties` or `Get-Property-Values` before treating `false` as a signal, and say when a conclusion rests on that ambiguity.\n- Breakdown responses nest `$overall` and per-segment series objects. Flatten to one row per complete breakdown combination inside `execute_code` before returning, and drop `$overall` unless the question asks for the total.\n- Use `Get-Report` when the request names an existing saved report. Use `Run-Query` for a new question.\n- Dashboard row and cell ids are opaque. Read the full layout before editing and preserve grouping and order. Report cells need a project-local `query_id`; duplicate within the same project when possible. Cross-project reconstruction needs new queries in the target project.\n- Query ids can expire. Build the dashboard promptly and rerun a query if its id is rejected as expired; no fixed TTL is guaranteed.\n- This account's tool list is not a fixed set. Mixpanel gates parts of its MCP catalog by plan and beta enrollment — experiments, feature flags, session replay, and issue triage are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- Mixpanel meters MCP traffic per user per hour, shared with everything else that credential does. Reuse discovery results within a run and avoid speculative fan-out.\n- An `auth_required` failure means this connector's Mixpanel 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 Mixpanel's own words instead — read it rather than re-authorizing.\n- Treat every create, update, edit, rename, fill, merge, dismiss, duplicate, or delete operation as a write. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n",
|
|
8
|
+
"api": "\n\n- This connection uses Connecta's REST tools with a service account. Hosted MCP is the default `mixpanel()` mode; `via: \"api\"` explicitly selects REST. Declare two connection ids when both are useful. The operator pastes `username:secret` into the API connection, which sends ordinary HTTP Basic, unlike hosted MCP's `Bearer Basic` scheme. OAuth and static headers are refused in API mode.\n- One connection stays in one residency region, across all projects the account can reach. US uses `mixpanel.com` for query/app, `data.mixpanel.com` for export and `api.mixpanel.com` for ingestion. EU uses `eu.mixpanel.com`, `data-eu.mixpanel.com` and `api-eu.mixpanel.com`. India uses `in.mixpanel.com`, `data-in.mixpanel.com` and `api-in.mixpanel.com`. Wrong residency can look like missing data; do not interpret an empty response without checking the project region.\n- Start with `list_projects`, which returns redacted account metadata from `/api/app/me`. Its public reference does not specify the response schema. Resolve ids from that metadata or project settings, never guess one. Every project-scoped tool accepts `project_id`; an omitted id uses `defaultProjectId` when configured. An explicit id overrides that default and still requires service-account membership. Projects with Data Views may also require `workspace_id`, which the query and report tools accept; `export_events` and `import_events` do not.\n- Use REST for raw event export, profile queries and individual event sequences. Use hosted MCP for schema discovery, business context, aggregate analytics and dashboard work. Discover exact event and property names there before filtering raw data.\n- Export has no documented cursor. Begin with a short date range and an event or `where` filter. Every export returns `truncated` and a notice. A true flag means events may be missing at the byte or event cap; narrow ranges or filters, rather than inferring \"no later event B\" from a sample. Dates are inclusive. Mixpanel documents UTC for newer projects and the project timezone for older projects; check project settings around the January 2023 transition. Event timestamps default to seconds, or milliseconds with `time_in_ms`.\n- `get_profile_activity` sorts a user's returned events by timestamp, but `/stream/query` is in maintenance mode and has no documented pagination/completeness guarantee. For absence or sequence claims use complete, filtered raw export and sort/reduce inside `execute_code`.\n- `query_profiles` is a read-only POST to the Query API's `/engage`, distinct from ingestion `/engage` profile mutations. Pagination is vendor-owned: there is no documented caller `page_size`. Keep the same selector and output properties, pass the returned `session_id`, and increment `page` while `page.hasMore` is true. Do not treat `total` as the page length. `raw: true` preserves the redacted vendor envelope when needed.\n- Saved Insights uses `bookmark_id` from a report URL. Saved Funnels uses `funnel_id`, discoverable with `mixpanel_api_read` on `/api/query/funnels/list`. The documented Retention endpoint takes events and date parameters, not a saved-report id; default/birth retention also requires `born_event`. Use hosted MCP for an existing saved Retention report; `query_retention` recomputes retention from events and dates. Reduce nested report results before returning them.\n- Retention `interval` is capped at 90 only for `unit: \"day\"`, including the default when unit is omitted. Week and month intervals have no documented 90-unit cap. Both named and generic retention reads enforce the day limit.\n- `mixpanel_api_search` and `mixpanel_api_details` inspect the reviewed operation subset locally. `mixpanel_api_read` permits GET only on those operations, with fixed residency hosts and no caller URL or headers. It cannot reach service-account administration, arbitrary app APIs, profile mutation, JQL or ingestion. Generic export retains the named export's byte/event bounds. Use named tools for Engage and import.\n- `import_events` is an additive write and requires an Owner or Admin service account on the target project. Connecta enforces 2000 events, 10MB per batch, less than 1MB per event and Mixpanel's documented property limits. Supply stable `$insert_id` values and use strict validation. A 400 can still ingest the valid events; never retry a whole batch automatically. Inspect the target and failed records through a trusted operator workflow before deciding what to send again.\n- Profile writes are deferred. The official ingestion `/engage` contract requires `$token` in each update; service-account Basic alone does not satisfy it, and the service-account reference documents no project-token retrieval operation. Profile delete and other irreversible mutations are absent.\n- Query permits 60 queries per hour and 5 concurrent queries per project. Export permits 60 per hour, 3 per second and 100 concurrent queries. Ingestion permits 2GB uncompressed JSON per minute or about 30000 events per second. The API connection conservatively shares a 60-call/hour, 5-concurrent default across all tools and projects; it is a per-runtime approximation, not a distributed quota or a per-second export limiter. Operators may override `callAdmission`, especially for import-heavy deployments or multiple runtimes sharing an account. A vendor 429 still wins; respect `Retry-After`.\n- Successful responses recursively redact credential-named properties and bearer URLs. Arbitrary analytics properties remain user data; do not put secrets into free-text event names or property values. HTTP failures expose status and recovery guidance rather than vendor text that could echo a credential.\n"
|
|
9
9
|
}
|
|
10
10
|
};
|
|
@@ -5,7 +5,7 @@ export const skill = {
|
|
|
5
5
|
"fragments": {
|
|
6
6
|
"oauth_head": "# Notion MCP usage\n\nOfficial MCP interface: tool names, descriptions, argument schemas, and result\nschemas come from Notion's live server. Connecta preserves that catalog and\napplies release-reviewed classification. Reviewed writes stay writes even when Notion claims they only read.\n\nWorkspace purpose: ",
|
|
7
7
|
"oauth": "\n\n- This connection acts as its OAuth-authorized user and reaches what that user\n can. It holds no integration token. An internal integration (bot) is a\n separate connector with `auth: { type: \"token\" }` under another id; never\n switch to it to recover a failed call here.\n- Discover the live catalog before assuming a tool exists. Notion can gate\n tools by workspace, account, client, and rollout independently of Connecta.\n `notion-get-tool-access` reports what the workspace's plan allows.\n- Start broad discovery with `notion-search`, then use `notion-fetch` on\n the exact page, database, data source, or object before changing it.\n- Use the live input schema as the contract. Notion owns these MCP schemas;\n the REST schemas of Connecta's token connector do not apply to MCP tools with\n similar names.\n- The next-step card tools return guidance without enabling access. Call\n `notion-check-mcp-next-steps` only when fetch instructs you to, at most\n once. Call `notion-show-advanced-analysis-next-steps` once at turn end\n only when a multi-source query requires the full version.\n- Session and agent tools can launch asynchronous work. Read session state and\n events before sending another message, waiting, or stopping a session.\n- File uploads and attachment tools create durable workspace state. Keep\n downloads inside the requested task and do not expose signed attachment URLs.\n `notion-get-file-download-urls` returns signed download URLs that expire\n after a few minutes. Use them only for the requested download and keep\n stable `notion-file-block://` references in edited or copied Markdown.\n `notion-download-skill` also returns a signed, expiring archive URL.\n The token connector's REST redaction does not apply to hosted tools;\n do not repeat signed download URLs in replies.\n- `notion-upload-skill` is a write for both actions. `prepare` returns a\n credential-bearing upload URL and headers with a ten-minute expiry;\n keep them inside the upload flow and out of replies. `complete` replaces\n the target page's title, Description, body, and Files.\n- Connecta's generic REST tools (`notion_api_*`) and the `integration_*`\n helpers exist only on a token connector.\n- An `auth_required` failure means this connector's OAuth grant is missing or\n expired. Run `authorize_connector` for this connector id, then retry.\n",
|
|
8
|
-
"key": "\n## Identity and tools\n\nThis is Connecta's REST connector to `api.notion.com`, acting as one internal\nintegration (a bot) with an operator-managed integration token. It reaches\nexactly the pages and data sources shared with that integration, and the\nintegration's capabilities decide which calls succeed: comment and\nuser-information capabilities are off by default. It never acts as a person.\nHosted Notion MCP is a separate OAuth connector.\n\n- Named tools cover the common path and flatten Notion's payloads:\n `integration_search` (titles), `integration_get_page` (properties),\n `integration_get_page_content` (blocks as text),\n `integration_get_data_source_schema`, and `integration_query_data_source`\n (rows). `integration_create_page` and `integration_append_blocks` author\n Notion's rich-text and block shapes from plain text, Markdown, or checklists.\n- Four generic tools reach every other operation in the pinned API index.\n `notion_api_search` finds operations, not content. `notion_api_details`\n returns one operation's parameters. `notion_api_read` calls GETs plus the\n read-only POST queries: search, data source and meeting note queries, and\n agent, session, and session event queries. `notion_api_write` calls every\n other POST, PATCH, and DELETE with `method` and `path` stated.\n- Every generic call is checked against the index before it is sent. An\n unknown path returns the nearest operations, and an unknown or missing\n parameter returns `validation.issues` with the accepted names. Nothing\n reached Notion, so fix the call from that answer.\n- The generic set replaces the old one-operation tools. Read a database\n container with `GET /v1/databases/{database_id}`, users with `GET /v1/users`\n or `GET /v1/users/me`, and comments with `GET /v1/comments`. Update\n properties, trash or restore (`in_trash`) with `PATCH /v1/pages/{page_id}`,\n and comment with `POST /v1/comments` (`rich_text` or `markdown`).\n- Refused by design: Notion's OAuth token endpoints (`/v1/oauth/*`) and\n multipart file sends. Import a file with `POST /v1/file_uploads` and\n `mode: \"external_url\"` instead.\n- Notion-minted file links lose their whole query and fragment wherever\n they appear, including page Markdown, async task results, and error text.\n Connecta matches the actual authority: `file.notion.so`,\n `secure.notion-static.com`, `notionusercontent.com` and their subdomains,\n or the `prod-files-secure` and `secure.notion-static.com` S3 buckets,\n virtual-hosted or exactly the first path segment on an S3 endpoint.\n Escaped schemes, slashes, and query/fragment openers are recognized. The\n withheld span runs through the next whitespace, quote, or angle bracket;\n trailing closing punctuation comes back. A returned file URL names the\n file but cannot download it. Links at other authorities, including proxy\n URLs with a Notion URL inside their path or query, come back as written.\n AWS `X-Amz-*` and Google `X-Goog-*` signing `Signature`, `Credential`, and\n `Security-Token` values are withheld at any host.\n- A write whose Notion file link carries the exact `[redacted]` placeholder\n in its own query or fragment, plain or up to four layers encoded, is\n refused before dispatch. Each inline Markdown destination is checked,\n including adjacent links and images, balanced or escaped parentheses, and\n leading spaces, tabs, or one line ending. A marker after the destination's\n close is ordinary text. To keep a file, leave its block in place or\n re-upload it with `POST /v1/file_uploads`; to edit Markdown around media,\n match `old_str` on nearby text rather than the withheld URL.\n- Each string is scanned separately. Links split across rich-text runs,\n encoded hostnames or signing-parameter names, bare hosts without `//`,\n and userinfo before a Notion host are outside this scan's threat model.\n The shared URL rule still removes userinfo from whole URL fields.\n Credential-named fields read as `\"[redacted]\"`, a database property named\n like a credential (`API token`) included. Failures of the OAuth, file upload, bot user, and\n agent operations carry Notion's code, not its message.\n- Not here: the hosted MCP's user identity, its search across connected\n apps, page duplication, team and skill search, attachment downloads, and\n page-to-skill conversion. Those need an OAuth connector.\n\n## Databases contain data sources\n\n- The id in a Notion database URL is a **database id**.\n `GET /v1/databases/{database_id}` through `notion_api_read` lists its\n `data_sources`, usually one. `integration_search` returns data sources\n directly and skips that step.\n- `integration_get_data_source_schema` and `integration_query_data_source`\n take the **data_source_id**, and so does `integration_create_page` when\n adding a row.\n\nSo the sequence for \"find rows in this database\" is the database read, then\n`integration_get_data_source_schema`, then `integration_query_data_source`.\n\n## Reading\n\n- `integration_get_page` returns properties. To read or summarize a whole\n body, prefer `notion_api_read` `GET /v1/pages/{page_id}/markdown`: one\n request returns every nested level as Markdown (headings, lists, to-dos,\n toggles, tables, code, links, mentions, equations). A huge page comes back\n with `truncated: true` and the unread blocks in `unknown_block_ids`; read\n each id as a `page_id` on the same path.\n- `integration_get_page_content` returns the body as flat blocks with ids,\n for quoting a block or appending after one. Nested blocks (toggles, list\n children, table rows) need `depth`, and each level multiplies requests. A\n deep read stops at an internal ceiling and reports `truncated: true` rather\n than spending the whole rate-limit budget.\n- Notion truncates `title`, `rich_text`, `relation`, and `people` at 25\n entries. `integration_get_page` reports each in `truncated_properties` as\n `{ name, id }`; read the complete value with `notion_api_read`\n `GET /v1/pages/{page_id}/properties/{property_id}` and follow its cursor.\n- Every named read projects Notion's payload to ids, plain text, and\n flattened property values. Pass `raw: true` for the full response, with\n the same value-safety redaction, when a specific field is missing. It is\n much larger. Narrow\n `integration_query_data_source` and `integration_get_page` with `properties`\n when the goal is fewer fields. This also narrows the request to Notion with\n repeated `filter_properties` URL parameters, shrinking the response before\n Connecta reads it. The title property is always fetched so the projected\n `title` stays populated. Page retrieval allows at most 100 distinct filters,\n including the title id. A larger selection fetches all properties and still\n projects only the selected names locally. Data source queries have no\n documented filter count limit. `raw: true` returns the upstream response,\n filtered when those filters fit and full when page retrieval falls back.\n Omit `properties` for the full response on either tool.\n `integration_get_page_content` with `raw: true` returns one level and ignores\n `depth`.\n\n## Writing content\n\n`integration_append_blocks` adds children and nothing else. It cannot move,\nreorder, or replace an existing block, and a block appended through the API can\nnever be relocated by it afterwards. Get the position right the first time with\n`position` and `after_block_id`. Notion caps one call at 100 blocks. Text\nlonger than Notion's 2,000-character run limit is split into runs for you.\n\nFor formatted content, write Markdown to a page with `notion_api_write`\n`PATCH /v1/pages/{page_id}/markdown`. Append with\n`{\"type\":\"insert_content\",\"insert_content\":{\"content\":\"## Notes\\n- one\",\"position\":{\"type\":\"end\"}}}`\n(`start` prepends). Edit text in place with `update_content`, whose\n`content_updates` are `{ old_str, new_str }` pairs copied exactly from the\npage's Markdown, and `replace_content` swaps the whole page for `new_str`.\nNotion refuses an edit that would delete a child page or database with a\n`validation_error` naming them. Removing ordinary text needs no flag; to delete\nthose children, set `allow_deleting_content: true` inside the command body\n(`{\"type\":\"update_content\",\"update_content\":{\"content_updates\":[…],\"allow_deleting_content\":true}}`),\nand leave it out unless the task is to delete them. `integration_create_page`\ntakes the same Markdown for a new page's body.\n\n## Pagination\n\n- Named list tools take `page_size` (max 100) and return `has_more` with\n `next_cursor`; pass it back as `start_cursor`.\n- Generic results arrive as `{ status, data, page? }`. On a list,\n `page.next` is the cursor and `page.param` is `start_cursor`: send it in\n `query` for a GET, and in `body` when `page.in` is `body`, as on the POST\n queries. Pass `select` dot paths relative to `data`, such as `results.id`;\n `page` survives any `select`.\n- A view query is created with `notion_api_write`\n `POST /v1/views/{view_id}/queries`, a write because Notion stores it until\n `expires_at`. Its first page names the continuation in `page.path`: read\n `GET /v1/views/{view_id}/queries/{query_id}` with `start_cursor` in `query`.\n- Meeting-note queries have no cursor. `limit` caps at 50, and\n `page.hasMore` without `page.next` means more notes exist: narrow the\n filter (for example by `created_time`) rather than paging.\n- A data source query stops at 10,000 rows. `integration_query_data_source`\n then returns `incomplete: true`, and a generic query `page.hasMore` without\n `page.next`: narrow the filter rather than trusting the count.\n- Cursors are opaque: never parse or construct one. Follow pages inside\n `execute_code` and reduce there.\n\n## Failures worth reading carefully\n\n- **404** means \"no such object\" *or* \"not shared with this integration\", and\n Notion will not say which. Never conclude a page was deleted from it; check\n that the page is shared with the integration in Notion.\n- **403** is not an expired token. The integration is missing a capability\n (comment capabilities are off by default), the object was never shared, or\n the workspace is past its plan's block limit; Notion's message says which.\n Re-authorizing cannot fix it; an operator must change it in Notion.\n- **429** carries a retry window. Notion's [request limits](https://developers.notion.com/reference/request-limits)\n allow 600 requests a minute per connection on Business and Enterprise and\n 180 on other plans, plus a workspace-wide limit shared by all connections.\n Connecta defaults to 180 calls a minute, three at a time, per runtime.\n A token connector's optional `callAdmission` replaces that whole policy;\n account for other runtime traffic and tools that make multiple HTTP requests.\n Wait out the window rather than retrying immediately. A 429 that says the\n integration's API access is blocked is not retryable.\n- **5xx on a write** may still have been saved. When Notion names the\n object it committed, the failure says so: read that object instead of\n repeating the write. Otherwise check the target before trying again.\n",
|
|
8
|
+
"key": "\n## Identity and tools\n\nThis is Connecta's REST connector to `api.notion.com`, acting as one internal\nintegration (a bot) with an operator-managed integration token. It reaches\nexactly the pages and data sources shared with that integration, and the\nintegration's capabilities decide which calls succeed: comment and\nuser-information capabilities are off by default. It never acts as a person.\nHosted Notion MCP is a separate OAuth connector.\n\n- Named tools cover the common path and flatten Notion's payloads:\n `integration_search` (titles), `integration_get_page` (properties),\n `integration_get_page_content` (blocks as text),\n `integration_get_data_source_schema`, and `integration_query_data_source`\n (rows). `integration_create_page` and `integration_append_blocks` author\n Notion's rich-text and block shapes from plain text, Markdown, or checklists.\n- Four generic tools reach every other operation in the pinned API index.\n `notion_api_search` finds operations, not content. `notion_api_details`\n returns one operation's parameters. `notion_api_read` calls GETs plus the\n read-only POST queries: search, data source and meeting note queries, and\n agent, session, and session event queries. `notion_api_write` calls every\n other POST, PATCH, and DELETE with `method` and `path` stated.\n- Every generic call is checked against the index before it is sent. An\n unknown path returns the nearest operations, and an unknown or missing\n parameter returns `validation.issues` with the accepted names. Nothing\n reached Notion, so fix the call from that answer.\n- The generic set replaces the old one-operation tools. Read a database\n container with `GET /v1/databases/{database_id}`, users with `GET /v1/users`\n or `GET /v1/users/me`, and comments with `GET /v1/comments`. Update\n properties, trash or restore (`in_trash`) with `PATCH /v1/pages/{page_id}`,\n and comment with `POST /v1/comments` (`rich_text` or `markdown`).\n- Refused by design: Notion's OAuth token endpoints (`/v1/oauth/*`) and\n multipart file sends. Import a file with `POST /v1/file_uploads` and\n `mode: \"external_url\"` instead.\n- Notion-minted file links lose their whole query and fragment wherever\n they appear, including page Markdown, async task results, and error text.\n Connecta matches the actual authority: `file.notion.so`,\n `secure.notion-static.com`, `notionusercontent.com` and their subdomains,\n or the `prod-files-secure` and `secure.notion-static.com` S3 buckets,\n virtual-hosted or exactly the first path segment on an S3 endpoint.\n Escaped schemes, slashes, and query/fragment openers are recognized. The\n withheld span runs through the next whitespace, quote, or angle bracket;\n trailing closing punctuation comes back. A returned file URL names the\n file but cannot download it. Links at other authorities, including proxy\n URLs with a Notion URL inside their path or query, come back as written.\n AWS `X-Amz-*` and Google `X-Goog-*` signing `Signature`, `Credential`, and\n `Security-Token` values are withheld at any host.\n- A write whose Notion file link carries the exact `[redacted]` placeholder\n in its own query or fragment, plain or up to four layers encoded, is\n refused before dispatch. Each inline Markdown destination is checked,\n including adjacent links and images, balanced or escaped parentheses, and\n leading spaces, tabs, or one line ending. A marker after the destination's\n close is ordinary text. To keep a file, leave its block in place or\n re-upload it with `POST /v1/file_uploads`; to edit Markdown around media,\n match `old_str` on nearby text rather than the withheld URL.\n- Each string is scanned separately. Links split across rich-text runs,\n encoded hostnames or signing-parameter names, bare hosts without `//`,\n and userinfo before a Notion host are outside this scan's threat model.\n The shared URL rule still removes userinfo from whole URL fields.\n Credential-named fields read as `\"[redacted]\"`, a database property named\n like a credential (`API token`) included. Failures of the OAuth, file upload, bot user, and\n agent operations carry Notion's code, not its message.\n- Not here: the hosted MCP's user identity, its search across connected\n apps, page duplication, team and skill search, attachment downloads, and\n page-to-skill conversion. Those need an OAuth connector.\n\n## Databases contain data sources\n\n- The id in a Notion database URL is a **database id**.\n `GET /v1/databases/{database_id}` through `notion_api_read` lists its\n `data_sources`, usually one. `integration_search` returns data sources\n directly and skips that step.\n- `integration_get_data_source_schema` and `integration_query_data_source`\n take the **data_source_id**, and `integration_create_page` takes it as\n `parent_data_source_id` when adding a row.\n\nSo the sequence for \"find rows in this database\" is the database read, then\n`integration_get_data_source_schema`, then `integration_query_data_source`.\n\n## Reading\n\n- `integration_get_page` returns properties. To read or summarize a whole\n body, prefer `notion_api_read` `GET /v1/pages/{page_id}/markdown`: one\n request returns every nested level as Markdown (headings, lists, to-dos,\n toggles, tables, code, links, mentions, equations). A huge page comes back\n with `truncated: true` and the unread blocks in `unknown_block_ids`; read\n each id as a `page_id` on the same path.\n- `integration_get_page_content` returns the body as flat blocks with ids,\n for quoting a block or appending after one. Nested blocks (toggles, list\n children, table rows) need `depth`, and each level multiplies requests. A\n deep read stops at an internal ceiling and reports `truncated: true` rather\n than spending the whole rate-limit budget.\n- Notion truncates `title`, `rich_text`, `relation`, and `people` at 25\n entries. `integration_get_page` reports each in `truncated_properties` as\n `{ name, id }`; read the complete value with `notion_api_read`\n `GET /v1/pages/{page_id}/properties/{property_id}` and follow its cursor.\n- Every named read projects Notion's payload to ids, plain text, and\n flattened property values. Pass `raw: true` for the full response, with\n the same value-safety redaction, when a specific field is missing. It is\n much larger. Narrow\n `integration_query_data_source` and `integration_get_page` with `properties`\n when the goal is fewer fields. This also narrows the request to Notion with\n repeated `filter_properties` URL parameters, shrinking the response before\n Connecta reads it. The title property is always fetched so the projected\n `title` stays populated. Page retrieval allows at most 100 distinct filters,\n including the title id. A larger selection fetches all properties and still\n projects only the selected names locally. Data source queries have no\n documented filter count limit. `raw: true` returns the upstream response,\n filtered when those filters fit and full when page retrieval falls back.\n Omit `properties` for the full response on either tool.\n `integration_get_page_content` with `raw: true` returns one level and ignores\n `depth`.\n\n## Writing content\n\n`integration_append_blocks` adds children and nothing else. It cannot move,\nreorder, or replace an existing block, and a block appended through the API can\nnever be relocated by it afterwards. Get the position right the first time with\n`position` and `after_block_id`. Notion caps one call at 100 blocks. Text\nlonger than Notion's 2,000-character run limit is split into runs for you.\n\nFor formatted content, write Markdown to a page with `notion_api_write`\n`PATCH /v1/pages/{page_id}/markdown`. Append with\n`{\"type\":\"insert_content\",\"insert_content\":{\"content\":\"## Notes\\n- one\",\"position\":{\"type\":\"end\"}}}`\n(`start` prepends). Edit text in place with `update_content`, whose\n`content_updates` are `{ old_str, new_str }` pairs copied exactly from the\npage's Markdown, and `replace_content` swaps the whole page for `new_str`.\nNotion refuses an edit that would delete a child page or database with a\n`validation_error` naming them. Removing ordinary text needs no flag; to delete\nthose children, set `allow_deleting_content: true` inside the command body\n(`{\"type\":\"update_content\",\"update_content\":{\"content_updates\":[…],\"allow_deleting_content\":true}}`),\nand leave it out unless the task is to delete them. `integration_create_page`\ntakes the same Markdown for a new page's body.\n\n## Pagination\n\n- Named list tools take `page_size` (max 100) and return `has_more` with\n `next_cursor`; pass it back as `start_cursor`.\n- Generic results arrive as `{ status, data, page? }`. On a list,\n `page.next` is the cursor and `page.param` is `start_cursor`: send it in\n `query` for a GET, and in `body` when `page.in` is `body`, as on the POST\n queries. Pass `select` dot paths relative to `data`, such as `results.id`;\n `page` survives any `select`.\n- A view query is created with `notion_api_write`\n `POST /v1/views/{view_id}/queries`, a write because Notion stores it until\n `expires_at`. Its first page names the continuation in `page.path`: read\n `GET /v1/views/{view_id}/queries/{query_id}` with `start_cursor` in `query`.\n- Meeting-note queries have no cursor. `limit` caps at 50, and\n `page.hasMore` without `page.next` means more notes exist: narrow the\n filter (for example by `created_time`) rather than paging.\n- A data source query stops at 10,000 rows. `integration_query_data_source`\n then returns `incomplete: true`, and a generic query `page.hasMore` without\n `page.next`: narrow the filter rather than trusting the count.\n- Cursors are opaque: never parse or construct one. Follow pages inside\n `execute_code` and reduce there.\n\n## Failures worth reading carefully\n\n- **404** means \"no such object\" *or* \"not shared with this integration\", and\n Notion will not say which. Never conclude a page was deleted from it; check\n that the page is shared with the integration in Notion.\n- **403** is not an expired token. The integration is missing a capability\n (comment capabilities are off by default), the object was never shared, or\n the workspace is past its plan's block limit; Notion's message says which.\n Re-authorizing cannot fix it; an operator must change it in Notion.\n- **429** carries a retry window. Notion's [request limits](https://developers.notion.com/reference/request-limits)\n allow 600 requests a minute per connection on Business and Enterprise and\n 180 on other plans, plus a workspace-wide limit shared by all connections.\n Connecta defaults to 180 calls a minute, three at a time, per runtime.\n A token connector's optional `callAdmission` replaces that whole policy;\n account for other runtime traffic and tools that make multiple HTTP requests.\n Wait out the window rather than retrying immediately. A 429 that says the\n integration's API access is blocked is not retryable.\n- **5xx on a write** may still have been saved. When Notion names the\n object it committed, the failure says so: read that object instead of\n repeating the write. Otherwise check the target before trying again.\n",
|
|
9
9
|
"shared": "\n## Notion's data model\n\n- A database is a container; its rows and schema live in a *data source*\n inside it. Queries, schema reads, and new rows take the data source, and\n Notion rejects a database id in its place.\n- Property names in filters, sorts, and writes must match the schema exactly,\n including case. Read the schema before composing one.\n- A `status` write must use an existing option name; inventing one fails.\n A new `select` or `multi_select` name does not fail: when the connection\n can write to the data source, Notion adds it to the schema as a new\n option. Use the options the schema lists unless a schema change is meant.\n- Writes **replace** a property. Sending one item to a `multi_select` or\n `relation` drops the others, so send the complete intended value.\n- `rollup`, `formula`, `unique_id`, and the created and edited fields are\n computed. They cannot be written.\n- A data source's title column is rarely called \"title\". Read its name from\n the schema before creating or renaming a row.\n- Notion has **no idempotency key**. A retried page create or comment makes a\n duplicate. Confirm with a search before repeating a write that may have\n partially succeeded.\n"
|
|
10
10
|
}
|
|
11
11
|
};
|
|
@@ -10,7 +10,7 @@ import { skill } from "./skill.generated.js";
|
|
|
10
10
|
* nothing to install. Every tool below is hand-written. Overflow's OpenAPI
|
|
11
11
|
* document is drift evidence only — `src/providers/overflow/drift.json`
|
|
12
12
|
* records the operations the named tools touch, and
|
|
13
|
-
* `
|
|
13
|
+
* `pnpm run providers:check --provider overflow` compares them with the
|
|
14
14
|
* published document without a credential. That document is served only by
|
|
15
15
|
* staging (`server.stage.overflow.co/api/docs/openapi.json`; production's
|
|
16
16
|
* equivalent answers 404), so the check reads staging's contract and assumes
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "\n\n## Money, units, and status\n\nOverflow does not use one money unit, so read the unit per field:\n\n- Fields named `…InCents` are cents: deposits and their line items and\n totals, refunds, chargebacks, and the authorize body.\n- Recurring-gift `amount` is dollars: Overflow's create and update bodies\n say so, and its subscription reads carry the same field.\n- Contribution `amount` states no unit in Overflow's schema. Check one known\n gift before reporting totals.\n- `status` is per asset type. A gift is final at `PAID_OUT` (cash),\n `CONFIRMED` (manual cash, crypto), `CONTRIBUTION_RECEIVED` or `BILLED`\n (stock), and `CONTRIBUTION_RECEIVED` (DAF). `statusBucket` filters by\n Overflow's coarser PENDING / CONFIRMED / FAILED / CANCELED.\n- There is no funds endpoint. A gift reports its campaign and subcampaign;\n `fundId` and `subFundId` appear only in the payment-authorize body.\n- Poll for changes with `minimumUpdatedDate`; `minimumInitiatedDate` is\n when the donor gave.\n\n## Reconcile a deposit\n\n`list_deposits` → `get_deposit_summary` for totals → `get_deposit` for the\nline items, whose `referenceId` names the contribution, refund, or\nchargeback. Contributions also carry their `depositId`.\n\n## Donor data is sensitive\n\nGiving history and donor contact details are personal data. Reduce inside\n`execute_code` — aggregate, count, or keep ids — and return names, emails,\nphones, or addresses only when the task needs them.
|
|
3
|
+
"content": "\n\n## Money, units, and status\n\nOverflow does not use one money unit, so read the unit per field:\n\n- Fields named `…InCents` are cents: deposits and their line items and\n totals, refunds, chargebacks, and the authorize body.\n- Recurring-gift `amount` is dollars: Overflow's create and update bodies\n say so, and its subscription reads carry the same field.\n- Contribution `amount` states no unit in Overflow's schema. Check one known\n gift before reporting totals.\n- `status` is per asset type. A gift is final at `PAID_OUT` (cash),\n `CONFIRMED` (manual cash, crypto), `CONTRIBUTION_RECEIVED` or `BILLED`\n (stock), and `CONTRIBUTION_RECEIVED` (DAF). `statusBucket` filters by\n Overflow's coarser PENDING / CONFIRMED / FAILED / CANCELED.\n- There is no funds endpoint. A gift reports its campaign and subcampaign;\n `fundId` and `subFundId` appear only in the payment-authorize body.\n- Poll for changes with `minimumUpdatedDate`; `minimumInitiatedDate` is\n when the donor gave.\n\n## Reconcile a deposit\n\n`list_deposits` → `get_deposit_summary` for totals → `get_deposit` for the\nline items, whose `referenceId` names the contribution, refund, or\nchargeback. Contributions also carry their `depositId`.\n\n## Donor data is sensitive\n\nGiving history and donor contact details are personal data. Reduce inside\n`execute_code` — aggregate, count, or keep ids — and return names, emails,\nphones, or addresses only when the task needs them. Contribution rows carry\nonly the donor's id and name; `list_donors` includes email but omits phone and\naddress; `get_donor` is the deliberate full-contact lookup.\n\n## Writes go through `overflow_api_mutate`\n\nEvery Overflow write moves money or changes a donor record, so none has a\nnamed tool. Each is one approval-gated `overflow_api_mutate` call with\nOverflow's documented body:\n\n- `POST /contributions` charges a saved payment method. Its `amount`\n states no unit; confirm it against a known gift of the same donor before\n charging. `POST /payments/authorize` takes `amountInCents` and\n authorizes a new method (`0` only saves it).\n- `POST /contributions/{id}/initiate-refund` (under $1,000 only).\n- `POST` / `PATCH` / `DELETE /subscriptions/{donorId}[/{subscriptionId}]`\n creates, changes, or cancels a recurring gift. Its `amount` is dollars,\n not cents: a $50.00 monthly gift is `amount: 50`. Cancel takes a\n `cancellationReason` body.\n- `POST /donors`, `PATCH /donors/{id}`.\n\nResolve every id first — donors, payment methods (`list_payment_methods`),\ncampaigns, locations — and never guess one. A write that fails with HTTP 5xx\nmay still have happened: read before retrying.\n\n## Hatches, pages, and limits\n\n- `overflow_api_get` reaches the reads without a named tool: single refunds,\n chargebacks, campaigns, locations, webhooks, and recurring gifts\n (`/subscriptions/{donorId}/{id}`), and Tap devices, groups, and\n destinations (`/tap/devices`, `/tap/groups`, `/tap/destinations`). Paths\n are below `/api/v3`; array filters repeat a `name[]` query pair.\n- Lists page by number: pass `page` while `page.hasMore` is true.\n `list_webhook_event_logs` pages by cursor instead.\n- Overflow allows 120 requests per minute per API client. This connection\n admits the same per runtime, four at a time, which approximates rather than\n enforces it; a `rate_limited` failure carries the wait.\n",
|
|
4
4
|
"instructionsHeading": "Nonprofit instructions",
|
|
5
5
|
"fragments": {
|
|
6
|
-
"guide_0": "\n\n## Money, units, and status\n\nOverflow does not use one money unit, so read the unit per field:\n\n- Fields named `…InCents` are cents: deposits and their line items and\n totals, refunds, chargebacks, and the authorize body.\n- Recurring-gift `amount` is dollars: Overflow's create and update bodies\n say so, and its subscription reads carry the same field.\n- Contribution `amount` states no unit in Overflow's schema. Check one known\n gift before reporting totals.\n- `status` is per asset type. A gift is final at `PAID_OUT` (cash),\n `CONFIRMED` (manual cash, crypto), `CONTRIBUTION_RECEIVED` or `BILLED`\n (stock), and `CONTRIBUTION_RECEIVED` (DAF). `statusBucket` filters by\n Overflow's coarser PENDING / CONFIRMED / FAILED / CANCELED.\n- There is no funds endpoint. A gift reports its campaign and subcampaign;\n `fundId` and `subFundId` appear only in the payment-authorize body.\n- Poll for changes with `minimumUpdatedDate`; `minimumInitiatedDate` is\n when the donor gave.\n\n## Reconcile a deposit\n\n`list_deposits` → `get_deposit_summary` for totals → `get_deposit` for the\nline items, whose `referenceId` names the contribution, refund, or\nchargeback. Contributions also carry their `depositId`.\n\n## Donor data is sensitive\n\nGiving history and donor contact details are personal data. Reduce inside\n`execute_code` — aggregate, count, or keep ids — and return names, emails,\nphones, or addresses only when the task needs them.
|
|
6
|
+
"guide_0": "\n\n## Money, units, and status\n\nOverflow does not use one money unit, so read the unit per field:\n\n- Fields named `…InCents` are cents: deposits and their line items and\n totals, refunds, chargebacks, and the authorize body.\n- Recurring-gift `amount` is dollars: Overflow's create and update bodies\n say so, and its subscription reads carry the same field.\n- Contribution `amount` states no unit in Overflow's schema. Check one known\n gift before reporting totals.\n- `status` is per asset type. A gift is final at `PAID_OUT` (cash),\n `CONFIRMED` (manual cash, crypto), `CONTRIBUTION_RECEIVED` or `BILLED`\n (stock), and `CONTRIBUTION_RECEIVED` (DAF). `statusBucket` filters by\n Overflow's coarser PENDING / CONFIRMED / FAILED / CANCELED.\n- There is no funds endpoint. A gift reports its campaign and subcampaign;\n `fundId` and `subFundId` appear only in the payment-authorize body.\n- Poll for changes with `minimumUpdatedDate`; `minimumInitiatedDate` is\n when the donor gave.\n\n## Reconcile a deposit\n\n`list_deposits` → `get_deposit_summary` for totals → `get_deposit` for the\nline items, whose `referenceId` names the contribution, refund, or\nchargeback. Contributions also carry their `depositId`.\n\n## Donor data is sensitive\n\nGiving history and donor contact details are personal data. Reduce inside\n`execute_code` — aggregate, count, or keep ids — and return names, emails,\nphones, or addresses only when the task needs them. Contribution rows carry\nonly the donor's id and name; `list_donors` includes email but omits phone and\naddress; `get_donor` is the deliberate full-contact lookup.\n\n## Writes go through `overflow_api_mutate`\n\nEvery Overflow write moves money or changes a donor record, so none has a\nnamed tool. Each is one approval-gated `overflow_api_mutate` call with\nOverflow's documented body:\n\n- `POST /contributions` charges a saved payment method. Its `amount`\n states no unit; confirm it against a known gift of the same donor before\n charging. `POST /payments/authorize` takes `amountInCents` and\n authorizes a new method (`0` only saves it).\n- `POST /contributions/{id}/initiate-refund` (under $1,000 only).\n- `POST` / `PATCH` / `DELETE /subscriptions/{donorId}[/{subscriptionId}]`\n creates, changes, or cancels a recurring gift. Its `amount` is dollars,\n not cents: a $50.00 monthly gift is `amount: 50`. Cancel takes a\n `cancellationReason` body.\n- `POST /donors`, `PATCH /donors/{id}`.\n\nResolve every id first — donors, payment methods (`list_payment_methods`),\ncampaigns, locations — and never guess one. A write that fails with HTTP 5xx\nmay still have happened: read before retrying.\n\n## Hatches, pages, and limits\n\n- `overflow_api_get` reaches the reads without a named tool: single refunds,\n chargebacks, campaigns, locations, webhooks, and recurring gifts\n (`/subscriptions/{donorId}/{id}`), and Tap devices, groups, and\n destinations (`/tap/devices`, `/tap/groups`, `/tap/destinations`). Paths\n are below `/api/v3`; array filters repeat a `name[]` query pair.\n- Lists page by number: pass `page` while `page.hasMore` is true.\n `list_webhook_event_logs` pages by cursor instead.\n- Overflow allows 120 requests per minute per API client. This connection\n admits the same per runtime, four at a time, which approximates rather than\n enforces it; a `rate_limited` failure carries the wait.\n"
|
|
7
7
|
}
|
|
8
8
|
};
|
|
@@ -22,7 +22,7 @@ import { skill } from "./skill.generated.js";
|
|
|
22
22
|
* documentation graph listing every version (`/<app>/v2/documentation`).
|
|
23
23
|
* `src/providers/planning-center/drift.json` records each endpoint a named
|
|
24
24
|
* tool calls, digested at the version pinned below, and
|
|
25
|
-
* `
|
|
25
|
+
* `pnpm run providers:check --provider planning-center` reports a touched
|
|
26
26
|
* contract that moved and a newly published version of any pinned product.
|
|
27
27
|
* Both are evidence only: no tool here is generated from either document.
|
|
28
28
|
*/
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Planning Center usage\n\nPlanning Center Online church management — People, Services, Groups, Check-Ins, Calendar, Registrations, Giving, Publishing, Webhooks.\n\n## Identity first\n\n- A person id is the join key across every product: the id `search_people` returns is the `personId` for Services schedules, Groups memberships, Giving donations, and Check-Ins. Resolve ids with list tools; never guess one.\n- A person id that used to resolve and now answers `not_found` was most likely merged. Read `/people/v2/person_mergers` with `where[person_to_remove_id]` and follow `person_to_keep_id`.\n- `get_me` names the user this token acts as. Every read and write runs with that user's permissions, product by product; a `provider_permission_denied` means an administrator must grant that user access to the product or record.\n\n## Results and paging\n\n- Records keep Planning Center's own snake_case attribute names; a relationship appears as `<relationship>_id`. Pass `raw: true` for the untouched JSON:API resources and `included
|
|
3
|
+
"content": "# Planning Center usage\n\nPlanning Center Online church management — People, Services, Groups, Check-Ins, Calendar, Registrations, Giving, Publishing, Webhooks.\n\n## Identity first\n\n- A person id is the join key across every product: the id `search_people` returns is the person id (`personId` argument, `person_id` field) for Services schedules, Groups memberships, Giving donations, and Check-Ins. Resolve ids with list tools; never guess one.\n- A person id that used to resolve and now answers `not_found` was most likely merged. Read `/people/v2/person_mergers` with `where[person_to_remove_id]` and follow `person_to_keep_id`.\n- `get_me` names the user this token acts as. Every read and write runs with that user's permissions, product by product; a `provider_permission_denied` means an administrator must grant that user access to the product or record.\n\n## Results and paging\n\n- Records keep Planning Center's own snake_case attribute names; a relationship appears as `<relationship>_id`. Pass `raw: true` for the untouched JSON:API resources and `included`; `get_me` and `list_webhook_subscriptions` have no raw mode.\n- Lists page by offset: `perPage` up to 100, then pass `page.nextOffset` back as `offset` while `page.hasMore` is true. `page.totalCount` answers \"how many\" without paging — request `perPage: 1`.\n- A bare date in a date filter is the organization's local day; pass a full timestamp for exact bounds. Name filters are exact; end one with `%` for a prefix match.\n- `get_plan` fetches the plan, its items, and its team in one call and sets `itemsTruncated`/`teamMembersTruncated` past 100 rows; page the rest with `pco_api_get`.\n\n## What each product lets the API write\n\n- People: full read/write. `update_person` with `status: inactive` archives reversibly; a DELETE is permanent and only reachable through `pco_api_mutate`. `run_list` can fire list automations.\n- Services: full read/write. A plan's dates come from its plan times. Scheduling needs a team.\n- Groups: memberships and group settings are writable; groups cannot be created, and events and attendance are read-only.\n- Check-Ins and Registrations: read-only. Nobody can be checked in or registered through the API.\n- Calendar: events, instances, and times are read-only; tags, resources, rooms, and folders are writable through `pco_api_mutate`.\n- Giving: the named tools only read, amounts in integer cents. Creating or editing donations, refunds, and committing a batch move money and go through `pco_api_mutate`.\n- Publishing and the `api` product (organization, OAuth applications, personal access tokens) have no named tools; use the hatches.\n\n## The hatches\n\n- `pco_api_get` reaches any GET; `pco_api_mutate` any POST, PATCH, or DELETE, always approval-gated. Paths start `/<app>/v2`, exactly as Planning Center documents them; the connector owns host, auth, content type, and the `X-PCO-API-Version` header.\n- Bodies are JSON:API documents: `{\"data\":{\"type\":\"Email\",\"attributes\":{...}}}`. `null` clears an attribute; an omitted key is left alone.\n- Each product is pinned to a reviewed API version: people 2026-06-04, services 2018-11-01, groups 2023-07-10, check-ins 2025-05-28, calendar 2026-06-22, registrations 2025-05-01, giving 2019-10-18, publishing 2024-03-25, webhooks 2022-10-20, api 2026-09-24. Pass `version` only for an endpoint those versions lack.\n- Query parameters are name/value pairs with literal brackets: `where[status]`, `include`, `order`, `filter`, `fields[Person]`. Every list response's `meta` names the `can_query_by`, `can_order_by`, and `can_include` values for that endpoint; an unknown `where` key is ignored rather than refused.\n- File uploads go to a separate host (upload.planningcenteronline.com), which this connection does not reach.\n\n## Rate limits\n\nPlanning Center allows 100 requests per 20 seconds per user, shared by every integration running as that user. This connection admits 100 calls per 20 seconds, five at a time, per runtime — an approximation, not a guarantee. A `rate_limited` failure carries Planning Center's wait. Filter server-side and use `totalCount` rather than paging to count.\n",
|
|
4
4
|
"instructionsHeading": "Organization instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"heading": "# Planning Center usage\n\nPlanning Center Online church management — ",
|
|
7
7
|
"purpose": " — for: ",
|
|
8
|
-
"identity": "\n\n## Identity first\n\n- A person id is the join key across every product: the id `search_people` returns is the `personId` for ",
|
|
9
|
-
"conventions": ". Resolve ids with list tools; never guess one.\n- A person id that used to resolve and now answers `not_found` was most likely merged. Read `/people/v2/person_mergers` with `where[person_to_remove_id]` and follow `person_to_keep_id`.\n- `get_me` names the user this token acts as. Every read and write runs with that user's permissions, product by product; a `provider_permission_denied` means an administrator must grant that user access to the product or record.\n\n## Results and paging\n\n- Records keep Planning Center's own snake_case attribute names; a relationship appears as `<relationship>_id`. Pass `raw: true` for the untouched JSON:API resources and `included
|
|
8
|
+
"identity": "\n\n## Identity first\n\n- A person id is the join key across every product: the id `search_people` returns is the person id (`personId` argument, `person_id` field) for ",
|
|
9
|
+
"conventions": ". Resolve ids with list tools; never guess one.\n- A person id that used to resolve and now answers `not_found` was most likely merged. Read `/people/v2/person_mergers` with `where[person_to_remove_id]` and follow `person_to_keep_id`.\n- `get_me` names the user this token acts as. Every read and write runs with that user's permissions, product by product; a `provider_permission_denied` means an administrator must grant that user access to the product or record.\n\n## Results and paging\n\n- Records keep Planning Center's own snake_case attribute names; a relationship appears as `<relationship>_id`. Pass `raw: true` for the untouched JSON:API resources and `included`; `get_me` and `list_webhook_subscriptions` have no raw mode.\n- Lists page by offset: `perPage` up to 100, then pass `page.nextOffset` back as `offset` while `page.hasMore` is true. `page.totalCount` answers \"how many\" without paging — request `perPage: 1`.\n- A bare date in a date filter is the organization's local day; pass a full timestamp for exact bounds. Name filters are exact; end one with `%` for a prefix match.\n",
|
|
10
10
|
"plans": "- `get_plan` fetches the plan, its items, and its team in one call and sets `itemsTruncated`/`teamMembersTruncated` past 100 rows; page the rest with `pco_api_get`.\n",
|
|
11
11
|
"writes": "\n## What each product lets the API write\n\n",
|
|
12
12
|
"people": "- People: full read/write. `update_person` with `status: inactive` archives reversibly; a DELETE is permanent and only reachable through `pco_api_mutate`. `run_list` can fire list automations.\n",
|
|
@@ -25,6 +25,6 @@ export const skill = {
|
|
|
25
25
|
"pins": ". Pass `version` only for an endpoint those versions lack.\n- Query parameters are name/value pairs with literal brackets: `where[status]`, `include`, `order`, `filter`, `fields[Person]`. Every list response's `meta` names the `can_query_by`, `can_order_by`, and `can_include` values for that endpoint; an unknown `where` key is ignored rather than refused.\n- File uploads go to a separate host (upload.planningcenteronline.com), which this connection does not reach.\n",
|
|
26
26
|
"disabled": "- This deployment disables some products, and the hatches refuse their paths before sending. While any product is disabled they also refuse People lists, list results, rules, and conditions, by path or `include`, because a list's conditions can draw on any product; webhook event and delivery payloads; and any change to an existing webhook subscription, whose id does not name its product. A new subscription must name a reachable product's event.\n",
|
|
27
27
|
"limits": "\n## Rate limits\n\nPlanning Center allows 100 requests per 20 seconds per user, shared by every integration running as that user. This connection admits 100 calls per 20 seconds, five at a time, per runtime — an approximation, not a guarantee. A `rate_limited` failure carries Planning Center's wait. Filter server-side and use `totalCount` rather than paging to count.\n",
|
|
28
|
-
"published": "# Planning Center usage\n\nPlanning Center Online church management — People, Services, Groups, Check-Ins, Calendar, Registrations, Giving, Publishing, Webhooks.\n\n## Identity first\n\n- A person id is the join key across every product: the id `search_people` returns is the `personId` for Services schedules, Groups memberships, Giving donations, and Check-Ins. Resolve ids with list tools; never guess one.\n- A person id that used to resolve and now answers `not_found` was most likely merged. Read `/people/v2/person_mergers` with `where[person_to_remove_id]` and follow `person_to_keep_id`.\n- `get_me` names the user this token acts as. Every read and write runs with that user's permissions, product by product; a `provider_permission_denied` means an administrator must grant that user access to the product or record.\n\n## Results and paging\n\n- Records keep Planning Center's own snake_case attribute names; a relationship appears as `<relationship>_id`. Pass `raw: true` for the untouched JSON:API resources and `included
|
|
28
|
+
"published": "# Planning Center usage\n\nPlanning Center Online church management — People, Services, Groups, Check-Ins, Calendar, Registrations, Giving, Publishing, Webhooks.\n\n## Identity first\n\n- A person id is the join key across every product: the id `search_people` returns is the person id (`personId` argument, `person_id` field) for Services schedules, Groups memberships, Giving donations, and Check-Ins. Resolve ids with list tools; never guess one.\n- A person id that used to resolve and now answers `not_found` was most likely merged. Read `/people/v2/person_mergers` with `where[person_to_remove_id]` and follow `person_to_keep_id`.\n- `get_me` names the user this token acts as. Every read and write runs with that user's permissions, product by product; a `provider_permission_denied` means an administrator must grant that user access to the product or record.\n\n## Results and paging\n\n- Records keep Planning Center's own snake_case attribute names; a relationship appears as `<relationship>_id`. Pass `raw: true` for the untouched JSON:API resources and `included`; `get_me` and `list_webhook_subscriptions` have no raw mode.\n- Lists page by offset: `perPage` up to 100, then pass `page.nextOffset` back as `offset` while `page.hasMore` is true. `page.totalCount` answers \"how many\" without paging — request `perPage: 1`.\n- A bare date in a date filter is the organization's local day; pass a full timestamp for exact bounds. Name filters are exact; end one with `%` for a prefix match.\n- `get_plan` fetches the plan, its items, and its team in one call and sets `itemsTruncated`/`teamMembersTruncated` past 100 rows; page the rest with `pco_api_get`.\n\n## What each product lets the API write\n\n- People: full read/write. `update_person` with `status: inactive` archives reversibly; a DELETE is permanent and only reachable through `pco_api_mutate`. `run_list` can fire list automations.\n- Services: full read/write. A plan's dates come from its plan times. Scheduling needs a team.\n- Groups: memberships and group settings are writable; groups cannot be created, and events and attendance are read-only.\n- Check-Ins and Registrations: read-only. Nobody can be checked in or registered through the API.\n- Calendar: events, instances, and times are read-only; tags, resources, rooms, and folders are writable through `pco_api_mutate`.\n- Giving: the named tools only read, amounts in integer cents. Creating or editing donations, refunds, and committing a batch move money and go through `pco_api_mutate`.\n- Publishing and the `api` product (organization, OAuth applications, personal access tokens) have no named tools; use the hatches.\n\n## The hatches\n\n- `pco_api_get` reaches any GET; `pco_api_mutate` any POST, PATCH, or DELETE, always approval-gated. Paths start `/<app>/v2`, exactly as Planning Center documents them; the connector owns host, auth, content type, and the `X-PCO-API-Version` header.\n- Bodies are JSON:API documents: `{\"data\":{\"type\":\"Email\",\"attributes\":{...}}}`. `null` clears an attribute; an omitted key is left alone.\n- Each product is pinned to a reviewed API version: people 2026-06-04, services 2018-11-01, groups 2023-07-10, check-ins 2025-05-28, calendar 2026-06-22, registrations 2025-05-01, giving 2019-10-18, publishing 2024-03-25, webhooks 2022-10-20, api 2026-09-24. Pass `version` only for an endpoint those versions lack.\n- Query parameters are name/value pairs with literal brackets: `where[status]`, `include`, `order`, `filter`, `fields[Person]`. Every list response's `meta` names the `can_query_by`, `can_order_by`, and `can_include` values for that endpoint; an unknown `where` key is ignored rather than refused.\n- File uploads go to a separate host (upload.planningcenteronline.com), which this connection does not reach.\n\n## Rate limits\n\nPlanning Center allows 100 requests per 20 seconds per user, shared by every integration running as that user. This connection admits 100 calls per 20 seconds, five at a time, per runtime — an approximation, not a guarantee. A `rate_limited` failure carries Planning Center's wait. Filter server-side and use `totalCount` rather than paging to count.\n"
|
|
29
29
|
}
|
|
30
30
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "\n- Inspect result framing before accessing fields. A text result is not automatically an object; parse it only if the returned text is valid JSON. Do not iterate fields that the response does not contain.\n- Resolve ids before acting; never guess one. `list-projects` yields the `project_id` every project-scoped call takes. `list-apps`, `list-products`, `list-entitlements`, `list-offerings`, `list-paywalls`, `list-audiences`, and `list-customers` yield the ids their `get-`, `update-`, `archive-`, and `delete-` counterparts expect. A plausible-looking id belongs to another project or to nobody.\n- Customers are addressed by the app user id your SDK set, not by an internal key. Find one with `list-customers` before `get-customer`, and carry the id it returned unchanged.\n- Customer and subscription objects are large, and a customer's history is larger. Page with the cursor the list returned rather than raising the page size, and reduce inside `execute_code` — select the fields the question needs and return those, not the whole object.\n- Whether a customer should have access is `gives_access` on each subscription from `list-subscriptions`, which RevenueCat calls the authoritative flag. `status` and `expires_date` describe the store-side state and disagree with it during grace periods, billing retries, and promotional grants — answer access questions from `gives_access` and say which subscription it came from.\n- `get-chart-data` is the metrics path: read `get-chart-options-schema` for the chart you want before calling it, rather than guessing an option name. `get-overview-metrics` and `get-revenue-metric` answer the summary questions in one call.\n- `create-paywall-ai` and `edit-paywall-ai` are asynchronous. Poll the task id they return with `get-paywall-ai-task` rather than assuming the work finished when the call returned.\n- Store changes ride the plan workflow, not the deprecated direct tools: `create-product-store-state-plan`, then `plan-product-store-state-plan`, then `apply-product-store-state-plan`. Every step past the create is asynchronous — read the plan back with `get-product-store-state-plan` between steps instead of assuming the last one finished.\n- This connection's tool list is not a fixed set. RevenueCat gates parts of its MCP catalog by plan, platform, and beta enrollment — paywall AI editing, benchmarks, experiments, virtual currencies, and the account-billing tools are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- `render-paywall-screenshot` is unclassified on purpose because RevenueCat's reference gives it no access column. An explicit, uncontradicted live read annotation qualifies it as a read; otherwise it fails closed as a write. The public reference alone cannot establish that rendering has no side effects.\n- RevenueCat meters API v2 per minute and per domain, and the domains differ: 480 requests per minute for customer information and virtual currencies, 60 for project configuration and audiences, 25 for charts and metrics. It answers a breach with `429`, a `Retry-After` header, and a `backoff_ms` field. Back off on that rather than retrying immediately, and expect chart sweeps to hit the ceiling long before customer reads do.\n- Treat every create, update, archive,
|
|
3
|
+
"content": "\n- Inspect result framing before accessing fields. A text result is not automatically an object; parse it only if the returned text is valid JSON. Do not iterate fields that the response does not contain.\n- Resolve ids before acting; never guess one. `list-projects` yields the `project_id` every project-scoped call takes. `list-apps`, `list-products`, `list-entitlements`, `list-offerings`, `list-paywalls`, `list-audiences`, and `list-customers` yield the ids their `get-`, `update-`, `archive-`, and `delete-` counterparts expect. A plausible-looking id belongs to another project or to nobody.\n- Customers are addressed by the app user id your SDK set, not by an internal key. Find one with `list-customers` before `get-customer`, and carry the id it returned unchanged.\n- Customer and subscription objects are large, and a customer's history is larger. Page with the cursor the list returned rather than raising the page size, and reduce inside `execute_code` — select the fields the question needs and return those, not the whole object.\n- Whether a customer should have access is `gives_access` on each subscription from `list-subscriptions`, which RevenueCat calls the authoritative flag. `status` and `expires_date` describe the store-side state and disagree with it during grace periods, billing retries, and promotional grants — answer access questions from `gives_access` and say which subscription it came from.\n- `get-chart-data` is the metrics path: read `get-chart-options-schema` for the chart you want before calling it, rather than guessing an option name. `get-overview-metrics` and `get-revenue-metric` answer the summary questions in one call.\n- `create-paywall-ai` and `edit-paywall-ai` are asynchronous. Poll the task id they return with `get-paywall-ai-task` rather than assuming the work finished when the call returned.\n- Store changes ride the plan workflow, not the deprecated direct tools: `create-product-store-state-plan`, then `plan-product-store-state-plan`, then `apply-product-store-state-plan`. Every step past the create is asynchronous — read the plan back with `get-product-store-state-plan` between steps instead of assuming the last one finished.\n- This connection's tool list is not a fixed set. RevenueCat gates parts of its MCP catalog by plan, platform, and beta enrollment — paywall AI editing, benchmarks, experiments, virtual currencies, and the account-billing tools are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- `render-paywall-screenshot` is unclassified on purpose because RevenueCat's reference gives it no access column. An explicit, uncontradicted live read annotation qualifies it as a read; otherwise it fails closed as a write. The public reference alone cannot establish that rendering has no side effects.\n- RevenueCat meters API v2 per minute and per domain, and the domains differ: 480 requests per minute for customer information and virtual currencies, 60 for project configuration and audiences, 25 for charts and metrics. It answers a breach with `429`, a `Retry-After` header, and a `backoff_ms` field. Back off on that rather than retrying immediately, and expect chart sweeps to hit the ceiling long before customer reads do.\n- Treat every operation other than a `get-` or `list-` read as a write, including create, update, archive, duplicate, validate, start, attach, detach, delete, publish, grant, assign, and submit. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n- An `auth_required` failure means this connector's RevenueCat authorization is missing or expired: run `authorize_connector` for this connector id, then retry the same call unchanged. A rejected argument, a permission gap, or a plan restriction comes back in RevenueCat's own words instead — read it rather than re-authorizing.\n# RevenueCat usage\n\nAccount-scoped connection: this OAuth session reaches every RevenueCat project the account can see. Connector purpose: \n\nCall `list-projects` first and carry the exact `project_id` it returned into every project-scoped call. Connecta does not pick a project, and the connector id, title, and purpose are routing hints rather than proof of which project a call will land in. If more than one project fits the request, stop and ask; never guess a `project_id`.\n. RevenueCat secret API keys are project-wide, so this key reaches exactly one project and nothing outside it. A second project is a second connector with its own key and its own id — never a `project_id` argument pointed somewhere else.\n\nConfirm the project on first use: `list-projects` returns the one project this key can see, and its `project_id` is the one every project-scoped call takes. An empty or unexpected result means wrong connector, not missing data.\n\nA RevenueCat secret key is issued read-only or write-enabled, and connecta cannot tell which this one is. It does not filter writes for a read-only key: every write is offered, reaches RevenueCat, and fails there in RevenueCat's own words. Read that refusal as \"this key cannot write\" rather than as a bad argument, and route the write to a connector configured with a write-enabled key.\n",
|
|
4
4
|
"instructionsHeading": "Project instructions",
|
|
5
5
|
"fragments": {
|
|
6
|
-
"guide_0": "\n- Inspect result framing before accessing fields. A text result is not automatically an object; parse it only if the returned text is valid JSON. Do not iterate fields that the response does not contain.\n- Resolve ids before acting; never guess one. `list-projects` yields the `project_id` every project-scoped call takes. `list-apps`, `list-products`, `list-entitlements`, `list-offerings`, `list-paywalls`, `list-audiences`, and `list-customers` yield the ids their `get-`, `update-`, `archive-`, and `delete-` counterparts expect. A plausible-looking id belongs to another project or to nobody.\n- Customers are addressed by the app user id your SDK set, not by an internal key. Find one with `list-customers` before `get-customer`, and carry the id it returned unchanged.\n- Customer and subscription objects are large, and a customer's history is larger. Page with the cursor the list returned rather than raising the page size, and reduce inside `execute_code` — select the fields the question needs and return those, not the whole object.\n- Whether a customer should have access is `gives_access` on each subscription from `list-subscriptions`, which RevenueCat calls the authoritative flag. `status` and `expires_date` describe the store-side state and disagree with it during grace periods, billing retries, and promotional grants — answer access questions from `gives_access` and say which subscription it came from.\n- `get-chart-data` is the metrics path: read `get-chart-options-schema` for the chart you want before calling it, rather than guessing an option name. `get-overview-metrics` and `get-revenue-metric` answer the summary questions in one call.\n- `create-paywall-ai` and `edit-paywall-ai` are asynchronous. Poll the task id they return with `get-paywall-ai-task` rather than assuming the work finished when the call returned.\n- Store changes ride the plan workflow, not the deprecated direct tools: `create-product-store-state-plan`, then `plan-product-store-state-plan`, then `apply-product-store-state-plan`. Every step past the create is asynchronous — read the plan back with `get-product-store-state-plan` between steps instead of assuming the last one finished.\n- This connection's tool list is not a fixed set. RevenueCat gates parts of its MCP catalog by plan, platform, and beta enrollment — paywall AI editing, benchmarks, experiments, virtual currencies, and the account-billing tools are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- `render-paywall-screenshot` is unclassified on purpose because RevenueCat's reference gives it no access column. An explicit, uncontradicted live read annotation qualifies it as a read; otherwise it fails closed as a write. The public reference alone cannot establish that rendering has no side effects.\n- RevenueCat meters API v2 per minute and per domain, and the domains differ: 480 requests per minute for customer information and virtual currencies, 60 for project configuration and audiences, 25 for charts and metrics. It answers a breach with `429`, a `Retry-After` header, and a `backoff_ms` field. Back off on that rather than retrying immediately, and expect chart sweeps to hit the ceiling long before customer reads do.\n- Treat every create, update, archive,
|
|
6
|
+
"guide_0": "\n- Inspect result framing before accessing fields. A text result is not automatically an object; parse it only if the returned text is valid JSON. Do not iterate fields that the response does not contain.\n- Resolve ids before acting; never guess one. `list-projects` yields the `project_id` every project-scoped call takes. `list-apps`, `list-products`, `list-entitlements`, `list-offerings`, `list-paywalls`, `list-audiences`, and `list-customers` yield the ids their `get-`, `update-`, `archive-`, and `delete-` counterparts expect. A plausible-looking id belongs to another project or to nobody.\n- Customers are addressed by the app user id your SDK set, not by an internal key. Find one with `list-customers` before `get-customer`, and carry the id it returned unchanged.\n- Customer and subscription objects are large, and a customer's history is larger. Page with the cursor the list returned rather than raising the page size, and reduce inside `execute_code` — select the fields the question needs and return those, not the whole object.\n- Whether a customer should have access is `gives_access` on each subscription from `list-subscriptions`, which RevenueCat calls the authoritative flag. `status` and `expires_date` describe the store-side state and disagree with it during grace periods, billing retries, and promotional grants — answer access questions from `gives_access` and say which subscription it came from.\n- `get-chart-data` is the metrics path: read `get-chart-options-schema` for the chart you want before calling it, rather than guessing an option name. `get-overview-metrics` and `get-revenue-metric` answer the summary questions in one call.\n- `create-paywall-ai` and `edit-paywall-ai` are asynchronous. Poll the task id they return with `get-paywall-ai-task` rather than assuming the work finished when the call returned.\n- Store changes ride the plan workflow, not the deprecated direct tools: `create-product-store-state-plan`, then `plan-product-store-state-plan`, then `apply-product-store-state-plan`. Every step past the create is asynchronous — read the plan back with `get-product-store-state-plan` between steps instead of assuming the last one finished.\n- This connection's tool list is not a fixed set. RevenueCat gates parts of its MCP catalog by plan, platform, and beta enrollment — paywall AI editing, benchmarks, experiments, virtual currencies, and the account-billing tools are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here.\n- `render-paywall-screenshot` is unclassified on purpose because RevenueCat's reference gives it no access column. An explicit, uncontradicted live read annotation qualifies it as a read; otherwise it fails closed as a write. The public reference alone cannot establish that rendering has no side effects.\n- RevenueCat meters API v2 per minute and per domain, and the domains differ: 480 requests per minute for customer information and virtual currencies, 60 for project configuration and audiences, 25 for charts and metrics. It answers a breach with `429`, a `Retry-After` header, and a `backoff_ms` field. Back off on that rather than retrying immediately, and expect chart sweeps to hit the ceiling long before customer reads do.\n- Treat every operation other than a `get-` or `list-` read as a write, including create, update, archive, duplicate, validate, start, attach, detach, delete, publish, grant, assign, and submit. For guest calls, classification, and routing, fetch `skills({ name: \"usage\" })`.\n- An `auth_required` failure means this connector's RevenueCat authorization is missing or expired: run `authorize_connector` for this connector id, then retry the same call unchanged. A rejected argument, a permission gap, or a plan restriction comes back in RevenueCat's own words instead — read it rather than re-authorizing.\n",
|
|
7
7
|
"guide_1": "# RevenueCat usage\n\nAccount-scoped connection: this OAuth session reaches every RevenueCat project the account can see. Connector purpose: ",
|
|
8
8
|
"guide_2": "\n\nCall `list-projects` first and carry the exact `project_id` it returned into every project-scoped call. Connecta does not pick a project, and the connector id, title, and purpose are routing hints rather than proof of which project a call will land in. If more than one project fits the request, stop and ask; never guess a `project_id`.\n",
|
|
9
9
|
"guide_3": ". RevenueCat secret API keys are project-wide, so this key reaches exactly one project and nothing outside it. A second project is a second connector with its own key and its own id — never a `project_id` argument pointed somewhere else.\n\nConfirm the project on first use: `list-projects` returns the one project this key can see, and its `project_id` is the one every project-scoped call takes. An empty or unexpected result means wrong connector, not missing data.\n\nA RevenueCat secret key is issued read-only or write-enabled, and connecta cannot tell which this one is. It does not filter writes for a read-only key: every write is offered, reaches RevenueCat, and fails there in RevenueCat's own words. Read that refusal as \"this key cannot write\" rather than as a bad argument, and route the write to a connector configured with a write-enabled key.\n"
|
|
@@ -28,7 +28,7 @@ import { skill } from "./skill.generated.js";
|
|
|
28
28
|
*
|
|
29
29
|
* Drift. Google publishes a credential-free Discovery document per API.
|
|
30
30
|
* `src/providers/sheets/drift.json` records the seven methods the tools
|
|
31
|
-
* call, and `
|
|
31
|
+
* call, and `pnpm run providers:check --provider sheets` reports a touched
|
|
32
32
|
* contract that moved or a method that stopped accepting the scope below.
|
|
33
33
|
*/
|
|
34
34
|
import { apiConnector as api, defined } from "../../connectors/api-connector.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "# Google Sheets usage\n\nActs as the signed-in person in their own Google Sheets through Workspace delegation: it reaches only spreadsheets they can open, starting from an id.\n\nConnection purpose: \n\n## Whose spreadsheets\n\nEvery call acts as the Workspace account deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument\nnames an account. A call with none mapped
|
|
3
|
+
"content": "# Google Sheets usage\n\nActs as the signed-in person in their own Google Sheets through Workspace delegation: it reaches only spreadsheets they can open, starting from an id.\n\nConnection purpose: \n\n## Whose spreadsheets\n\nEvery call acts as the Workspace account deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument\nnames an account. A call with none mapped fails `auth_required`; only an operator can change\nthat. An id that is\nunknown or not shared with this person fails `connector_call_failed` (or\n`provider_permission_denied` when Google refuses access) — Google does not\nsay which; check the id or ask the owner to share it.\n\n## Finding a spreadsheet\n\n- Sheets cannot list or search files. Find one by name with a Google Drive\n search, or take the id from its URL (between `/d/` and `/edit`).\n- `get_spreadsheet` first: sheet titles, ids, grid sizes, and named ranges,\n so ranges are named right rather than guessed.\n\n## Ranges\n\n- A1 notation. Quote a sheet title with spaces or punctuation:\n `'Q3 Budget'!A1:D20`, doubling any `'` inside it. A bare title is the\n whole sheet; `Sheet1!B:B` a whole column; a named range works by name.\n- `get_values` pages by whole rows under `maxCells` and `maxBytes`:\n page with `page.nextCursor` and the same spreadsheetId, ranges, and\n render options. A range with explicit rows (`A1:D500`, `B:D`) resumes\n where it stopped; a sheet title or named range is re-read and its earlier\n rows skipped, so `rowOffset` says how far into Google's echoed range the\n page starts.\n- A row too wide or too large for one page is refused, not cut: narrow the\n columns or lower `maxCellChars`. The default `maxBytes` is what\n `execute_code` can receive; raise it only for a direct `call_tool`\n read.\n- A cell over characters ends with a truncation marker;\n read it alone with `maxCellChars` for the rest. Reduce inside\n `execute_code` before returning a large read.\n\n## Writing\n\n- `valueInputOption` is required: `RAW` stores text as given;\n `USER_ENTERED` parses it as typed, so a leading `=` makes a formula. Use\n RAW for data from outside the spreadsheet.\n- In written rows, `null` leaves a cell as it was and `\"\"` empties it.\n- `append_values` inserts rows below the table and overwrites nothing;\n `update_values` and `batch_update_values` overwrite the cells they\n cover; `clear_values` empties values but keeps formatting.\n- `batch_update_spreadsheet` takes Google's own Request objects for\n everything else (formatting, sorting, deleting rows or sheets); it is\n atomic, always needs approval, and can destroy data. Replies too large\n to return come back cut to their kind, ids, and counts with a `note`;\n the requests still applied, so never send them again to see the rest.\n- Sheets has no revision check on writes: edits are last-writer-wins, so a\n person editing the same cells meanwhile is overwritten without warning.\n Read just before writing over anything a person may be editing.\n- Moving, sharing, or deleting a spreadsheet file is Drive's job.\n",
|
|
4
4
|
"instructionsHeading": "Connection instructions",
|
|
5
5
|
"fragments": {
|
|
6
6
|
"guide_0": "# Google Sheets usage\n\nActs as the signed-in person in their own Google Sheets through Workspace delegation: it reaches only spreadsheets they can open, starting from an id.\n\nConnection purpose: ",
|
|
7
|
-
"guide_1": "\n\n## Whose spreadsheets\n\nEvery call acts as the Workspace account deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument\nnames an account. A call with none mapped
|
|
7
|
+
"guide_1": "\n\n## Whose spreadsheets\n\nEvery call acts as the Workspace account deployment config maps the caller\nto; an agent acts as the owner of the pool it came through, unless the deployment fixes one shared account that every caller, agent or person, acts as. No argument\nnames an account. A call with none mapped fails `auth_required`; only an operator can change\nthat. An id that is\nunknown or not shared with this person fails `connector_call_failed` (or\n`provider_permission_denied` when Google refuses access) — Google does not\nsay which; check the id or ask the owner to share it.\n\n## Finding a spreadsheet\n\n- Sheets cannot list or search files. Find one by name with a Google Drive\n search, or take the id from its URL (between `/d/` and `/edit`).\n- `get_spreadsheet` first: sheet titles, ids, grid sizes, and named ranges,\n so ranges are named right rather than guessed.\n\n## Ranges\n\n- A1 notation. Quote a sheet title with spaces or punctuation:\n `'Q3 Budget'!A1:D20`, doubling any `'` inside it. A bare title is the\n whole sheet; `Sheet1!B:B` a whole column; a named range works by name.\n- `get_values` pages by whole rows under `maxCells` and `maxBytes`:\n page with `page.nextCursor` and the same spreadsheetId, ranges, and\n render options. A range with explicit rows (`A1:D500`, `B:D`) resumes\n where it stopped; a sheet title or named range is re-read and its earlier\n rows skipped, so `rowOffset` says how far into Google's echoed range the\n page starts.\n- A row too wide or too large for one page is refused, not cut: narrow the\n columns or lower `maxCellChars`. The default `maxBytes` is what\n `execute_code` can receive; raise it only for a direct `call_tool`\n read.\n- A cell over ",
|
|
8
8
|
"guide_2": " characters ends with a truncation marker;\n read it alone with `maxCellChars` for the rest. Reduce inside\n `execute_code` before returning a large read.\n\n## Writing\n\n- `valueInputOption` is required: `RAW` stores text as given;\n `USER_ENTERED` parses it as typed, so a leading `=` makes a formula. Use\n RAW for data from outside the spreadsheet.\n- In written rows, `null` leaves a cell as it was and `\"\"` empties it.\n- `append_values` inserts rows below the table and overwrites nothing;\n `update_values` and `batch_update_values` overwrite the cells they\n cover; `clear_values` empties values but keeps formatting.\n- `batch_update_spreadsheet` takes Google's own Request objects for\n everything else (formatting, sorting, deleting rows or sheets); it is\n atomic, always needs approval, and can destroy data. Replies too large\n to return come back cut to their kind, ids, and counts with a `note`;\n the requests still applied, so never send them again to see the rest.\n- Sheets has no revision check on writes: edits are last-writer-wins, so a\n person editing the same cells meanwhile is overwritten without warning.\n Read just before writing over anything a person may be editing.\n- Moving, sharing, or deleting a spreadsheet file is Drive's job.\n"
|
|
9
9
|
}
|
|
10
10
|
};
|
|
@@ -40,7 +40,7 @@ import { skill } from "./skill.generated.js";
|
|
|
40
40
|
*
|
|
41
41
|
* Drift. Google publishes a credential-free Discovery document per API.
|
|
42
42
|
* `src/providers/slides/drift.json` records the five methods the tools
|
|
43
|
-
* call, and `
|
|
43
|
+
* call, and `pnpm run providers:check --provider slides` reports a touched
|
|
44
44
|
* contract that moved or a method that stopped accepting the scope below.
|
|
45
45
|
*/
|
|
46
46
|
import { apiConnector as api, defined } from "../../connectors/api-connector.js";
|