@zackbart/connecta 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +30 -20
  3. package/bin/connecta.mjs +25 -6
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.d.ts +167 -23
  6. package/dist/access-tokens.js +676 -140
  7. package/dist/agent-identity.js +47 -0
  8. package/dist/auth/cloudflare-access.js +13 -5
  9. package/dist/auth-elicitation.js +1 -1
  10. package/dist/call-admission-context.js +15 -0
  11. package/dist/call-admission.js +30 -43
  12. package/dist/call-recovery.js +6 -2
  13. package/dist/catalog-service.js +7 -2
  14. package/dist/config-value-sources.js +14 -9
  15. package/dist/config.d.ts +58 -78
  16. package/dist/config.js +143 -47
  17. package/dist/connector-access.d.ts +4 -33
  18. package/dist/connector-access.js +15 -99
  19. package/dist/connector-caller.d.ts +7 -1
  20. package/dist/connector-caller.js +3 -2
  21. package/dist/connectors/api-connector.d.ts +27 -4
  22. package/dist/connectors/api-connector.js +2 -0
  23. package/dist/connectors/guarded-fetch.d.ts +9 -0
  24. package/dist/connectors/guarded-fetch.js +7 -0
  25. package/dist/connectors/remote-mcp.js +2 -0
  26. package/dist/describe-config.d.ts +32 -7
  27. package/dist/describe-config.js +9 -18
  28. package/dist/errors.d.ts +1 -1
  29. package/dist/errors.js +1 -0
  30. package/dist/execute.js +16 -14
  31. package/dist/index.d.ts +4 -3
  32. package/dist/index.js +30 -10
  33. package/dist/invocation.js +12 -7
  34. package/dist/membership.d.ts +51 -0
  35. package/dist/membership.js +121 -0
  36. package/dist/meta-tools.js +17 -19
  37. package/dist/module-contracts.d.ts +23 -6
  38. package/dist/operator-record.d.ts +2 -2
  39. package/dist/operator-record.js +2 -2
  40. package/dist/operator-ui/agents.d.ts +74 -0
  41. package/dist/operator-ui/agents.js +104 -0
  42. package/dist/operator-ui/contract.d.ts +151 -29
  43. package/dist/operator-ui/endpoints.js +192 -0
  44. package/dist/operator-ui/fix-prompts.js +4 -4
  45. package/dist/operator-ui/generated.js +5 -4
  46. package/dist/operator-ui/member-page.js +112 -0
  47. package/dist/operator-ui/model.d.ts +15 -7
  48. package/dist/operator-ui/view.d.ts +84 -19
  49. package/dist/operator-ui/view.js +83 -21
  50. package/dist/page-styles.js +1 -1
  51. package/dist/program-writes.js +2 -2
  52. package/dist/provider.d.ts +63 -11
  53. package/dist/provider.js +46 -7
  54. package/dist/providers/_shared/google/workspace.d.ts +53 -16
  55. package/dist/providers/_shared/google/workspace.js +31 -9
  56. package/dist/providers/_shared/rest/tools.js +2 -2
  57. package/dist/providers/_shared/rest/value-safety.d.ts +50 -0
  58. package/dist/providers/_shared/rest/value-safety.js +699 -7
  59. package/dist/providers/breeze/skill.generated.js +2 -2
  60. package/dist/providers/ccb/index.js +1 -1
  61. package/dist/providers/cloudflare/index.js +3 -3
  62. package/dist/providers/cloudflare/rest.js +25 -3
  63. package/dist/providers/cloudflare/skill.generated.js +2 -2
  64. package/dist/providers/cloudflare/value-safety.js +32 -0
  65. package/dist/providers/docs/index.d.ts +1 -1
  66. package/dist/providers/docs/index.js +5 -5
  67. package/dist/providers/docs/skill.generated.js +2 -2
  68. package/dist/providers/drive/index.d.ts +1 -1
  69. package/dist/providers/drive/index.js +4 -4
  70. package/dist/providers/drive/skill.generated.js +2 -2
  71. package/dist/providers/forms/index.d.ts +1 -1
  72. package/dist/providers/forms/index.js +5 -5
  73. package/dist/providers/forms/skill.generated.js +2 -2
  74. package/dist/providers/github/skill.generated.js +2 -2
  75. package/dist/providers/gmail/index.d.ts +1 -1
  76. package/dist/providers/gmail/index.js +279 -26
  77. package/dist/providers/gmail/skill.generated.js +2 -2
  78. package/dist/providers/linear/index.js +1 -1
  79. package/dist/providers/mixpanel/index.d.ts +27 -12
  80. package/dist/providers/mixpanel/index.js +58 -6
  81. package/dist/providers/mixpanel/operations.js +20 -0
  82. package/dist/providers/mixpanel/rest.js +603 -0
  83. package/dist/providers/mixpanel/skill.generated.js +3 -2
  84. package/dist/providers/mixpanel/value-safety.js +16 -0
  85. package/dist/providers/notion/index.d.ts +7 -0
  86. package/dist/providers/notion/index.js +116 -11
  87. package/dist/providers/notion/rest.js +183 -24
  88. package/dist/providers/notion/skill.generated.js +4 -4
  89. package/dist/providers/notion/value-safety.js +30 -5
  90. package/dist/providers/overflow/index.js +1 -1
  91. package/dist/providers/planning-center/index.d.ts +27 -0
  92. package/dist/providers/planning-center/index.js +287 -25
  93. package/dist/providers/planning-center/skill.generated.js +24 -4
  94. package/dist/providers/sheets/index.d.ts +4 -1
  95. package/dist/providers/sheets/index.js +9 -6
  96. package/dist/providers/sheets/skill.generated.js +2 -2
  97. package/dist/providers/slides/index.d.ts +1 -1
  98. package/dist/providers/slides/index.js +6 -6
  99. package/dist/providers/slides/skill.generated.js +2 -2
  100. package/dist/providers/stripe/rest.js +9 -2
  101. package/dist/providers/stripe/skill.generated.js +2 -2
  102. package/dist/providers/stripe/value-safety.js +22 -0
  103. package/dist/providers/vercel/index.js +2 -2
  104. package/dist/providers/vercel/rest.js +20 -2
  105. package/dist/providers/vercel/skill.generated.js +2 -2
  106. package/dist/providers/vercel/value-safety.js +22 -0
  107. package/dist/providers/wispr-flow/index.d.ts +14 -0
  108. package/dist/providers/wispr-flow/index.js +117 -0
  109. package/dist/providers/wispr-flow/skill.generated.js +8 -0
  110. package/dist/registry.d.ts +2 -3
  111. package/dist/registry.js +8 -9
  112. package/dist/routes/access-tokens.js +82 -75
  113. package/dist/routes/activity-disclosure.js +17 -18
  114. package/dist/routes/activity.js +34 -54
  115. package/dist/routes/actor-labels.js +33 -4
  116. package/dist/routes/agent-path.js +43 -0
  117. package/dist/routes/connect.js +16 -12
  118. package/dist/routes/credentials.js +16 -40
  119. package/dist/routes/mcp.js +155 -85
  120. package/dist/routes/oauth-management.js +11 -5
  121. package/dist/routes/oauth.js +3 -9
  122. package/dist/routes/operator.js +25 -17
  123. package/dist/routes/shared.d.ts +60 -20
  124. package/dist/routes/shared.js +187 -96
  125. package/dist/routes/ui-config.js +437 -97
  126. package/dist/routes/ui-data.js +2 -2
  127. package/dist/routes/ui.js +9 -16
  128. package/dist/runtime/call-admission.js +9 -0
  129. package/dist/server.js +8 -0
  130. package/dist/skills.js +5 -4
  131. package/dist/storage/keys.js +9 -1
  132. package/dist/tool-safety.js +13 -5
  133. package/dist/types.d.ts +12 -1
  134. package/dist/ui.d.ts +2 -2
  135. package/dist/ui.js +17 -15
  136. package/dist/usage-guide.js +3 -3
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/dist/workspace-address.js +9 -0
  140. package/dist/workspace-subject.js +23 -0
  141. package/documentation/README.md +46 -22
  142. package/documentation/architecture.md +378 -58
  143. package/documentation/auth.md +806 -215
  144. package/documentation/code-mode.md +93 -81
  145. package/documentation/deploying.md +128 -26
  146. package/documentation/integrating.md +145 -22
  147. package/documentation/meta-tools.md +58 -46
  148. package/documentation/migration-0.33.md +344 -0
  149. package/documentation/operating.md +14 -9
  150. package/documentation/operator-tests.md +117 -31
  151. package/documentation/operator-ui.md +326 -55
  152. package/documentation/provider-auth-migration.md +42 -27
  153. package/documentation/provider-migration-0.29.md +3 -3
  154. package/examples/worker/AGENTS.md +35 -8
  155. package/examples/worker/README.md +92 -53
  156. package/examples/worker/src/connecta.config.ts +41 -20
  157. package/examples/worker/wrangler.jsonc +3 -0
  158. package/package.json +7 -2
  159. package/templates/node/.env.example +3 -0
  160. package/templates/node/AGENTS.md +33 -10
  161. package/templates/node/Dockerfile +2 -2
  162. package/templates/node/README.md +71 -44
  163. package/templates/node/docker-compose.yml +3 -2
  164. package/templates/node/package.json +4 -3
  165. package/templates/node/src/adopt-token.ts +23 -0
  166. package/templates/node/src/connecta.config.ts +33 -12
  167. package/templates/node/src/rotate-token.ts +17 -0
  168. package/dist/tool-safety.d.ts +0 -9
  169. package/templates/node/src/provision-token.ts +0 -17
