@zackbart/connecta 0.33.0 → 0.34.1
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 +65 -0
- package/README.md +100 -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-drift.d.ts +30 -26
- package/dist/catalog-drift.js +169 -84
- package/dist/catalog-service.js +16 -2
- 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/connectors/api-connector.d.ts +3 -0
- package/dist/connectors/api-connector.js +5 -1
- package/dist/connectors/option-shapes.js +1 -1
- package/dist/connectors/remote-mcp.d.ts +2 -0
- package/dist/connectors/remote-mcp.js +5 -1
- package/dist/describe-config.d.ts +5 -1
- package/dist/describe-config.js +2 -0
- package/dist/execute.js +1 -11
- package/dist/fix-prompt.js +2 -2
- package/dist/index.js +1 -0
- package/dist/invocation.js +3 -2
- package/dist/membership.d.ts +2 -0
- package/dist/meta-tools.js +3 -2
- 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 +565 -0
- package/dist/operator-ui/connection-map.js +376 -0
- package/dist/operator-ui/endpoints.js +45 -16
- package/dist/operator-ui/fix-prompts.js +1 -1
- package/dist/operator-ui/generated.js +4 -4
- package/dist/operator-ui/health.js +52 -0
- package/dist/operator-ui/member-page.js +13 -28
- package/dist/operator-ui/overview.js +123 -0
- package/dist/operator-ui/view.d.ts +26 -22
- package/dist/operator-ui/view.js +130 -96
- package/dist/page-styles.js +5 -3
- package/dist/provider.d.ts +6 -1
- package/dist/provider.js +18 -1
- package/dist/providers/_shared/google/workspace.d.ts +9 -4
- package/dist/providers/_shared/google/workspace.js +7 -3
- package/dist/providers/basecamp/index.d.ts +2 -0
- package/dist/providers/breeze/index.d.ts +2 -0
- package/dist/providers/ccb/index.d.ts +2 -0
- package/dist/providers/ccb/index.js +2 -2
- package/dist/providers/cloudflare/index.d.ts +2 -0
- package/dist/providers/cloudflare/index.js +5 -1
- package/dist/providers/cloudflare/openapi.generated.js +3296 -3206
- package/dist/providers/cloudflare/rest.js +19 -0
- package/dist/providers/cloudflare/skill.generated.js +2 -2
- package/dist/providers/cloudflare/value-safety.js +51 -18
- 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/intercom/index.d.ts +24 -0
- package/dist/providers/intercom/index.js +121 -0
- package/dist/providers/intercom/skill.generated.js +8 -0
- package/dist/providers/linear/index.js +204 -19
- package/dist/providers/linear/skill.generated.js +2 -2
- package/dist/providers/mixpanel/index.d.ts +2 -0
- package/dist/providers/mixpanel/index.js +21 -17
- package/dist/providers/mixpanel/skill.generated.js +3 -3
- package/dist/providers/notion/index.d.ts +2 -0
- package/dist/providers/notion/index.js +1 -1
- package/dist/providers/notion/skill.generated.js +1 -1
- package/dist/providers/overflow/index.d.ts +2 -0
- package/dist/providers/overflow/index.js +1 -1
- package/dist/providers/overflow/skill.generated.js +2 -2
- package/dist/providers/planning-center/index.d.ts +2 -0
- package/dist/providers/planning-center/index.js +1 -1
- package/dist/providers/planning-center/skill.generated.js +4 -4
- package/dist/providers/revenuecat/index.d.ts +5 -3
- package/dist/providers/revenuecat/index.js +147 -7
- package/dist/providers/revenuecat/skill.generated.js +3 -3
- 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/stripe/index.d.ts +2 -0
- package/dist/providers/stripe/index.js +50 -30
- package/dist/providers/stripe/skill.generated.js +2 -1
- package/dist/providers/tithely/index.d.ts +2 -0
- package/dist/providers/tithely/index.js +1 -1
- package/dist/providers/vercel/index.d.ts +2 -0
- package/dist/providers/vercel/index.js +2 -2
- package/dist/providers/vercel/openapi.generated.js +391 -374
- package/dist/providers/vercel/skill.generated.js +2 -2
- package/dist/providers/vercel/value-safety.js +14 -4
- package/dist/registry.d.ts +10 -14
- package/dist/registry.js +77 -62
- package/dist/routes/mcp.js +20 -13
- package/dist/routes/oauth.js +2 -2
- package/dist/routes/ui-data.js +8 -2
- package/dist/skills.js +18 -2
- package/dist/storage/sql.js +5 -1
- package/dist/types.d.ts +36 -11
- 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 +66 -18
- package/documentation/auth.md +183 -67
- package/documentation/code-mode.md +30 -10
- package/documentation/deploying.md +5 -1
- package/documentation/integrating.md +64 -7
- package/documentation/meta-tools.md +38 -24
- package/documentation/migration-0.33.md +21 -13
- package/documentation/migration-0.34.md +67 -0
- package/documentation/operating.md +1 -1
- package/documentation/operator-tests.md +52 -20
- package/documentation/operator-ui.md +341 -104
- package/documentation/provider-auth-migration.md +22 -0
- 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 +23 -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
|
@@ -12,10 +12,10 @@ export const MIXPANEL_MCP_ENDPOINTS = {
|
|
|
12
12
|
in: "https://mcp-in.mixpanel.com/mcp",
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
* Reviewed
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* Reviewed against https://docs.mixpanel.com/docs/mcp and the 2026-10-19 live
|
|
16
|
+
* US describe schemas (#343, #703). Eleven of the 64 previously pinned tools
|
|
17
|
+
* changed since the 2026-08-30 review (#395, #512); unchanged pins are retained.
|
|
18
|
+
* Get-Audit-Log is documented beta-only: no live schema digest is claimed.
|
|
19
19
|
*/
|
|
20
20
|
const MIXPANEL_CLASSIFICATION = {
|
|
21
21
|
tools: {
|
|
@@ -47,7 +47,7 @@ const MIXPANEL_CLASSIFICATION = {
|
|
|
47
47
|
"Get-Dashboard": {
|
|
48
48
|
"verdict": "read",
|
|
49
49
|
"reason": "Retrieves Mixpanel dashboard information without changing vendor state.",
|
|
50
|
-
"schemaDigest": "sha256:
|
|
50
|
+
"schemaDigest": "sha256:0af78ab95566a20eeba08af6922f2017d98503e2b14dcbe8d7e2ac54eef870d7",
|
|
51
51
|
},
|
|
52
52
|
"Get-Business-Context": {
|
|
53
53
|
"verdict": "read",
|
|
@@ -64,10 +64,14 @@ const MIXPANEL_CLASSIFICATION = {
|
|
|
64
64
|
"reason": "Retrieves Mixpanel organizations information without changing vendor state.",
|
|
65
65
|
"schemaDigest": "sha256:e4c23f123dde073e00038d92ebd53f45157c83a4d3a2ff686ab1b66c859f4a11",
|
|
66
66
|
},
|
|
67
|
+
"Get-Audit-Log": {
|
|
68
|
+
"verdict": "read",
|
|
69
|
+
"reason": "Queries existing organization audit history; limited-access beta requiring organization admin or owner access.",
|
|
70
|
+
},
|
|
67
71
|
"Get-Events": {
|
|
68
72
|
"verdict": "read",
|
|
69
73
|
"reason": "Retrieves Mixpanel events information without changing vendor state.",
|
|
70
|
-
"schemaDigest": "sha256:
|
|
74
|
+
"schemaDigest": "sha256:09d7874b0c60cf790944b8998ee943f200912eac4349dfa80d0f2ae82a7a7998",
|
|
71
75
|
},
|
|
72
76
|
"List-Properties": {
|
|
73
77
|
"verdict": "read",
|
|
@@ -112,7 +116,7 @@ const MIXPANEL_CLASSIFICATION = {
|
|
|
112
116
|
"List-Cohorts": {
|
|
113
117
|
"verdict": "read",
|
|
114
118
|
"reason": "Retrieves Mixpanel cohorts information without changing vendor state.",
|
|
115
|
-
"schemaDigest": "sha256:
|
|
119
|
+
"schemaDigest": "sha256:505ff07f668186b670bcce28aa339076140b85896eb4f64afad78d963018297a",
|
|
116
120
|
},
|
|
117
121
|
"Describe-Cohort-Schema": {
|
|
118
122
|
"verdict": "read",
|
|
@@ -127,7 +131,7 @@ const MIXPANEL_CLASSIFICATION = {
|
|
|
127
131
|
"Get-Metric": {
|
|
128
132
|
"verdict": "read",
|
|
129
133
|
"reason": "Retrieves Mixpanel metric information without changing vendor state.",
|
|
130
|
-
"schemaDigest": "sha256:
|
|
134
|
+
"schemaDigest": "sha256:451e867abd1162f25bec96f76cb1b1d1d84eb33c04fe4d804e729810d93d26d7",
|
|
131
135
|
},
|
|
132
136
|
"List-Metrics": {
|
|
133
137
|
"verdict": "read",
|
|
@@ -145,9 +149,9 @@ const MIXPANEL_CLASSIFICATION = {
|
|
|
145
149
|
"schemaDigest": "sha256:26a3f3e0b4e52135d74335dd769103303071fc48c15b1df40efd2dfa5a353848",
|
|
146
150
|
},
|
|
147
151
|
"Get-Experiment": {
|
|
148
|
-
"verdict": "
|
|
149
|
-
"reason": "
|
|
150
|
-
"schemaDigest": "sha256:
|
|
152
|
+
"verdict": "destructive",
|
|
153
|
+
"reason": "Optional compute flags persist refreshed result caches and may auto-conclude an experiment; the full tool can modify existing state.",
|
|
154
|
+
"schemaDigest": "sha256:aebe329b5c5f3a9a465d40f12af1d6336d1304cc949c291eea752a5dd532e6fa",
|
|
151
155
|
},
|
|
152
156
|
"Get-Experiment-Setup-Guidance": {
|
|
153
157
|
"verdict": "read",
|
|
@@ -197,12 +201,12 @@ const MIXPANEL_CLASSIFICATION = {
|
|
|
197
201
|
"Create-Dashboard": {
|
|
198
202
|
"verdict": "write",
|
|
199
203
|
"reason": "Create Dashboard creates or appends Mixpanel state; it has side effects.",
|
|
200
|
-
"schemaDigest": "sha256:
|
|
204
|
+
"schemaDigest": "sha256:59fbe58cc8b69b31adfa7015e541d495abc566f56b1320e10ad77288ff9facf0",
|
|
201
205
|
},
|
|
202
206
|
"Update-Dashboard": {
|
|
203
207
|
"verdict": "destructive",
|
|
204
208
|
"reason": "Update Dashboard changes existing Mixpanel state or removes it.",
|
|
205
|
-
"schemaDigest": "sha256:
|
|
209
|
+
"schemaDigest": "sha256:6f2f11e94a13667848bdcb30531a7ff478a8c53460a5e0859f8241b64d0008da",
|
|
206
210
|
},
|
|
207
211
|
"Duplicate-Dashboard": {
|
|
208
212
|
"verdict": "write",
|
|
@@ -307,22 +311,22 @@ const MIXPANEL_CLASSIFICATION = {
|
|
|
307
311
|
"Create-Metric": {
|
|
308
312
|
"verdict": "write",
|
|
309
313
|
"reason": "Create Metric creates or appends Mixpanel state; it has side effects.",
|
|
310
|
-
"schemaDigest": "sha256:
|
|
314
|
+
"schemaDigest": "sha256:c10bc660e0664a9aba6450a45b50fb1fbea6a4fac809fd5390616416f72e04d6",
|
|
311
315
|
},
|
|
312
316
|
"Update-Metric": {
|
|
313
317
|
"verdict": "destructive",
|
|
314
318
|
"reason": "Update Metric changes existing Mixpanel state or removes it.",
|
|
315
|
-
"schemaDigest": "sha256:
|
|
319
|
+
"schemaDigest": "sha256:ca03823ea995d0ae53807a60b1dd0d47f1667b28cfd24cdab52a9fc66f0c2834",
|
|
316
320
|
},
|
|
317
321
|
"Create-Experiment": {
|
|
318
322
|
"verdict": "write",
|
|
319
323
|
"reason": "Create Experiment creates or appends Mixpanel state; it has side effects.",
|
|
320
|
-
"schemaDigest": "sha256:
|
|
324
|
+
"schemaDigest": "sha256:ce64eafec7f2490fa3c76030c75340bf5dd282a9d0bfb0c5353bcdc3b50f602d",
|
|
321
325
|
},
|
|
322
326
|
"Update-Experiment": {
|
|
323
327
|
"verdict": "destructive",
|
|
324
328
|
"reason": "Update Experiment changes existing Mixpanel state or removes it.",
|
|
325
|
-
"schemaDigest": "sha256:
|
|
329
|
+
"schemaDigest": "sha256:27c0e2b58fbb1468dda938c4112560e9481f58bc4ddafd4a94bad56666c5b12c",
|
|
326
330
|
},
|
|
327
331
|
"Create-Feature-Flag": {
|
|
328
332
|
"verdict": "write",
|
|
@@ -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- `Get-Events` accepts `event_names` for exact lookup or `query` for substring discovery, never both. Use `include_details` only when metadata is needed; `tag`, `verified`, `hidden`, and `dropped` filter the results.\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- `Get-Experiment` is a write-capable tool: `compute_exposures` and `compute_metrics` persist refreshed result caches and may auto-conclude an experiment. Use `List-Experiments` for read-only inventory; `Get-Experiment` requires a writable endpoint even with both compute flags false.\n- `Get-Audit-Log` queries organization audit history. It is a limited-access beta and requires organization admin or owner access. If absent, search cannot enable it; request beta access through Mixpanel, then reconnect after enrollment.\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
|
};
|
|
@@ -13,6 +13,8 @@ interface NotionCommonOptions {
|
|
|
13
13
|
instructions?: string;
|
|
14
14
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
15
15
|
maxResultBytes?: number;
|
|
16
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
17
|
+
readOnly?: boolean;
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Notion's official hosted MCP server, acting as the OAuth-authorized user.
|
|
@@ -291,7 +291,7 @@ const NOTION_MCP_CLASSIFICATION = {
|
|
|
291
291
|
},
|
|
292
292
|
},
|
|
293
293
|
};
|
|
294
|
-
const COMMON = keys("title", "authScope", "purpose", "instructions", "maxResultBytes");
|
|
294
|
+
const COMMON = keys("title", "authScope", "purpose", "instructions", "maxResultBytes", "readOnly");
|
|
295
295
|
/** The closed options notion() accepts, selected by `auth.type`; see `assertKnownOptions`. */
|
|
296
296
|
const NOTION_OPTIONS = variants(["auth", "type"], {
|
|
297
297
|
// The never-typed keys stay out of the shape, so the walk refuses them by name.
|
|
@@ -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
|
};
|
|
@@ -33,6 +33,8 @@ export interface OverflowOptions {
|
|
|
33
33
|
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
34
34
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
35
35
|
maxResultBytes?: number;
|
|
36
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
37
|
+
readOnly?: boolean;
|
|
36
38
|
/** Default page size for page-numbered list tools. Defaults to 25; Overflow caps it at 100. */
|
|
37
39
|
defaultPageSize?: number;
|
|
38
40
|
/**
|
|
@@ -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
|
};
|
|
@@ -44,6 +44,8 @@ export interface PlanningCenterOptions {
|
|
|
44
44
|
authScope?: "shared" | "personal";
|
|
45
45
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
46
46
|
maxResultBytes?: number;
|
|
47
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
48
|
+
readOnly?: boolean;
|
|
47
49
|
/**
|
|
48
50
|
* The `User-Agent` Planning Center sees. It asks integrations to identify
|
|
49
51
|
* themselves and refuses a request without one; name your deployment and a
|
|
@@ -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
|
};
|
|
@@ -23,15 +23,17 @@ export interface RevenueCatOptions {
|
|
|
23
23
|
* API v2 `sk_` secret key, which RevenueCat scopes to exactly one project —
|
|
24
24
|
* hence one key, one connector, and no `project` option: checking a declared
|
|
25
25
|
* project against `list-projects` at construction would be the unasked-for
|
|
26
|
-
* credential test P10 forbids.
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* credential test P10 forbids. RevenueCat may omit tools based on key
|
|
27
|
+
* permissions; Connecta serves the live catalog, not the reviewed superset.
|
|
28
|
+
* A served tool can still be refused by RevenueCat for missing permissions.
|
|
29
29
|
*/
|
|
30
30
|
auth?: RemoteMcpAuth;
|
|
31
31
|
/** Project-specific conventions appended to the maintained provider guide. */
|
|
32
32
|
instructions?: string;
|
|
33
33
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
34
34
|
maxResultBytes?: number;
|
|
35
|
+
/** Serve only read-classified tools on every endpoint. Defaults to false. */
|
|
36
|
+
readOnly?: boolean;
|
|
35
37
|
/**
|
|
36
38
|
* Optional per-runtime policy. There is no default even though RevenueCat
|
|
37
39
|
* publishes numbers (API v2, read 2026-08-18): the limit is per minute per
|