@zackbart/connecta 0.34.1 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +418 -0
- package/README.md +65 -105
- package/SECURITY.md +3 -2
- package/bin/connecta.mjs +41 -16
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.d.ts +2 -1
- package/dist/access-tokens.js +66 -30
- package/dist/activity.d.ts +1 -1
- package/dist/activity.js +2 -0
- package/dist/agent-config.d.ts +23 -0
- package/dist/agent-config.js +59 -0
- package/dist/agent-description.js +16 -0
- package/dist/agent-identity.js +4 -4
- package/dist/agent-observation.js +39 -0
- package/dist/auth/clerk-sdk/client.d.ts +2 -0
- package/dist/auth/clerk-sdk/generated.js +554 -563
- package/dist/auth/clerk-transport.js +70 -3
- package/dist/auth/clerk.d.ts +13 -0
- package/dist/auth/clerk.js +653 -246
- package/dist/auth/cloudflare-access.js +46 -42
- package/dist/auth/downstream-client-metadata.js +2 -2
- package/dist/auth/downstream-oauth.js +2044 -1161
- package/dist/auth/downstream-revocation.js +88 -88
- package/dist/auth/native-call.js +20 -0
- package/dist/auth/native.js +184 -0
- package/dist/auth/oauth-start-reset.js +11 -5
- package/dist/auth/oauth-token-response.js +121 -102
- package/dist/auth/oauth-v2-migration.js +97 -78
- package/dist/auth/renewable-grant.js +10 -0
- package/dist/auth/static-oauth.js +751 -281
- package/dist/auth/vercel-agent.js +266 -0
- package/dist/auth-admission.js +204 -0
- package/dist/auth-elicitation.js +350 -241
- package/dist/authorization-handoff.js +164 -90
- package/dist/call-admission-context.js +3 -10
- package/dist/call-admission.js +2 -0
- package/dist/call-classification.js +9 -0
- package/dist/call-recovery.js +32 -13
- package/dist/canonical-json.js +8 -0
- package/dist/catalog-cache.js +369 -213
- package/dist/catalog-service.js +659 -530
- package/dist/catalog.js +5 -1
- package/dist/concurrency.js +0 -25
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +3 -1
- package/dist/config-schema.js +13 -3
- package/dist/config.d.ts +40 -6
- package/dist/config.js +40 -26
- package/dist/connector-caller.d.ts +4 -0
- package/dist/connector-caller.js +17 -4
- package/dist/connectors/api-connector.d.ts +29 -7
- package/dist/connectors/api-connector.js +145 -57
- package/dist/connectors/guarded-fetch.d.ts +51 -17
- package/dist/connectors/guarded-fetch.js +215 -121
- package/dist/connectors/negotiation-cache.js +20 -10
- package/dist/connectors/remote-mcp.d.ts +10 -30
- package/dist/connectors/remote-mcp.js +955 -563
- package/dist/d1.js +10 -243
- package/dist/derived-context.js +5 -0
- package/dist/describe-config.d.ts +3 -1
- package/dist/describe-config.js +2 -0
- package/dist/downstream-input.js +347 -260
- package/dist/errors.d.ts +87 -6
- package/dist/errors.js +114 -4
- package/dist/execute.js +87 -50
- package/dist/executor-admission.js +3 -0
- package/dist/executors/quickjs.js +163 -112
- package/dist/guarded-transport.js +50 -0
- package/dist/guest-runtime.js +1 -1
- package/dist/http/client.js +439 -0
- package/dist/http/errors.js +558 -0
- package/dist/http/fields.js +49 -0
- package/dist/http/hatch-paging.js +318 -0
- package/dist/http/hatch.js +1279 -0
- package/dist/http/json-schema.js +267 -0
- package/dist/http/node-transport.js +241 -0
- package/dist/http/oauth.js +162 -0
- package/dist/http/paging.js +227 -0
- package/dist/http/provider.js +221 -0
- package/dist/http/safety.js +82 -0
- package/dist/http/services.js +79 -0
- package/dist/http/tools.js +379 -0
- package/dist/http/vendor-index.js +138 -0
- package/dist/http/vendor.js +448 -0
- package/dist/http/write-request.js +66 -0
- package/dist/http-policy.d.ts +11 -0
- package/dist/http-policy.js +45 -0
- package/dist/inbound-credential.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -0
- package/dist/invocation-auth.js +30 -19
- package/dist/invocation.js +218 -85
- package/dist/mcp-result.js +0 -4
- package/dist/membership.d.ts +4 -0
- package/dist/meta-output.js +1 -1
- package/dist/meta-tools.js +531 -406
- package/dist/native-recovery.js +52 -0
- package/dist/native-tools.js +382 -0
- package/dist/node.d.ts +4 -3
- package/dist/node.js +45 -8
- package/dist/oauth-handoff.js +177 -104
- package/dist/oauth-partition.js +6 -20
- package/dist/oauth-sealing.js +6 -7
- package/dist/operation-vocabulary.js +101 -0
- package/dist/operator-record.d.ts +2 -2
- package/dist/operator-record.js +17 -4
- package/dist/operator-ui/contract.d.ts +8 -3
- package/dist/operator-ui/generated.js +4 -4
- package/dist/operator-ui/model.d.ts +2 -3
- package/dist/program-writes.js +47 -26
- package/dist/provider-call-facts.d.ts +115 -0
- package/dist/provider-call-facts.js +182 -0
- package/dist/provider-call.d.ts +112 -0
- package/dist/provider-call.js +271 -0
- package/dist/provider.d.ts +65 -31
- package/dist/provider.js +116 -39
- package/dist/providers/_shared/google/hatch.js +91 -0
- package/dist/providers/_shared/google/kit.js +427 -0
- package/dist/providers/_shared/google/result-size.js +15 -2
- package/dist/providers/_shared/google/service-account.d.ts +2 -1
- package/dist/providers/_shared/google/service-account.js +8 -4
- package/dist/providers/_shared/google/workspace.d.ts +6 -210
- package/dist/providers/_shared/google/workspace.js +6 -425
- package/dist/providers/_shared/rest/compact-index.js +86 -0
- package/dist/providers/_shared/rest/kit.js +146 -0
- package/dist/providers/_shared/rest/operation-index.js +230 -14
- package/dist/providers/_shared/rest/response-media.js +11 -0
- package/dist/providers/_shared/rest/value-safety.js +353 -195
- package/dist/providers/_shared/sensitive-fields.js +27 -0
- package/dist/providers/breeze/api.js +584 -0
- package/dist/providers/breeze/index.d.ts +1 -3
- package/dist/providers/breeze/index.js +67 -1420
- package/dist/providers/breeze/openapi.generated.js +60 -0
- package/dist/providers/breeze/skill.generated.js +2 -6
- package/dist/providers/breeze/tools.js +696 -0
- package/dist/providers/ccb/api.js +496 -0
- package/dist/providers/ccb/index.d.ts +15 -37
- package/dist/providers/ccb/index.js +77 -1702
- package/dist/providers/ccb/lib/constants.d.ts +34 -0
- package/dist/providers/ccb/lib/constants.js +74 -0
- package/dist/providers/ccb/lib/guide.js +9 -0
- package/dist/providers/ccb/lib/verdicts.js +387 -0
- package/dist/providers/ccb/openapi.generated.js +397 -0
- package/dist/providers/ccb/safety.js +295 -0
- package/dist/providers/ccb/skill.generated.js +2 -6
- package/dist/providers/ccb/tools.js +853 -0
- package/dist/providers/cloudflare/api.js +1066 -0
- package/dist/providers/cloudflare/index.d.ts +5 -22
- package/dist/providers/cloudflare/index.js +79 -197
- package/dist/providers/cloudflare/lib/config.d.ts +16 -0
- package/dist/providers/cloudflare/lib/config.js +2 -0
- package/dist/providers/cloudflare/lib/discovery.js +29 -0
- package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
- package/dist/providers/cloudflare/lib/hatch.js +103 -0
- package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
- package/dist/providers/cloudflare/lib/response-media.js +75 -0
- package/dist/providers/cloudflare/openapi.generated.js +4 -3
- package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
- package/dist/providers/cloudflare/skill.generated.js +2 -10
- package/dist/providers/cloudflare/tools.js +360 -0
- package/dist/providers/docs/api.js +51 -0
- package/dist/providers/docs/index.d.ts +1 -12
- package/dist/providers/docs/index.js +53 -1165
- package/dist/providers/docs/lib/constants.d.ts +18 -0
- package/dist/providers/docs/lib/constants.js +18 -0
- package/dist/providers/docs/lib/discovery.js +26 -0
- package/dist/providers/docs/lib/requests.js +48 -0
- package/dist/providers/docs/lib/schemas.js +168 -0
- package/dist/providers/docs/openapi.generated.js +16 -0
- package/dist/providers/docs/safety.js +54 -0
- package/dist/providers/docs/skill.generated.js +2 -6
- package/dist/providers/docs/tools.js +801 -0
- package/dist/providers/drive/api.js +177 -0
- package/dist/providers/drive/index.d.ts +1 -12
- package/dist/providers/drive/index.js +38 -1448
- package/dist/providers/drive/lib/constants.d.ts +6 -0
- package/dist/providers/drive/lib/constants.js +6 -0
- package/dist/providers/drive/lib/contracts.js +532 -0
- package/dist/providers/drive/lib/hatch.js +197 -0
- package/dist/providers/drive/openapi.generated.js +77 -0
- package/dist/providers/drive/safety.js +90 -0
- package/dist/providers/drive/skill.generated.js +2 -6
- package/dist/providers/drive/tools.js +1290 -0
- package/dist/providers/forms/api.js +71 -0
- package/dist/providers/forms/index.d.ts +1 -12
- package/dist/providers/forms/index.js +49 -1145
- package/dist/providers/forms/lib/constants.d.ts +14 -0
- package/dist/providers/forms/lib/constants.js +19 -0
- package/dist/providers/forms/lib/hatch.js +72 -0
- package/dist/providers/forms/openapi.generated.js +23 -0
- package/dist/providers/forms/safety.js +34 -0
- package/dist/providers/forms/skill.generated.js +2 -8
- package/dist/providers/forms/tools.js +1023 -0
- package/dist/providers/github/api.js +150 -0
- package/dist/providers/github/index.d.ts +4 -5
- package/dist/providers/github/index.js +30 -87
- package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
- package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
- package/dist/providers/github/lib/discovery.js +132 -0
- package/dist/providers/github/lib/operations.js +58 -0
- package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
- package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
- package/dist/providers/github/openapi.generated.js +1216 -0
- package/dist/providers/github/safety.js +216 -0
- package/dist/providers/github/skill.generated.js +2 -5
- package/dist/providers/github/tools.js +506 -0
- package/dist/providers/gmail/api.js +114 -0
- package/dist/providers/gmail/index.d.ts +1 -12
- package/dist/providers/gmail/index.js +73 -1621
- package/dist/providers/gmail/lib/constants.d.ts +13 -0
- package/dist/providers/gmail/lib/constants.js +16 -0
- package/dist/providers/gmail/lib/index.js +173 -0
- package/dist/providers/gmail/lib/schemas.js +176 -0
- package/dist/providers/gmail/lib/threading.js +120 -0
- package/dist/providers/gmail/openapi.generated.js +92 -0
- package/dist/providers/gmail/safety.js +21 -0
- package/dist/providers/gmail/skill.generated.js +2 -6
- package/dist/providers/gmail/tools.js +1435 -0
- package/dist/providers/overflow/api.js +1008 -0
- package/dist/providers/overflow/index.d.ts +1 -17
- package/dist/providers/overflow/index.js +60 -1488
- package/dist/providers/overflow/skill.generated.js +2 -5
- package/dist/providers/overflow/tools.js +873 -0
- package/dist/providers/planning-center/api.js +467 -0
- package/dist/providers/planning-center/index.d.ts +4 -35
- package/dist/providers/planning-center/index.js +69 -2445
- package/dist/providers/planning-center/lib/constants.d.ts +58 -0
- package/dist/providers/planning-center/lib/constants.js +83 -0
- package/dist/providers/planning-center/lib/discovery.js +40 -0
- package/dist/providers/planning-center/lib/guide.js +39 -0
- package/dist/providers/planning-center/lib/json-api.js +83 -0
- package/dist/providers/planning-center/lib/verdicts.js +1758 -0
- package/dist/providers/planning-center/openapi.generated.js +1764 -0
- package/dist/providers/planning-center/safety.js +188 -0
- package/dist/providers/planning-center/skill.generated.js +2 -27
- package/dist/providers/planning-center/tools.js +1010 -0
- package/dist/providers/quickbooks/api.js +299 -0
- package/dist/providers/quickbooks/index.d.ts +13 -0
- package/dist/providers/quickbooks/index.js +93 -0
- package/dist/providers/quickbooks/lib/query.js +118 -0
- package/dist/providers/quickbooks/lib/writes.js +168 -0
- package/dist/providers/quickbooks/safety.js +222 -0
- package/dist/providers/quickbooks/skill.generated.js +5 -0
- package/dist/providers/quickbooks/tools.js +117 -0
- package/dist/providers/sheets/api.js +133 -0
- package/dist/providers/sheets/index.d.ts +4 -16
- package/dist/providers/sheets/index.js +61 -1172
- package/dist/providers/sheets/lib/constants.d.ts +14 -0
- package/dist/providers/sheets/lib/constants.js +14 -0
- package/dist/providers/sheets/lib/hatch.js +163 -0
- package/dist/providers/sheets/lib/requests.js +105 -0
- package/dist/providers/sheets/lib/schemas.js +138 -0
- package/dist/providers/sheets/openapi.generated.js +30 -0
- package/dist/providers/sheets/safety.js +27 -0
- package/dist/providers/sheets/skill.generated.js +2 -7
- package/dist/providers/sheets/tools.js +733 -0
- package/dist/providers/slides/api.js +147 -0
- package/dist/providers/slides/index.d.ts +2 -10
- package/dist/providers/slides/index.js +48 -2555
- package/dist/providers/slides/lib/constants.d.ts +11 -0
- package/dist/providers/slides/lib/constants.js +11 -0
- package/dist/providers/slides/lib/requests.js +60 -0
- package/dist/providers/slides/lib/schemas.js +565 -0
- package/dist/providers/slides/openapi.generated.js +18 -0
- package/dist/providers/slides/skill.generated.js +2 -6
- package/dist/providers/slides/tools.js +1968 -0
- package/dist/providers/stripe/api.js +401 -0
- package/dist/providers/stripe/index.d.ts +5 -27
- package/dist/providers/stripe/index.js +74 -210
- package/dist/providers/stripe/lib/config.d.ts +4 -0
- package/dist/providers/stripe/lib/config.js +2 -0
- package/dist/providers/stripe/openapi.generated.js +4 -3
- package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
- package/dist/providers/stripe/skill.generated.js +2 -11
- package/dist/providers/stripe/tools.js +77 -0
- package/dist/providers/tithely/api.js +531 -0
- package/dist/providers/tithely/index.d.ts +17 -0
- package/dist/providers/tithely/index.js +86 -965
- package/dist/providers/tithely/skill.generated.js +2 -5
- package/dist/providers/tithely/tools.js +426 -0
- package/dist/providers/vercel/api.js +551 -0
- package/dist/providers/vercel/index.d.ts +9 -25
- package/dist/providers/vercel/index.js +65 -260
- package/dist/providers/vercel/lib/config.d.ts +2 -0
- package/dist/providers/vercel/lib/config.js +2 -0
- package/dist/providers/vercel/lib/index.js +117 -0
- package/dist/providers/vercel/lib/logs.js +100 -0
- package/dist/providers/vercel/lib/pin.js +270 -0
- package/dist/providers/vercel/openapi.generated.js +10 -5
- package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
- package/dist/providers/vercel/skill.generated.js +2 -7
- package/dist/providers/vercel/tools.js +642 -0
- package/dist/redaction-identity.js +47 -0
- package/dist/redaction-marker.js +125 -0
- package/dist/registry.d.ts +21 -15
- package/dist/registry.js +409 -376
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/actor-labels.js +1 -1
- package/dist/routes/connect.js +1 -1
- package/dist/routes/dispatch.js +103 -0
- package/dist/routes/mcp.js +170 -79
- package/dist/routes/oauth.js +6 -1
- package/dist/routes/operator.js +2 -2
- package/dist/routes/shared.d.ts +5 -4
- package/dist/routes/shared.js +244 -177
- package/dist/routes/skills.js +10 -8
- package/dist/routes/ui-config.js +27 -3
- package/dist/routes/ui-data.js +21 -20
- package/dist/routes/ui.js +1 -1
- package/dist/runtime/call-admission.js +0 -26
- package/dist/runtime/call-entry.js +30 -0
- package/dist/runtime/call.js +552 -0
- package/dist/runtime/connector-scope.js +7 -0
- package/dist/runtime/credential-probe.js +34 -0
- package/dist/runtime/executor.js +40 -0
- package/dist/runtime/native-storage.js +63 -0
- package/dist/runtime/registry.js +27 -0
- package/dist/runtime/request-scope.js +72 -0
- package/dist/runtime/run.js +15 -7
- package/dist/runtime/shared-read.js +34 -5
- package/dist/runtime/storage.js +34 -21
- package/dist/sent-secrets.d.ts +194 -37
- package/dist/sent-secrets.js +1743 -340
- package/dist/server.js +5 -56
- package/dist/skills.js +322 -223
- package/dist/sqlite.js +15 -14
- package/dist/storage/d1-copy.js +255 -0
- package/dist/storage/d1-driver.js +15 -0
- package/dist/storage/keys.js +28 -0
- package/dist/storage/sql-driver.js +88 -0
- package/dist/storage/sql-native.js +544 -0
- package/dist/storage/sql-schema.d.ts +6 -0
- package/dist/storage/sql-schema.js +15 -0
- package/dist/storage/sql.d.ts +14 -20
- package/dist/storage/sql.js +29 -535
- package/dist/timeout.js +0 -26
- package/dist/tool-safety.js +20 -10
- package/dist/types.d.ts +9 -109
- package/dist/ui.d.ts +1 -1
- package/dist/ui.js +0 -1
- package/dist/upward.js +3 -0
- package/dist/usage-guide.js +3 -1
- package/dist/validate.d.ts +4 -2
- package/dist/validate.js +86 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +312 -292
- package/dist/workspace-subject.js +5 -1
- package/dist/write-outcome.js +454 -34
- package/documentation/README.md +28 -7
- package/documentation/architecture.md +2409 -255
- package/documentation/auth.md +867 -114
- package/documentation/code-mode.md +49 -16
- package/documentation/deploying.md +164 -20
- package/documentation/hosted-mcp-migration.md +460 -0
- package/documentation/integrating.md +129 -144
- package/documentation/meta-tools.md +409 -32
- package/documentation/operating.md +27 -3
- package/documentation/operator-tests.md +3 -0
- package/documentation/operator-ui.md +84 -48
- package/documentation/provider-auth-migration.md +16 -63
- package/documentation/provider-migration-0.29.md +10 -5
- package/documentation/provider-migration-0.35.md +975 -0
- package/documentation/providers.md +1347 -0
- package/examples/worker/AGENTS.md +23 -9
- package/examples/worker/README.md +254 -135
- package/examples/worker/alchemy.run.ts +116 -0
- package/examples/worker/gitignore +10 -0
- package/examples/worker/src/connecta.config.ts +23 -21
- package/examples/worker/wrangler.jsonc +14 -5
- package/guides/connecting-agents.md +157 -0
- package/guides/how-it-works.md +156 -0
- package/guides/safety-and-trust.md +153 -0
- package/package.json +30 -50
- package/templates/node/.env.example +3 -0
- package/templates/node/README.md +22 -2
- package/templates/node/docker-compose.yml +1 -0
- package/templates/node/package.json +3 -2
- package/templates/node/src/connecta.config.ts +8 -0
- package/dist/catalog-drift.d.ts +0 -138
- package/dist/catalog-drift.js +0 -578
- package/dist/operator-ui/agents.d.ts +0 -74
- package/dist/operator-ui/agents.js +0 -104
- package/dist/operator-ui/config-summary.js +0 -565
- package/dist/operator-ui/connection-map.js +0 -376
- package/dist/operator-ui/endpoints.js +0 -221
- package/dist/operator-ui/fix-prompts.d.ts +0 -18
- package/dist/operator-ui/fix-prompts.js +0 -87
- package/dist/operator-ui/health.js +0 -52
- package/dist/operator-ui/member-page.js +0 -97
- package/dist/operator-ui/overview.js +0 -123
- package/dist/operator-ui/setup-commands.js +0 -51
- package/dist/operator-ui/view.d.ts +0 -401
- package/dist/operator-ui/view.js +0 -705
- package/dist/providers/_shared/request-token.js +0 -83
- package/dist/providers/_shared/rest/dispatch.js +0 -41
- package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
- package/dist/providers/_shared/rest/tools.d.ts +0 -141
- package/dist/providers/_shared/rest/tools.js +0 -569
- package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
- package/dist/providers/basecamp/index.d.ts +0 -45
- package/dist/providers/basecamp/index.js +0 -997
- package/dist/providers/basecamp/skill.generated.js +0 -9
- package/dist/providers/cloudflare/rest.d.ts +0 -37
- package/dist/providers/cloudflare/rest.js +0 -1097
- package/dist/providers/github/hosted.js +0 -218
- package/dist/providers/github/rest.js +0 -321
- package/dist/providers/infisical/index.d.ts +0 -7
- package/dist/providers/infisical/index.js +0 -713
- package/dist/providers/infisical/skill.generated.js +0 -8
- package/dist/providers/intercom/index.d.ts +0 -24
- package/dist/providers/intercom/index.js +0 -121
- package/dist/providers/intercom/skill.generated.js +0 -8
- package/dist/providers/linear/index.d.ts +0 -55
- package/dist/providers/linear/index.js +0 -537
- package/dist/providers/linear/skill.generated.js +0 -8
- package/dist/providers/mixpanel/index.d.ts +0 -59
- package/dist/providers/mixpanel/index.js +0 -472
- package/dist/providers/mixpanel/operations.js +0 -20
- package/dist/providers/mixpanel/rest.js +0 -603
- package/dist/providers/mixpanel/skill.generated.js +0 -10
- package/dist/providers/mixpanel/value-safety.js +0 -16
- package/dist/providers/notion/index.d.ts +0 -67
- package/dist/providers/notion/index.js +0 -368
- package/dist/providers/notion/openapi.generated.js +0 -75
- package/dist/providers/notion/rest.d.ts +0 -24
- package/dist/providers/notion/rest.js +0 -1226
- package/dist/providers/notion/skill.generated.js +0 -11
- package/dist/providers/notion/value-safety.js +0 -56
- package/dist/providers/revenuecat/index.d.ts +0 -51
- package/dist/providers/revenuecat/index.js +0 -697
- package/dist/providers/revenuecat/skill.generated.js +0 -11
- package/dist/providers/stripe/rest.d.ts +0 -14
- package/dist/providers/stripe/rest.js +0 -402
- package/dist/providers/vercel/rest.d.ts +0 -20
- package/dist/providers/vercel/rest.js +0 -1020
- package/dist/providers/wispr-flow/index.d.ts +0 -14
- package/dist/providers/wispr-flow/index.js +0 -117
- package/dist/providers/wispr-flow/skill.generated.js +0 -8
- /package/dist/providers/github/{key.js → lib/key.js} +0 -0
|
@@ -1,603 +0,0 @@
|
|
|
1
|
-
import { retryAfterMs } from "../../connectors/guarded-fetch.js";
|
|
2
|
-
import { ConnectorCallError } from "../../errors.js";
|
|
3
|
-
import { OperationIndex } from "../_shared/rest/operation-index.js";
|
|
4
|
-
import { callRest, restCall, restTools, restTransport } from "../_shared/rest/tools.js";
|
|
5
|
-
import { valueSafety } from "../_shared/rest/value-safety.js";
|
|
6
|
-
import { openapi } from "./operations.js";
|
|
7
|
-
import { MIXPANEL_VALUE_SAFETY } from "./value-safety.js";
|
|
8
|
-
/** Per runtime and connection, conservatively shared across its projects and API families. */
|
|
9
|
-
export const MIXPANEL_API_ADMISSION = {
|
|
10
|
-
rules: [
|
|
11
|
-
{
|
|
12
|
-
maxConcurrency: 5,
|
|
13
|
-
budget: { kind: "rolling-window", maxCalls: 60, windowMs: 3_600_000 },
|
|
14
|
-
maxQueueSize: 16,
|
|
15
|
-
queueTimeoutMs: 5_000,
|
|
16
|
-
},
|
|
17
|
-
],
|
|
18
|
-
};
|
|
19
|
-
const INDEX = new OperationIndex(openapi, { vendor: "mixpanel", title: "Mixpanel" });
|
|
20
|
-
const SAFETY = valueSafety(MIXPANEL_VALUE_SAFETY, () => INDEX);
|
|
21
|
-
const MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
22
|
-
const MAX_EXPORT_BYTES = 1024 * 1024;
|
|
23
|
-
const DEFAULT_EXPORT_BYTES = 64 * 1024;
|
|
24
|
-
const MAX_IMPORT_BYTES = 10_000_000;
|
|
25
|
-
const encoder = new TextEncoder();
|
|
26
|
-
const READ = { readOnlyHint: true };
|
|
27
|
-
const PROJECT = {
|
|
28
|
-
type: "integer",
|
|
29
|
-
minimum: 1,
|
|
30
|
-
maximum: Number.MAX_SAFE_INTEGER,
|
|
31
|
-
description: "Project id from list_projects or project settings; omitted only with defaultProjectId.",
|
|
32
|
-
};
|
|
33
|
-
const WORKSPACE = {
|
|
34
|
-
type: "integer",
|
|
35
|
-
minimum: 1,
|
|
36
|
-
description: "Data view workspace id when the project requires one.",
|
|
37
|
-
};
|
|
38
|
-
const DATE = {
|
|
39
|
-
type: "string",
|
|
40
|
-
pattern: "^\\d{4}-\\d{2}-\\d{2}$",
|
|
41
|
-
description: "Inclusive date, YYYY-MM-DD. Export uses the vendor's project-age timezone rules.",
|
|
42
|
-
};
|
|
43
|
-
const TEXT = { type: "string", minLength: 1, description: "Documented Mixpanel value." };
|
|
44
|
-
const RAW = {
|
|
45
|
-
type: "boolean",
|
|
46
|
-
description: "Return the redacted vendor envelope instead of the named tool's projection.",
|
|
47
|
-
};
|
|
48
|
-
const OUT = {
|
|
49
|
-
type: "object",
|
|
50
|
-
properties: {
|
|
51
|
-
status: { type: "integer" },
|
|
52
|
-
data: { description: "Redacted Mixpanel result." },
|
|
53
|
-
page: {
|
|
54
|
-
type: "object",
|
|
55
|
-
description: "When hasMore is true, repeat the same filters with the next page and session id.",
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
required: ["status", "data"],
|
|
59
|
-
};
|
|
60
|
-
const EXPORT_PROPERTIES = {
|
|
61
|
-
project_id: PROJECT,
|
|
62
|
-
from_date: DATE,
|
|
63
|
-
to_date: DATE,
|
|
64
|
-
event: {
|
|
65
|
-
type: "array",
|
|
66
|
-
items: { ...TEXT, description: "Exact event name." },
|
|
67
|
-
minItems: 1,
|
|
68
|
-
description: "Optional event-name filter, encoded as JSON on the wire.",
|
|
69
|
-
},
|
|
70
|
-
where: { ...TEXT, description: "Mixpanel segmentation expression to filter events." },
|
|
71
|
-
time_in_ms: { type: "boolean", description: "Request millisecond timestamps instead of the default seconds." },
|
|
72
|
-
limit: {
|
|
73
|
-
type: "integer",
|
|
74
|
-
minimum: 1,
|
|
75
|
-
maximum: 100_000,
|
|
76
|
-
description: "Mixpanel event cap is 100000; Connecta defaults to 1000.",
|
|
77
|
-
},
|
|
78
|
-
max_bytes: {
|
|
79
|
-
type: "integer",
|
|
80
|
-
minimum: 1,
|
|
81
|
-
maximum: MAX_EXPORT_BYTES,
|
|
82
|
-
description: "Connecta's byte ceiling, default 65536 and maximum 1048576; partial trailing lines are discarded.",
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
function record(value) {
|
|
86
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
87
|
-
}
|
|
88
|
-
function invalid(message) {
|
|
89
|
-
throw new ConnectorCallError("invalid_args", message);
|
|
90
|
-
}
|
|
91
|
-
function project(value, defaultProjectId) {
|
|
92
|
-
const id = value ?? defaultProjectId;
|
|
93
|
-
if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 1)
|
|
94
|
-
invalid("Supply a positive project_id from list_projects or configure defaultProjectId; never guess one.");
|
|
95
|
-
return id;
|
|
96
|
-
}
|
|
97
|
-
function dates(input) {
|
|
98
|
-
for (const key of ["from_date", "to_date"]) {
|
|
99
|
-
const value = input[key];
|
|
100
|
-
if (typeof value !== "string" ||
|
|
101
|
-
!/^\d{4}-\d{2}-\d{2}$/.test(value) ||
|
|
102
|
-
!Number.isFinite(Date.parse(value)) ||
|
|
103
|
-
new Date(value).toISOString().slice(0, 10) !== value)
|
|
104
|
-
invalid(`${key} must be a valid YYYY-MM-DD date.`);
|
|
105
|
-
}
|
|
106
|
-
if (String(input.from_date) > String(input.to_date))
|
|
107
|
-
invalid("from_date must be on or before to_date.");
|
|
108
|
-
}
|
|
109
|
-
function retention(query) {
|
|
110
|
-
if ((query.unit === undefined || query.unit === "day") && typeof query.interval === "number" && query.interval > 90) {
|
|
111
|
-
invalid("Day retention intervals must not exceed 90; unit defaults to day.");
|
|
112
|
-
}
|
|
113
|
-
if ((query.retention_type === undefined || query.retention_type === "birth") &&
|
|
114
|
-
(typeof query.born_event !== "string" || query.born_event.trim() === "")) {
|
|
115
|
-
invalid("birth retention, including the default, requires born_event. Use compounded for recurring retention.");
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
function failure(status, headers) {
|
|
119
|
-
if (status === 401)
|
|
120
|
-
return new ConnectorCallError("auth_required", "Mixpanel rejected the service account. An operator must set username:secret in this connection's operator UI.");
|
|
121
|
-
if (status === 403)
|
|
122
|
-
return new ConnectorCallError("provider_permission_denied", "Mixpanel refused access. Check the service account's project membership, data sensitivity and role; import needs Owner or Admin.", { retryable: false });
|
|
123
|
-
if (status === 429)
|
|
124
|
-
return new ConnectorCallError("rate_limited", "Mixpanel's project API budget is spent; respect the retry window.", {
|
|
125
|
-
retryAfterMs: retryAfterMs(headers) ?? 60_000,
|
|
126
|
-
});
|
|
127
|
-
const wait = retryAfterMs(headers);
|
|
128
|
-
if (status >= 500)
|
|
129
|
-
return new ConnectorCallError("unavailable", `Mixpanel returned HTTP ${status}.`, wait === undefined ? {} : { retryAfterMs: wait });
|
|
130
|
-
return new ConnectorCallError("connector_call_failed", `Mixpanel returned HTTP ${status}. Check the documented parameters, project access and region. Import failures can partially ingest valid records; inspect the target before repeating.`, { retryable: false });
|
|
131
|
-
}
|
|
132
|
-
async function authenticate(ctx) {
|
|
133
|
-
const pair = await ctx.credential?.get();
|
|
134
|
-
if (!pair)
|
|
135
|
-
throw new ConnectorCallError("auth_required", "An operator must set the Mixpanel service account as username:secret in this connection's operator UI.");
|
|
136
|
-
const colon = pair.indexOf(":");
|
|
137
|
-
if (colon < 1 || colon === pair.length - 1 || /[\r\n]/.test(pair))
|
|
138
|
-
throw new ConnectorCallError("auth_required", "The Mixpanel service account must be username:secret, with both fields non-empty.");
|
|
139
|
-
const bytes = encoder.encode(pair);
|
|
140
|
-
let binary = "";
|
|
141
|
-
for (const byte of bytes)
|
|
142
|
-
binary += String.fromCharCode(byte);
|
|
143
|
-
return { Authorization: `Basic ${btoa(binary)}` };
|
|
144
|
-
}
|
|
145
|
-
function encode(call) {
|
|
146
|
-
if (call.op.path === "/api/query/engage") {
|
|
147
|
-
const form = new URLSearchParams();
|
|
148
|
-
for (const [key, value] of Object.entries(call.body)) {
|
|
149
|
-
if (value !== undefined)
|
|
150
|
-
form.set(key, Array.isArray(value) || record(value) ? JSON.stringify(value) : String(value));
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
query: call.query,
|
|
154
|
-
rawBody: form.toString(),
|
|
155
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
const query = {};
|
|
159
|
-
for (const [key, value] of Object.entries(call.query)) {
|
|
160
|
-
if (value === undefined)
|
|
161
|
-
continue;
|
|
162
|
-
if (Array.isArray(value))
|
|
163
|
-
query[key] = JSON.stringify(value);
|
|
164
|
-
else if (["string", "number", "boolean"].includes(typeof value))
|
|
165
|
-
query[key] = value;
|
|
166
|
-
else
|
|
167
|
-
invalid(`Mixpanel query ${key} must be a scalar or JSON array.`);
|
|
168
|
-
}
|
|
169
|
-
return { query, ...(call.body !== undefined ? { body: call.body } : {}) };
|
|
170
|
-
}
|
|
171
|
-
/** Every dispatch has one fixed documented operation and residency host. */
|
|
172
|
-
export function mixpanelRest(region, defaultProjectId) {
|
|
173
|
-
const app = region === "us" ? "mixpanel.com" : `${region}.mixpanel.com`;
|
|
174
|
-
const suffix = region === "us" ? "" : `-${region}`;
|
|
175
|
-
const origins = {
|
|
176
|
-
query: `https://${app}`,
|
|
177
|
-
app: `https://${app}`,
|
|
178
|
-
export: `https://data${suffix}.mixpanel.com`,
|
|
179
|
-
ingestion: `https://api${suffix}.mixpanel.com`,
|
|
180
|
-
};
|
|
181
|
-
const transports = Object.fromEntries(Object.entries(origins).map(([server, baseUrl]) => [
|
|
182
|
-
server,
|
|
183
|
-
restTransport({
|
|
184
|
-
provider: "Mixpanel",
|
|
185
|
-
baseUrl,
|
|
186
|
-
maxResponseBytes: MAX_RESPONSE_BYTES,
|
|
187
|
-
timeoutMs: 60_000,
|
|
188
|
-
authenticate,
|
|
189
|
-
}),
|
|
190
|
-
]));
|
|
191
|
-
const vendor = {
|
|
192
|
-
vendor: "mixpanel",
|
|
193
|
-
title: "Mixpanel",
|
|
194
|
-
index: INDEX,
|
|
195
|
-
valueSafety: SAFETY,
|
|
196
|
-
transport: (server) => transports[server ?? "query"] ?? "Mixpanel admits only its reviewed residency hosts.",
|
|
197
|
-
failure,
|
|
198
|
-
encode,
|
|
199
|
-
readPosts: [["POST", "/api/query/engage", "Queries existing profiles; no profile mutation or ingestion occurs."]],
|
|
200
|
-
scope: (call) => call.path === "/api/app/me"
|
|
201
|
-
? call
|
|
202
|
-
: { ...call, query: { ...call.query, project_id: project(call.query.project_id, defaultProjectId) } },
|
|
203
|
-
result: (body) => {
|
|
204
|
-
if (record(body) && (body.status === "error" || body.status === 0 || body.error))
|
|
205
|
-
throw new ConnectorCallError("connector_call_failed", "Mixpanel reported a failure in a successful HTTP response. Check parameters and project access.", { retryable: false });
|
|
206
|
-
return body;
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
|
-
const request = (ctx, method, path, query = {}, body) => callRest(vendor, restCall(vendor, method, path, { query, body }), ctx);
|
|
210
|
-
async function exportEvents(args, ctx) {
|
|
211
|
-
dates(args);
|
|
212
|
-
const maxBytes = args.max_bytes ?? DEFAULT_EXPORT_BYTES;
|
|
213
|
-
const limit = args.limit ?? 1000;
|
|
214
|
-
if (typeof maxBytes !== "number" || !Number.isInteger(maxBytes) || maxBytes < 1 || maxBytes > MAX_EXPORT_BYTES)
|
|
215
|
-
invalid("max_bytes must be 1..1048576, Connecta's export ceiling.");
|
|
216
|
-
if (typeof limit !== "number" || !Number.isInteger(limit) || limit < 1 || limit > 100_000)
|
|
217
|
-
invalid("limit must be 1..100000, Mixpanel's export event ceiling.");
|
|
218
|
-
const call = restCall(vendor, "GET", "/api/2.0/export", { query: { ...args, max_bytes: undefined, limit } });
|
|
219
|
-
const framing = encode(call);
|
|
220
|
-
return transports.export({ method: "GET", path: call.path, query: framing.query, prefixOnly: true }, ctx, async (response) => {
|
|
221
|
-
if (!response.ok) {
|
|
222
|
-
// Cancel the unread error stream without waiting for its first chunk.
|
|
223
|
-
// Await it so a direct connector call also owns the body's lifetime.
|
|
224
|
-
try {
|
|
225
|
-
await response.cancel();
|
|
226
|
-
}
|
|
227
|
-
catch {
|
|
228
|
-
/* The status still describes an unreadable error response. */
|
|
229
|
-
}
|
|
230
|
-
if (ctx.signal?.aborted)
|
|
231
|
-
throw ctx.signal.reason;
|
|
232
|
-
throw failure(response.status, response.headers);
|
|
233
|
-
}
|
|
234
|
-
const { bytes, truncated: byteTruncated } = await response.prefix(maxBytes);
|
|
235
|
-
const text = new TextDecoder().decode(bytes);
|
|
236
|
-
const complete = byteTruncated ? text.slice(0, text.lastIndexOf("\n") + 1) : text;
|
|
237
|
-
const lines = complete.split("\n").filter((line) => line.trim() !== "");
|
|
238
|
-
let events;
|
|
239
|
-
try {
|
|
240
|
-
events = lines.map((line) => JSON.parse(line));
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
throw new ConnectorCallError("connector_call_failed", "Mixpanel returned malformed JSONL.", {
|
|
244
|
-
retryable: false,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
if (events.some((event) => !record(event) || typeof event.event !== "string" || !record(event.properties)))
|
|
248
|
-
throw new ConnectorCallError("connector_call_failed", "Mixpanel returned an unexpected export event shape.", {
|
|
249
|
-
retryable: false,
|
|
250
|
-
});
|
|
251
|
-
const truncated = byteTruncated || events.length >= limit;
|
|
252
|
-
return {
|
|
253
|
-
events: SAFETY.redact(events.slice(0, limit), "GET", call.path),
|
|
254
|
-
bytes_read: bytes.length,
|
|
255
|
-
truncated,
|
|
256
|
-
notice: truncated
|
|
257
|
-
? "Export may omit events at the byte or event cap. There is no cursor; narrow the date range or filters. Never infer absence from this sample."
|
|
258
|
-
: "Complete response for the requested range and filters.",
|
|
259
|
-
};
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
function readTool(name, description, path, properties, required, handler) {
|
|
263
|
-
return {
|
|
264
|
-
name,
|
|
265
|
-
description,
|
|
266
|
-
annotations: READ,
|
|
267
|
-
inputSchema: { type: "object", properties, required, additionalProperties: false },
|
|
268
|
-
outputSchema: OUT,
|
|
269
|
-
handler: handler ??
|
|
270
|
-
(async (args, ctx) => {
|
|
271
|
-
const { raw: _raw, ...query } = args;
|
|
272
|
-
if (query.from_date !== undefined || query.to_date !== undefined)
|
|
273
|
-
dates(query);
|
|
274
|
-
if (path === "/api/query/retention")
|
|
275
|
-
retention(query);
|
|
276
|
-
return await request(ctx, "GET", path, query);
|
|
277
|
-
}),
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
const generic = restTools(vendor)
|
|
281
|
-
.filter((tool) => tool.name !== "mixpanel_api_write")
|
|
282
|
-
.map((tool) => tool.name !== "mixpanel_api_read"
|
|
283
|
-
? {
|
|
284
|
-
...tool,
|
|
285
|
-
handler: async (args, ctx) => {
|
|
286
|
-
const result = (await tool.handler(args, ctx));
|
|
287
|
-
const routed = (row) => ({
|
|
288
|
-
...row,
|
|
289
|
-
tool: row.path === "/import"
|
|
290
|
-
? "import_events"
|
|
291
|
-
: row.path === "/api/query/engage"
|
|
292
|
-
? "query_profiles"
|
|
293
|
-
: "mixpanel_api_read",
|
|
294
|
-
});
|
|
295
|
-
return Array.isArray(result.operations)
|
|
296
|
-
? { ...result, operations: result.operations.map(routed) }
|
|
297
|
-
: routed(result);
|
|
298
|
-
},
|
|
299
|
-
}
|
|
300
|
-
: {
|
|
301
|
-
...tool,
|
|
302
|
-
inputSchema: {
|
|
303
|
-
...tool.inputSchema,
|
|
304
|
-
properties: {
|
|
305
|
-
path: tool.inputSchema.properties.path,
|
|
306
|
-
project_id: PROJECT,
|
|
307
|
-
query: {
|
|
308
|
-
type: "object",
|
|
309
|
-
description: "Documented query parameters; use details to resolve required names. No auth headers or body.",
|
|
310
|
-
},
|
|
311
|
-
select: tool.inputSchema.properties.select,
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
description: "GET a reviewed Mixpanel operation on this region's hosts. No arbitrary URLs, profile updates or service-account administration.",
|
|
315
|
-
handler: async (args, ctx) => {
|
|
316
|
-
if (args.method !== undefined && args.method !== "GET")
|
|
317
|
-
invalid("mixpanel_api_read permits GET only; use query_profiles for Engage and import_events for ingestion.");
|
|
318
|
-
const path = args.path;
|
|
319
|
-
const query = { ...(record(args.query) ? args.query : {}) };
|
|
320
|
-
if (args.project_id !== undefined) {
|
|
321
|
-
if (query.project_id !== undefined && query.project_id !== args.project_id)
|
|
322
|
-
invalid("Conflicting project_id values; use the top-level project_id.");
|
|
323
|
-
query.project_id = args.project_id;
|
|
324
|
-
}
|
|
325
|
-
if (path === "/api/2.0/export") {
|
|
326
|
-
const call = restCall(vendor, "GET", path, { query });
|
|
327
|
-
INDEX.check(call.op, call.query, call.body);
|
|
328
|
-
let event = query.event;
|
|
329
|
-
if (typeof event === "string") {
|
|
330
|
-
try {
|
|
331
|
-
event = JSON.parse(event);
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
invalid("event must be a JSON array of names.");
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (event !== undefined &&
|
|
338
|
-
(!Array.isArray(event) || event.length === 0 || !event.every((name) => typeof name === "string")))
|
|
339
|
-
invalid("event must be a JSON array of names.");
|
|
340
|
-
if (args.select !== undefined)
|
|
341
|
-
invalid("Generic export does not accept select; reduce the bounded export data in execute_code.");
|
|
342
|
-
return { status: 200, data: await exportEvents({ ...query, event }, ctx) };
|
|
343
|
-
}
|
|
344
|
-
if (query.from_date !== undefined || query.to_date !== undefined)
|
|
345
|
-
dates(query);
|
|
346
|
-
if (path === "/api/query/retention")
|
|
347
|
-
retention(query);
|
|
348
|
-
return await tool.handler({ ...args, query }, ctx);
|
|
349
|
-
},
|
|
350
|
-
});
|
|
351
|
-
const tools = [
|
|
352
|
-
...generic,
|
|
353
|
-
readTool("list_projects", "Read service-account identity and reachable project metadata. Project tokens and credentials are redacted.", "/api/app/me", {}, []),
|
|
354
|
-
{
|
|
355
|
-
name: "export_events",
|
|
356
|
-
description: "Export bounded raw JSONL events for a date range. Truncation is explicit; narrow filters to establish absence.",
|
|
357
|
-
annotations: READ,
|
|
358
|
-
inputSchema: {
|
|
359
|
-
type: "object",
|
|
360
|
-
properties: EXPORT_PROPERTIES,
|
|
361
|
-
required: ["from_date", "to_date"],
|
|
362
|
-
additionalProperties: false,
|
|
363
|
-
},
|
|
364
|
-
outputSchema: {
|
|
365
|
-
type: "object",
|
|
366
|
-
properties: {
|
|
367
|
-
events: { type: "array", items: { type: "object" } },
|
|
368
|
-
bytes_read: { type: "integer" },
|
|
369
|
-
truncated: { type: "boolean" },
|
|
370
|
-
notice: { type: "string" },
|
|
371
|
-
},
|
|
372
|
-
required: ["events", "bytes_read", "truncated", "notice"],
|
|
373
|
-
},
|
|
374
|
-
handler: exportEvents,
|
|
375
|
-
},
|
|
376
|
-
readTool("get_profile_activity", "Read one user's events, ordered by timestamp. Mixpanel's activity endpoint is in maintenance mode; use export for complete analysis.", "/api/query/stream/query", {
|
|
377
|
-
project_id: PROJECT,
|
|
378
|
-
workspace_id: WORKSPACE,
|
|
379
|
-
distinct_id: { ...TEXT, description: "Exact distinct_id of the user whose event history to read." },
|
|
380
|
-
from_date: DATE,
|
|
381
|
-
to_date: DATE,
|
|
382
|
-
raw: RAW,
|
|
383
|
-
}, ["distinct_id", "from_date", "to_date"], async (args, ctx) => {
|
|
384
|
-
dates(args);
|
|
385
|
-
const { distinct_id, raw, ...query } = args;
|
|
386
|
-
const result = await request(ctx, "GET", "/api/query/stream/query", { ...query, distinct_ids: [distinct_id] });
|
|
387
|
-
if (raw)
|
|
388
|
-
return result;
|
|
389
|
-
const body = result.data;
|
|
390
|
-
const events = record(body) && record(body.results) ? body.results.events : undefined;
|
|
391
|
-
if (!Array.isArray(events) ||
|
|
392
|
-
events.some((e) => !record(e) || !record(e.properties) || typeof e.properties.time !== "number"))
|
|
393
|
-
throw new ConnectorCallError("connector_call_failed", "Mixpanel returned an unexpected activity shape.", {
|
|
394
|
-
retryable: false,
|
|
395
|
-
});
|
|
396
|
-
return {
|
|
397
|
-
status: result.status,
|
|
398
|
-
data: {
|
|
399
|
-
events: [...events].sort((a, b) => a.properties.time - b.properties.time),
|
|
400
|
-
notice: "Maintenance endpoint; Mixpanel documents no completeness guarantee or pagination cursor.",
|
|
401
|
-
},
|
|
402
|
-
};
|
|
403
|
-
}),
|
|
404
|
-
readTool("query_profiles", "Query existing Engage profiles. This POST is read-only; continue with the returned session_id and next page, preserving filters.", "/api/query/engage", {
|
|
405
|
-
project_id: PROJECT,
|
|
406
|
-
workspace_id: WORKSPACE,
|
|
407
|
-
distinct_id: { ...TEXT, description: "Profile distinct id." },
|
|
408
|
-
distinct_ids: { type: "array", items: TEXT, description: "Exact profile ids, JSON encoded." },
|
|
409
|
-
where: { ...TEXT, description: "Mixpanel profile selector expression." },
|
|
410
|
-
output_properties: { type: "array", items: TEXT, description: "Profile property names to return." },
|
|
411
|
-
session_id: { ...TEXT, description: "Session from the previous response; repeat it unchanged." },
|
|
412
|
-
page: { type: "integer", minimum: 0, description: "Zero-based page; a nonzero page requires session_id." },
|
|
413
|
-
raw: RAW,
|
|
414
|
-
}, [], async (args, ctx) => {
|
|
415
|
-
const { project_id, workspace_id, raw, ...body } = args;
|
|
416
|
-
if (typeof body.page === "number" && body.page > 0 && !body.session_id)
|
|
417
|
-
invalid("A nonzero profile page requires session_id from the previous response.");
|
|
418
|
-
if (body.distinct_id !== undefined && body.distinct_ids !== undefined)
|
|
419
|
-
invalid("Use distinct_id or distinct_ids, not both.");
|
|
420
|
-
const result = await request(ctx, "POST", "/api/query/engage", { project_id, workspace_id }, body);
|
|
421
|
-
if (raw)
|
|
422
|
-
return result;
|
|
423
|
-
const data = result.data;
|
|
424
|
-
if (!record(data) ||
|
|
425
|
-
!Array.isArray(data.results) ||
|
|
426
|
-
typeof data.page_size !== "number" ||
|
|
427
|
-
data.page_size < 1 ||
|
|
428
|
-
typeof data.page !== "number" ||
|
|
429
|
-
typeof data.session_id !== "string")
|
|
430
|
-
throw new ConnectorCallError("connector_call_failed", "Mixpanel returned an unexpected profile page shape.", {
|
|
431
|
-
retryable: false,
|
|
432
|
-
});
|
|
433
|
-
const hasMore = data.results.length >= data.page_size;
|
|
434
|
-
return {
|
|
435
|
-
...result,
|
|
436
|
-
data: {
|
|
437
|
-
results: data.results,
|
|
438
|
-
total: data.total,
|
|
439
|
-
page: data.page,
|
|
440
|
-
session_id: data.session_id,
|
|
441
|
-
page_size: data.page_size,
|
|
442
|
-
},
|
|
443
|
-
page: { hasMore, ...(hasMore ? { next: String(data.page + 1), param: "page", in: "body" } : {}) },
|
|
444
|
-
};
|
|
445
|
-
}),
|
|
446
|
-
readTool("get_insights_report", "Read a saved Insights report by bookmark_id from its report URL. The id is project-local.", "/api/query/insights", {
|
|
447
|
-
project_id: PROJECT,
|
|
448
|
-
workspace_id: WORKSPACE,
|
|
449
|
-
bookmark_id: { ...PROJECT, description: "Saved Insights bookmark id from the report URL." },
|
|
450
|
-
}, ["bookmark_id"]),
|
|
451
|
-
readTool("get_funnel_report", "Read a saved funnel by funnel_id and date range. Use mixpanel_api_read on /api/query/funnels/list to resolve ids.", "/api/query/funnels", {
|
|
452
|
-
project_id: PROJECT,
|
|
453
|
-
workspace_id: WORKSPACE,
|
|
454
|
-
funnel_id: { ...PROJECT, description: "Saved funnel id from the vendor list or report URL." },
|
|
455
|
-
from_date: DATE,
|
|
456
|
-
to_date: DATE,
|
|
457
|
-
}, ["funnel_id", "from_date", "to_date"]),
|
|
458
|
-
readTool("query_retention", "Query retention by events and date range. Mixpanel documents no saved Retention bookmark-id endpoint.", "/api/query/retention", {
|
|
459
|
-
project_id: PROJECT,
|
|
460
|
-
workspace_id: WORKSPACE,
|
|
461
|
-
from_date: DATE,
|
|
462
|
-
to_date: DATE,
|
|
463
|
-
event: { ...TEXT, description: "Returning event name." },
|
|
464
|
-
born_event: { ...TEXT, description: "Initial event name for birth retention." },
|
|
465
|
-
retention_type: {
|
|
466
|
-
type: "string",
|
|
467
|
-
enum: ["birth", "compounded"],
|
|
468
|
-
description: "First-time or recurring retention, respectively.",
|
|
469
|
-
},
|
|
470
|
-
interval: {
|
|
471
|
-
type: "integer",
|
|
472
|
-
minimum: 1,
|
|
473
|
-
description: "Bucket interval; Mixpanel caps day intervals at 90.",
|
|
474
|
-
},
|
|
475
|
-
interval_count: {
|
|
476
|
-
type: "integer",
|
|
477
|
-
minimum: 1,
|
|
478
|
-
description: "Number of buckets, in addition to the zeroth interval.",
|
|
479
|
-
},
|
|
480
|
-
unit: { type: "string", enum: ["day", "week", "month"], description: "Interval unit, default day." },
|
|
481
|
-
}, ["from_date", "to_date"]),
|
|
482
|
-
{
|
|
483
|
-
name: "import_events",
|
|
484
|
-
description: "Import up to 2000 events and 10MB uncompressed JSON. Owner/Admin required; validation errors may still ingest valid events. Never replay automatically.",
|
|
485
|
-
annotations: { readOnlyHint: false },
|
|
486
|
-
inputSchema: {
|
|
487
|
-
type: "object",
|
|
488
|
-
properties: {
|
|
489
|
-
project_id: PROJECT,
|
|
490
|
-
events: {
|
|
491
|
-
type: "array",
|
|
492
|
-
minItems: 1,
|
|
493
|
-
maxItems: 2000,
|
|
494
|
-
description: "Mixpanel event batch; supply stable insert ids for deduplication.",
|
|
495
|
-
items: {
|
|
496
|
-
type: "object",
|
|
497
|
-
description: "One event, smaller than 1MB uncompressed.",
|
|
498
|
-
properties: {
|
|
499
|
-
event: { ...TEXT, description: "Exact event name." },
|
|
500
|
-
properties: {
|
|
501
|
-
type: "object",
|
|
502
|
-
description: "Event properties with time (integer seconds/milliseconds), distinct_id (string) and stable $insert_id (1..36 alphanumeric/hyphen bytes). Fewer than 255 keys; nested depth 3.",
|
|
503
|
-
},
|
|
504
|
-
},
|
|
505
|
-
required: ["event", "properties"],
|
|
506
|
-
additionalProperties: false,
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
},
|
|
510
|
-
required: ["events"],
|
|
511
|
-
additionalProperties: false,
|
|
512
|
-
},
|
|
513
|
-
outputSchema: OUT,
|
|
514
|
-
handler: async (args, ctx) => {
|
|
515
|
-
const events = args.events;
|
|
516
|
-
validateImport(events);
|
|
517
|
-
return await request(ctx, "POST", "/import", { project_id: args.project_id, strict: "1" }, events);
|
|
518
|
-
},
|
|
519
|
-
},
|
|
520
|
-
];
|
|
521
|
-
return {
|
|
522
|
-
tools,
|
|
523
|
-
async testCredential(value, ctx) {
|
|
524
|
-
try {
|
|
525
|
-
await request({ ...ctx, credential: { get: async () => value, getAll: async () => ({ value }) } }, "GET", "/api/app/me");
|
|
526
|
-
return {
|
|
527
|
-
ok: true,
|
|
528
|
-
message: "Authenticated Mixpanel service account; list_projects returns its reachable project metadata.",
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
catch (error) {
|
|
532
|
-
return {
|
|
533
|
-
ok: false,
|
|
534
|
-
message: error instanceof ConnectorCallError ? error.message : "Mixpanel rejected the service account.",
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
},
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
function validateImport(events) {
|
|
541
|
-
if (events.length < 1 || events.length > 2000)
|
|
542
|
-
invalid("Mixpanel import requires 1..2000 events.");
|
|
543
|
-
if (encoder.encode(JSON.stringify(events)).length > MAX_IMPORT_BYTES)
|
|
544
|
-
invalid("Mixpanel import is limited to 10MB of uncompressed JSON per batch.");
|
|
545
|
-
const walk = (value, depth) => {
|
|
546
|
-
if (Array.isArray(value)) {
|
|
547
|
-
if (value.length >= 255)
|
|
548
|
-
invalid("Mixpanel array properties must have fewer than 255 elements.");
|
|
549
|
-
for (const item of value)
|
|
550
|
-
walk(item, depth);
|
|
551
|
-
}
|
|
552
|
-
else if (record(value)) {
|
|
553
|
-
if (depth > 3)
|
|
554
|
-
invalid("Mixpanel nested object properties may be at most three levels deep.");
|
|
555
|
-
if (Object.keys(value).length >= 255)
|
|
556
|
-
invalid("Mixpanel objects must have fewer than 255 keys.");
|
|
557
|
-
for (const item of Object.values(value))
|
|
558
|
-
walk(item, depth + 1);
|
|
559
|
-
}
|
|
560
|
-
};
|
|
561
|
-
for (const event of events) {
|
|
562
|
-
if (!record(event) || !record(event.properties))
|
|
563
|
-
invalid("Each Mixpanel event requires a properties object.");
|
|
564
|
-
const props = event.properties;
|
|
565
|
-
if (typeof props.time !== "number" ||
|
|
566
|
-
!Number.isSafeInteger(props.time) ||
|
|
567
|
-
typeof props.distinct_id !== "string" ||
|
|
568
|
-
typeof props.$insert_id !== "string" ||
|
|
569
|
-
!/^[A-Za-z0-9-]{1,36}$/.test(props.$insert_id))
|
|
570
|
-
invalid("Mixpanel import needs integer time, string distinct_id and a 1..36-byte alphanumeric/hyphen $insert_id.");
|
|
571
|
-
const forbidden = new Set([
|
|
572
|
-
"00000000-0000-0000-0000-000000000000",
|
|
573
|
-
"anon",
|
|
574
|
-
"anonymous",
|
|
575
|
-
"nil",
|
|
576
|
-
"none",
|
|
577
|
-
"null",
|
|
578
|
-
"n/a",
|
|
579
|
-
"na",
|
|
580
|
-
"undefined",
|
|
581
|
-
"unknown",
|
|
582
|
-
"<nil>",
|
|
583
|
-
"0",
|
|
584
|
-
"-1",
|
|
585
|
-
"true",
|
|
586
|
-
"false",
|
|
587
|
-
"[]",
|
|
588
|
-
"{}",
|
|
589
|
-
]);
|
|
590
|
-
if (forbidden.has(props.distinct_id) || forbidden.has(props.$insert_id))
|
|
591
|
-
invalid("Mixpanel refuses this distinct_id or $insert_id placeholder value.");
|
|
592
|
-
if (encoder.encode(JSON.stringify(event)).length >= 1_000_000)
|
|
593
|
-
invalid("Each Mixpanel event must be smaller than 1MB of uncompressed JSON.");
|
|
594
|
-
walk(event.properties, 0);
|
|
595
|
-
const time = event.properties.time;
|
|
596
|
-
const earliest = Date.UTC(1971, 0, 1);
|
|
597
|
-
const latest = Date.now() + 3_600_000;
|
|
598
|
-
const seconds = time >= earliest / 1000 && time <= latest / 1000;
|
|
599
|
-
const milliseconds = time >= earliest && time <= latest;
|
|
600
|
-
if (!seconds && !milliseconds)
|
|
601
|
-
invalid("Mixpanel event time must be after 1971-01-01 and no more than one hour in the future.");
|
|
602
|
-
}
|
|
603
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
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`, 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
|
-
"instructionsHeading": "Account instructions",
|
|
5
|
-
"fragments": {
|
|
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- `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
|
-
}
|
|
10
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
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
|
-
};
|