@@ -1,9 +1,10 @@
1
1
  // Generated by scripts/generate-providers.mjs; do not edit.
2
2
  export const skill = {
3
- "content": " 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.\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",
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`.\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 `query_retention` or hosted MCP for an existing saved Retention report. 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"
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`.\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 `query_retention` or hosted MCP for an existing saved Retention report. 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"
8
9
  }
9
10
  };
@@ -0,0 +1,16 @@
1
+ import { safe } from "../_shared/rest/value-safety.js";
2
+ /** Reviewed 2026-10-14 against the official schemas and service-account guide. */
3
+ export const MIXPANEL_VALUE_SAFETY = {
4
+ title: "Mixpanel",
5
+ operations: {
6
+ "GET /api/app/me": safe("Account metadata has no documented response schema; redact credential-named fields recursively, including project tokens. No service-account administration endpoints are admitted."),
7
+ "GET /api/2.0/export": safe("JSONL event names and properties; arbitrary property bags pass through recursive credential and bearer-URL redaction."),
8
+ "GET /api/query/stream/query": safe("Activity event names and properties; arbitrary property bags pass through recursive credential and bearer-URL redaction."),
9
+ "POST /api/query/engage": safe("Existing profile ids and properties only; arbitrary property bags pass through recursive credential and bearer-URL redaction."),
10
+ "GET /api/query/insights": safe("Report headers name breakdown dimensions, not HTTP headers; numeric time series contain no authentication values.", ["headers"]),
11
+ "GET /api/query/funnels": safe("Funnel steps and conversion counts, no authentication values."),
12
+ "GET /api/query/funnels/list": safe("Saved funnel names and ids, no authentication values."),
13
+ "GET /api/query/retention": safe("Cohort dates and retention counts, no authentication values."),
14
+ "POST /import": safe("Import status and counts only. Vendor error text is withheld because it may quote submitted properties."),
15
+ },
16
+ };
@@ -38,6 +38,13 @@ export interface NotionTokenOptions extends NotionCommonOptions {
38
38
  auth: {
39
39
  type: "token";
40
40
  };
41
+ /**
42
+ * Per-runtime downstream call-admission policy, replacing the default of
43
+ * 180 calls/minute and 3 concurrent. Business and Enterprise allow 600
44
+ * requests/minute per connection; all connections share a workspace-wide
45
+ * limit. One admitted call can make several HTTP requests.
46
+ */
47
+ callAdmission?: ConnectorCallAdmissionPolicy;
41
48
  /** Operator-facing label for the integration token. */
42
49
  credentialLabel?: string;
43
50
  /**
@@ -11,13 +11,11 @@ export const NOTION_MCP_ENDPOINT = "https://mcp.notion.com/mcp";
11
11
  /** Lean by default: smaller than Notion's 100 so a first read stays cheap. */
12
12
  const DEFAULT_PAGE_SIZE = 25;
13
13
  /**
14
- * Notion documents "an average of three requests per second, with some bursts
15
- * beyond the average allowed" per connection. 180 calls per minute is that
16
- * average over a window short bursts pass and a sustained loop does not, and
17
- * `maxConcurrency: 3` is the load-bearing half — an averaged budget cannot stop
18
- * forty calls in one tick, and declaring a queue is also what makes the queue
19
- * settings legal at construction. Per runtime, and a floor on the real request
20
- * rate rather than a ceiling, since one admitted call can spend many fetches.
14
+ * Conservative default for every Notion plan: 180 calls per minute, with
15
+ * Connecta's concurrency cap of three to bound bursts. Business and Enterprise
16
+ * allow 600 requests per minute per connection; all connections also share a
17
+ * workspace-wide limit. This policy is per runtime and counts calls rather
18
+ * than HTTP requests, since one admitted call can spend many fetches.
21
19
  */
22
20
  const NOTION_ADMISSION = {
23
21
  rules: [
@@ -52,13 +50,31 @@ function oauthUsageGuide(purpose, instructions) {
52
50
  }
53
51
  /**
54
52
  * Reviewed in #705's provider audit against https://developers.notion.com/guides/mcp/mcp-supported-tools.
55
- * Retains the release-reviewed inventory, including names absent from today's
56
- * public reference. Live annotations were not reverified without credentials.
57
- * No schema digest is asserted without a captured schema to review.
53
+ * Extended in #814 from the operator's 2026-10-15 hosted catalog capture,
54
+ * recorded in mcp-catalog-2026-10-15.md. Includes names absent from the public
55
+ * reference and retains reviewed names absent from this client's catalog.
56
+ * Annotations were not visible; captured input summaries are not schema
57
+ * documents, so no schema digests are asserted.
58
58
  */
59
59
  const NOTION_MCP_CLASSIFICATION = {
60
60
  tools: {
61
61
  "notion-search": { "verdict": "read", "reason": "Retrieves Notion information without changing vendor state." },
62
+ "notion-ai-search": {
63
+ "verdict": "read",
64
+ "reason": "Legacy AI search, still callable by name; retrieves matching content without changing vendor state.",
65
+ },
66
+ "notion-get-self": {
67
+ "verdict": "read",
68
+ "reason": "Returns the connected workspace and user without changing vendor state.",
69
+ },
70
+ "notion-get-tool-access": {
71
+ "verdict": "read",
72
+ "reason": "Reads which tools the workspace plan allows; it grants nothing and changes nothing.",
73
+ },
74
+ "notion-download-skill": {
75
+ "verdict": "read",
76
+ "reason": "Returns an existing Skill as a signed, expiring archive URL; it changes no workspace state.",
77
+ },
62
78
  "notion-search-skills": {
63
79
  "verdict": "read",
64
80
  "reason": "Retrieves Notion skills information without changing vendor state.",
@@ -185,6 +201,94 @@ const NOTION_MCP_CLASSIFICATION = {
185
201
  "reason": "update view changes existing Notion state or removes it.",
186
202
  },
187
203
  "notion-stop-session": { "verdict": "destructive", "reason": "Stops an existing session and interrupts its work." },
204
+ "notion-check-mcp-next-steps": {
205
+ "verdict": "read",
206
+ "reason": "Returns an upsell or next-step card without changing workspace state or enabling access.",
207
+ },
208
+ "notion-show-advanced-analysis-next-steps": {
209
+ "verdict": "read",
210
+ "reason": "Returns a message and destination link without changing workspace state or enabling access.",
211
+ },
212
+ "notion-get-enabled-skills": {
213
+ "verdict": "read",
214
+ "reason": "Lists the current user's enabled skills; enabling or disabling them is a separate write.",
215
+ },
216
+ "notion-get-file-download-urls": {
217
+ "verdict": "read",
218
+ "reason": "Returns signed, expiring download URLs for existing file references without changing workspace state; use only for downloads and do not expose the URLs.",
219
+ },
220
+ "notion-get-groups": {
221
+ "verdict": "read",
222
+ "reason": "Retrieves visible permission group IDs and names without changing membership or permissions.",
223
+ },
224
+ "notion-list-favorite-pages": {
225
+ "verdict": "read",
226
+ "reason": "Lists existing favorites in sidebar order without changing them.",
227
+ },
228
+ "notion-list-page-backlinks": {
229
+ "verdict": "read",
230
+ "reason": "Retrieves IDs, titles and URLs of existing page backlinks without changing links or pages.",
231
+ },
232
+ "notion-list-private-pages": {
233
+ "verdict": "read",
234
+ "reason": "Lists existing pages and databases in the user's Private sidebar without changing them.",
235
+ },
236
+ "notion-list-recent-pages": {
237
+ "verdict": "read",
238
+ "reason": "Lists the user's existing page-view history without changing pages or recording a new view.",
239
+ },
240
+ "notion-list-shared-pages": {
241
+ "verdict": "read",
242
+ "reason": "Lists existing pages and databases in the user's Shared sidebar without changing sharing.",
243
+ },
244
+ "notion-memory-search": {
245
+ "verdict": "read",
246
+ "reason": "Searches memories saved by connected agents in earlier sessions without saving or changing memories.",
247
+ },
248
+ "notion-query-multiple-data-sources": {
249
+ "verdict": "read",
250
+ "reason": "Runs read-only SQLite SQL across existing data sources without changing them.",
251
+ },
252
+ "notion-delete-comment": {
253
+ "verdict": "destructive",
254
+ "reason": "Deletes a comment and may remove its discussion when no comments remain.",
255
+ },
256
+ "notion-delete-databases": {
257
+ "verdict": "destructive",
258
+ "reason": "Moves existing databases to Trash; an optional confirmation flag does not make this tool read-only.",
259
+ },
260
+ "notion-delete-pages": {
261
+ "verdict": "destructive",
262
+ "reason": "Moves existing pages to Trash; an optional confirmation flag does not make this tool read-only.",
263
+ },
264
+ "notion-restore-pages": {
265
+ "verdict": "destructive",
266
+ "reason": "Restores pages or databases from Trash and changes their workspace state.",
267
+ },
268
+ "notion-set-comment-reaction": {
269
+ "verdict": "destructive",
270
+ "reason": "Adds or removes the user's emoji reaction on a comment; repeatability does not make it read-only.",
271
+ },
272
+ "notion-set-discussion-resolved": {
273
+ "verdict": "destructive",
274
+ "reason": "Resolves or reopens an existing comment discussion.",
275
+ },
276
+ "notion-update-comment": {
277
+ "verdict": "destructive",
278
+ "reason": "Replaces the text of an existing comment.",
279
+ },
280
+ "notion-update-enabled-skills": {
281
+ "verdict": "destructive",
282
+ "reason": "Changes the current user's skill enablement for external MCP clients, Notion AI, or both.",
283
+ },
284
+ "notion-update-folder": {
285
+ "verdict": "destructive",
286
+ "reason": "Changes an existing folder by adding or removing files or adding a subfolder.",
287
+ },
288
+ "notion-upload-skill": {
289
+ "verdict": "destructive",
290
+ "reason": "Preparation returns a credential-bearing upload URL and headers; completion replaces the target page title, Description, body and Files. Both actions stay writes.",
291
+ },
188
292
  },
189
293
  };
190
294
  const COMMON = keys("title", "authScope", "purpose", "instructions", "maxResultBytes");
@@ -198,6 +302,7 @@ const NOTION_OPTIONS = variants(["auth", "type"], {
198
302
  }).shape,
199
303
  token: optionsOf()({
200
304
  ...COMMON,
305
+ callAdmission: CALL_ADMISSION,
201
306
  ...keys("credentialLabel", "defaultPageSize"),
202
307
  auth: optionsOf()(keys("type")),
203
308
  }).shape,
@@ -230,7 +335,7 @@ function notionToken(id, options, provider) {
230
335
  placeholder: "Paste the integration token",
231
336
  },
232
337
  testCredential: rest.testCredential,
233
- callAdmission: NOTION_ADMISSION,
338
+ callAdmission: options.callAdmission === undefined ? NOTION_ADMISSION : options.callAdmission,
234
339
  usageGuide: {
235
340
  content: tokenUsageGuide(options.purpose, options.instructions),
236
341
  summary: "Internal integration over REST. Flattened reads, authoring helpers, and the pinned API index for the rest.",