From ada95eebce3596b1c1146f27244f119c2eaf3626 Mon Sep 17 00:00:00 2001 From: Zackery Bartolome Date: Sun, 30 Aug 2026 12:50:08 -0400 Subject: [PATCH] Prepare 0.21.1 and reconcile hosted catalogs (#512) --- CHANGELOG.md | 44 ++++++++++++++++++++++++++++++++ documentation/auth.md | 2 +- documentation/linear.md | 10 +++++--- documentation/mixpanel.md | 8 +++--- documentation/operations.md | 8 +++--- documentation/revenuecat.md | 42 +++++++++++++++--------------- documentation/stripe.md | 27 ++++++++++---------- documentation/upgrading.md | 15 ++++++----- package-lock.json | 4 +-- package.json | 2 +- records/provider-audit.md | 14 +++++----- src/providers/linear.ts | 7 +++++ src/providers/mixpanel.ts | 10 +++++--- src/providers/revenuecat.ts | 3 ++- src/providers/stripe.ts | 13 +++++----- src/version.ts | 2 +- templates/node/package.json | 2 +- test/linear-provider.test.ts | 10 +++++++- test/mixpanel-provider.test.ts | 7 ++++- test/revenuecat-provider.test.ts | 21 ++++++++++----- test/stripe-provider.test.ts | 18 ++++++++----- 21 files changed, 177 insertions(+), 92 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 651aca2..50e97a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,50 @@ All notable changes to this package are documented here. +## 0.21.1 — 2026-08-30 + +This patch aligns the handwritten Cloudflare and Notion contracts and the four +hosted-provider manifests with their current catalogs. Existing deployments +need no configuration or storage migration. Cloudflare programs that depend on +provider fields outside the maintained `get_zone_setting` or +`get_worker_settings` projections should pass `raw: true`. Stripe's server has +retired three dedicated tool names, so programs that hardcoded them must search +the current catalog and use the generic API tools or `stripe_analytics`. + +### Added + +- **Current Cloudflare jurisdictions.** All eight R2 bucket tools accept the + `us` jurisdiction alongside `default`, `eu`, and `fedramp`. KV namespace + creation accepts its separate `eu`, `fedramp`, and `us` set and preserves + jurisdiction on namespace reads (#498, #499). +- **Current hosted-provider classifications.** Linear's workspace, template, + and issue-sharing tools join its plan-aware reviewed superset. RevenueCat's + refund-request preferences are read-only, and Mixpanel's metadata fill is a + destructive Lexicon update (#512). + +### Changed + +- **Useful Cloudflare result schemas.** All 48 named tools now declare their + maintained output keys. Zone settings and Worker settings project stable + camel-case fields by default and expose the provider response through + `raw: true`; rulesets, deployments, KV bulk operations, R2 CORS, and Pages + reuse named schemas instead of an empty object contract (#488). +- **Stripe's current eleven tools.** Account listing, account-management links, + and analytics replace the retired account-info, refund, and report tools. + The integration planner and analytics are additive writes because their tool + boundaries can create provider-side guide or query-run state (#512). + +### Fixed + +- Worker settings retain VPC binding identity and observability fields, + rulesets retain origin-range metadata, and zone setting ids include + Cloudflare's current WebMCP settings (#500, #501, #502, #503). +- Notion preserves every documented `verification.does_not_equal` value, and + Cloudflare's recorded KV bulk and zone-setting specifications match the + current published documents (#504, #509). +- Mixpanel records the current schemas for all 64 classified tools, including + the three experiment contracts that changed since the prior review (#512). + ## 0.21.0 — 2026-08-28 Cloudflare Access becomes the canonical interactive-auth path for Worker diff --git a/documentation/auth.md b/documentation/auth.md index 83d8e15..cc89b0f 100644 --- a/documentation/auth.md +++ b/documentation/auth.md @@ -65,7 +65,7 @@ Worker-level Access runs before every connecta route. Consequently: OAuth discovery paths when Managed OAuth is enabled. The [Worker example](../examples/worker/) carries the complete deployment shape -and the [upgrade guide](./upgrading.md#0200--0210) gives the reversible Clerk +and the [upgrade guide](./upgrading.md#0200--0211) gives the reversible Clerk migration. ## Clerk configuration is checked at construction diff --git a/documentation/linear.md b/documentation/linear.md index 2ae7e0a..c993dd1 100644 --- a/documentation/linear.md +++ b/documentation/linear.md @@ -110,9 +110,10 @@ the connector is present and reports `auth_required`. See The wrapper classifies Linear's documented `list_*`, `get_*`, and `search_documentation` tools as reads, and its `save_*`, `create_*`, `delete_*`, -`resolve_*`, `submit_*`, and `merge_*` tools as writes. An unfamiliar tool the -downstream leaves unannotated fails closed onto `call_destructive_tool` until a -Connecta release reviews it. +`resolve_*`, `submit_*`, `merge_*`, `share_issue`, and `unshare_issue` tools as +writes. Sharing changes an existing issue's access, so both halves are +destructive. An unfamiliar tool the downstream leaves unannotated fails closed +onto `call_destructive_tool` until a Connecta release reviews it. That classification fills in downstream silence and otherwise preserves explicit annotations. A tool on the read allowlist arriving with @@ -136,7 +137,8 @@ though some calls only create. The genuine creates are `create_issue_label` and ## The catalog is not a fixed set Linear's hosted `tools/list` varies by workspace plan and enabled features: -customer requests, releases, and code review do not appear in every workspace. +customer requests, releases, code review, templates, and explicit issue sharing +do not appear in every workspace. The maintained allowlists are therefore a superset — a classified name a workspace never returns costs nothing, and a genuinely new tool fails closed. Agents should search this connector's catalog for what the workspace actually diff --git a/documentation/mixpanel.md b/documentation/mixpanel.md index 492cb6c..eb94cdd 100644 --- a/documentation/mixpanel.md +++ b/documentation/mixpanel.md @@ -84,7 +84,7 @@ the maintained guide carries the missing call guidance: A read-only live audit on 2026-08-13 confirmed all three refusals against the US hosted endpoint. They are reported upstream as [`mixpanel/mixpanel-headless#202`](https://github.com/mixpanel/mixpanel-headless/issues/202). -The vetted catalog records the same audit's schema digests for all 63 tools, +The vetted catalog records current schema digests for all 64 tools, so a later schema correction or regression appears by tool name in the maintainer drift check. The guide can then shrink when the downstream schema becomes complete; Connecta does not absorb the defect permanently. @@ -110,8 +110,10 @@ rest) leave `destructiveHint` unset; `readOnlyHint: false` already routes them through the destructive path, and asserting destruction only inflates the approval copy the host shows a human. -Experiments and Feature Flags — 15 of the 63 classified tools — are Mixpanel -beta surfaces. Expect their names and schemas to move faster than the rest. +Experiments and Feature Flags are Mixpanel beta surfaces. Their three changed +schemas were reviewed again on 2026-08-30. The same review added +`Fill-Event-Metadata` as a destructive write because it applies generated names +and descriptions to existing Lexicon events. ## Rate limits diff --git a/documentation/operations.md b/documentation/operations.md index eef7817..80ff1ac 100644 --- a/documentation/operations.md +++ b/documentation/operations.md @@ -247,11 +247,11 @@ in. | `executor-admission.test.ts` | the portable bounded FIFO both pools use: active and queue ceilings, stable retryable overload, queue timeout, cancellation removal, idempotent release, shutdown | | `guarded-fetch.test.ts` | the guarded transport — construction, request building, destination confinement, and response handling | | `guest-api-contract.test.ts` | the shared guest contract on the Dynamic Worker, including caught call, typed inline describe recovery, discovery, utility, batch, and budget failure codes; plus the real authority boundary — local `data:` fetch, denied egress, unresolved DNS, empty environment paths, unavailable filesystem/HTTP builtins, and present runtime globals | -| `linear-provider.test.ts` | the Linear proxy's construction, classification, and guide | +| `linear-provider.test.ts` | the Linear proxy's construction, guide, plan-aware catalog superset, and current workspace, template, and issue-sharing classifications | | `meta-tools-call.test.ts` | registry-backed calls: structured errors, truncation and `get_result`, per-connector result bounds, JSON representation failures, MCP content bounds, and offset alignment | | `meta-tools-search.test.ts` | registry-backed discovery: bounded search with page and address maxima, compact and JSON schemas with constraints, typed describe recovery and suggestions, and structured-result compatibility | | `meta-tools.test.ts` | the remaining registry-backed meta-tools: the complete on-demand usage skill, connector-guide selection and summary bounds, stored-credential drift, catalog health, authorization, probe timeouts, and unavailable or unknown browse recovery | -| `mixpanel-provider.test.ts` | the Mixpanel proxy, its conditional-input guide and complete reviewed schema-digest manifest | +| `mixpanel-provider.test.ts` | the Mixpanel proxy, its conditional-input guide, destructive metadata fill, and complete 64-tool schema-digest manifest | | `notion-provider.test.ts` | Notion's deliberate tool surface, including declined expanded page inputs, request construction, lean projections, both pagination conventions, error mapping, and writes | | `operator-boundary.test.ts` | the operator row of the decisions table, after every mutation route: authentication material managed without moving a declared structure, and the one honest exception — a credential write making a remote catalog appear, which is discovery arriving, not an operator editing the deployment | | `operator-store.test.ts` | `src/operator-ui/app/store.ts` against a fake browser: the Clerk listener, ambient Access requests without a browser-readable token, `gate()`, the generation fence, and the request path | @@ -263,11 +263,11 @@ in. | `remote-mcp-pagination.test.ts` | the `tools/list` cursor chain in both directions — exact cursor handoff, first-wins dedup, a failed later page rejecting rather than returning its prefix, the runaway backstops, the tool-metadata re-prime across pages, and paginated catalogs reaching the discovery path | | `request-admission.test.ts` | `/mcp` bounded before auth, the stable 503 and `Retry-After`, health and operator responsiveness under saturation, payload-free counters, queued cancellation, shutdown rejection while active work drains, and the separate fallback code pool | | `result-shapes.test.ts` | passive output-shape learning: value-free bounded inference, merging, 256-entry LRU eviction, 24-hour expiry, runtime isolation, read-only admission, declared-schema precedence, definition-change invalidation, discovery provenance, and failure isolation | -| `revenuecat-provider.test.ts` | the RevenueCat proxy's per-project key scoping and account-wide OAuth guides, its purpose-bearing summary, the argued borderline verdicts in its digest-free manifest, and the deliberately unclassified `render-paywall-screenshot` | +| `revenuecat-provider.test.ts` | the RevenueCat proxy's per-project key scoping and account-wide OAuth guides, its purpose-bearing summary, the refund-preference read and argued borderline verdicts in its digest-free manifest, and the deliberately unclassified `render-paywall-screenshot` | | `server.test.ts` | end-to-end `/mcp` (401 → compact initialize instructions → seven compact definitions with bounded connector inventory and exact model-only Apps metadata → complete usage skill → `call_tool`), conditional guide pointers, open routes, Clerk `.well-known` metadata without network, code mode, and deferred catalog reads through both discovery surfaces | | `server-route-contracts.test.ts` | the route contracts `server.ts` must keep byte-identical: every built-in answered ahead of connector routes inside the security wrapper, open data-free shells with framing denied, per-route auth and same-origin requirements with exact 401/403/405 bodies, and OAuth `verifyState`-before-`finishAuth` ordering | | `startup-warnings.test.ts` | every construction-time `logger.warn` and, as importantly, the conditions that must *not* trigger one: open mode with a credential or OAuth connector, `publicUrl` unset beside OAuth, dropped branding and `uiAuth` URLs, a missing `verifyState`, a credential test-hook mismatch, and an unusable `calls.maxResultBytes` | -| `stripe-provider.test.ts` | the Stripe proxy's mixed-mode OAuth and fixed-mode header contracts, admission, exact account selectors, and no-guess rule | +| `stripe-provider.test.ts` | the Stripe proxy's mixed-mode OAuth and fixed-mode header contracts, current eleven-tool classifications, admission, exact account selectors, and no-guess rule | | `operator-view.test.ts` | the app's pure state rules from `view.ts`: filtering, page routing, capability states, activity summaries, drift display, and identity reset | | `ui-credentials.test.ts` | credential-management routes: save, test, delete, validation, authentication, same-origin checks, and multi-field credential shapes | | `ui.test.ts` | the server shell and remaining `/ui/*` routes: gated `/ui/data` with broken-connector isolation and registry-owned catalog-observation containment, plus the URL safety gates | diff --git a/documentation/revenuecat.md b/documentation/revenuecat.md index db59dca..44c8ee9 100644 --- a/documentation/revenuecat.md +++ b/documentation/revenuecat.md @@ -152,17 +152,18 @@ names the `authorize_connector` recovery. A permission gap, a plan restriction, or a rejected argument arrives as RevenueCat wrote it and is not an authorization problem. -## The ninety-five tools, and what they are classified as +## The ninety-six tools, and what they are classified as RevenueCat's [tool reference](https://www.revenuecat.com/docs/tools/mcp/tools-reference), -read on **2026-08-18**, documents ninety-five tools in a Read/Write table. -Ninety-four carry an access column and are classified here: **50 read-only, 15 -additive writes, 29 destructive writes.** +read on **2026-08-30**, documents ninety-six tools. Ninety-five carry an access +column and are classified here: **51 read-only, 15 additive writes, 29 +destructive writes.** Reads are every `Read` row, verbatim — the nine project and app reads, the four product reads, the entitlement, offering, targeting, paywall, customer, virtual -currency, chart, webhook, and SDK reads, and `get-paywall-ai-task`. +currency, chart, webhook, and SDK reads, `get-paywall-ai-task`, and +`get-refund-request-preferences`. Writes follow the verb where the verb is honest: `archive-*` and `unarchive-*` flip an existing object's active state, `update-*`, `delete-*`, `publish-*`, @@ -191,14 +192,15 @@ beside the row: `create-webhook-integration` deserves a sentence too. No existing integration changes, so the verb reads additive — but with filters omitted the new one "starts delivering" every customer event in the project to a URL the caller -typed. Customer data leaving the account is the `create_refund` argument again: -filed destructive on consequence, so the approval copy says what is at stake. +typed. Customer data leaving the account makes it destructive on consequence, +so the approval copy says what is at stake. **`render-paywall-screenshot` is deliberately unclassified.** RevenueCat's -reference gives it no access column at all, so no release has reviewed what it -does. It fails closed onto `call_destructive_tool` rather than being guessed -into the read path because its name sounds harmless (P5). The guide names it, -so an agent does not read the approval prompt as a bug. +reference gives it no access column. The current live server explicitly marks +it read-only, so that catalog keeps it callable from `execute_code`; if a later +catalog omits the annotation, it fails closed onto `call_destructive_tool`. +Connecta preserves the provider's current annotation without inventing a +release classification from the tool's harmless-sounding name (P5). That classification fills in downstream silence and otherwise preserves explicit annotations. A tool on the read allowlist arriving with `destructiveHint: true` @@ -242,7 +244,7 @@ API v2 meters per minute and **per domain** | Charts & Metrics | 25 | A `ConnectorCallAdmissionPolicy` carries exactly one rule, so a connector-wide -budget has to pick one of those six numbers for all ninety-five tools. +budget has to pick one of those six numbers for all ninety-six tools. Transcribing 25 would throttle a customer read loop to a nineteenth of its documented allowance; transcribing 480 would leave a chart sweep unprotected. Neither is the provider's limit, and both would look like RevenueCat being @@ -281,15 +283,13 @@ still needs restrained use. ## What is not verified -- **Every tool name is transcribed from RevenueCat's published reference on - 2026-08-18, not read from a live catalog.** No maintainer ran this against a - project with a real key before it shipped. A name the reference lists and the - server does not serve costs nothing; a tool the server serves and the - reference omits fails closed. The maintainer-run drift check with - `CONNECTA_DRIFT_REVENUECAT_KEY` set is what turns that into a finding with a - name attached. -- **No input or output schema has been read**, which is why the manifest - carries no digests. +- **The 2026-08-30 live review used a project-scoped catalog.** It proves the + additions that catalog serves, including `get-refund-request-preferences`, + but cannot prove a globally documented tool was removed. The manifest stays + a superset because plan, platform, and credential scope hide tools. +- **No complete schema set has been recorded**, which is why the manifest + carries no digests. The review read the new live schemas, but its scoped + catalog omitted many classified writes. - **Whether `render-paywall-screenshot` mutates anything.** It has no access column, and guessing is exactly what P5 exists to prevent. diff --git a/documentation/stripe.md b/documentation/stripe.md index 057b764..e5f11de 100644 --- a/documentation/stripe.md +++ b/documentation/stripe.md @@ -139,38 +139,39 @@ boots but cannot list tools is usually a dashboard toggle, not a bad key. ## The eleven tools, and what they are classified as -Stripe documents eleven tools on the hosted server. Seven are reads: +Stripe currently serves eleven tools. Seven are reads: `stripe_api_search`, `stripe_api_details`, `stripe_api_read`, -`get_stripe_account_info`, `get_balance_summary`, -`search_stripe_documentation`, `stripe_implementation_planner`. +`get_balance_summary`, `list_available_accounts_or_orgs`, +`manage_stripe_accounts`, and `search_stripe_documentation`. Four are writes: -`stripe_api_write` and `create_refund` are classified destructive; -`stripe_report` and `send_stripe_mcp_feedback` are additive. +`stripe_api_write` is destructive. `stripe_implementation_planner`, +`stripe_analytics`, and `send_stripe_mcp_feedback` are additive. Two of those deserve a sentence. `stripe_api_read` is a read because Stripe documents it as the `GET` half of a generic pair — the tool is the read boundary, not whichever endpoint an agent names inside it, and its sibling `stripe_api_write` carries every `POST`, `PATCH`, `PUT`, and `DELETE`. -`create_refund` is filed destructive despite its name: it reverses a settled -charge and moves money back out, which is a mutation of something that already -exists rather than a fresh object appearing beside it. Additive writes -(`stripe_report`, `send_stripe_mcp_feedback`) leave `destructiveHint` unset; +`stripe_implementation_planner` creates and continues provider-side guide +state. `stripe_analytics` combines retrieval with query-run creation behind one +tool. Stripe marks both not read-only and not destructive, so Connecta records +them as additive writes. They leave `destructiveHint` unset. Their explicit `readOnlyHint: false` already routes them through `call_destructive_tool`, and -asserting destruction only inflates the approval copy the host shows a human. +asserting destruction would misstate their effect. That classification fills in downstream silence and otherwise preserves -explicit annotations. It supplies the annotations Stripe leaves unset — Stripe -documents no MCP annotations at all. A tool on the read allowlist arriving with +explicit annotations. The current server annotates all eleven tools, while the +manifest records what this release reviewed and exposes later contradictions. +A tool on the read allowlist arriving with `destructiveHint: true` or `readOnlyHint: false` keeps exactly what the downstream said and stays behind `call_destructive_tool`. A tool on neither maintained list arriving with `readOnlyHint: true` keeps that too, and stays callable from `execute_code`. Both are the downstream telling you this release's allowlist is stale, and on a name no release has reviewed its word is the only evidence there is. One narrow fail-closed exception applies to a name this -release reviewed and filed destructive: a `create_refund` claiming +release reviewed and filed destructive: `stripe_api_write` claiming `readOnlyHint: true` is a downstream bug rather than news, and stays on the approval path. diff --git a/documentation/upgrading.md b/documentation/upgrading.md index a8c13db..b7d0add 100644 --- a/documentation/upgrading.md +++ b/documentation/upgrading.md @@ -57,7 +57,7 @@ exist so far: | --- | --- | --- | | **pre-template** | before 0.10.2 | no `connecta init` existed; hand-written, or copied from the retired `examples/node` | | **A** | 0.10.2 – 0.15.1 | `.env.example`, `.gitignore`, `AGENTS.md`, `CLAUDE.md`, `README.md`, `package.json`, `src/index.ts`, `tsconfig.json` | -| **B** | 0.16.0 – 0.21.0 | adds `.dockerignore`, `Dockerfile`, `docker-compose.yml`, and `src/file-activity.ts`; `src/index.ts` grows the four commented operator blocks; `.env.example` ships `CONNECTA_TOKEN=` empty | +| **B** | 0.16.0 – 0.21.1 | adds `.dockerignore`, `Dockerfile`, `docker-compose.yml`, and `src/file-activity.ts`; `src/index.ts` grows the four commented operator blocks; `.env.example` ships `CONNECTA_TOKEN=` empty | Generation A is a decade in template years and identifying it precisely does not matter, because you are about to reconstruct it exactly rather than guess @@ -106,7 +106,7 @@ know what to preserve, once to know what to re-verify at the end. ### Bump the pin and install ```sh -npm pkg set dependencies.@zackbart/connecta=0.21.0 +npm pkg set dependencies.@zackbart/connecta=0.21.1 npm install ``` @@ -130,7 +130,7 @@ Generate the *current* template beside the base you already made, into the same `$SCRATCH`: ```sh -(cd "$SCRATCH" && npx @zackbart/connecta@0.21.0 init current) +(cd "$SCRATCH" && npx @zackbart/connecta@0.21.1 init current) ``` You now have a three-way merge with a real base: `$SCRATCH/base` is what this @@ -186,7 +186,7 @@ A deployment older than 0.10.2 has no base to diff against. Do not try to manufacture one. Instead: 1. `SCRATCH=$(mktemp -d)`, then - `(cd "$SCRATCH" && npx @zackbart/connecta@0.21.0 init current)` — there is no + `(cd "$SCRATCH" && npx @zackbart/connecta@0.21.1 init current)` — there is no `base` leg here, only the current template to read from. 2. Copy `$SCRATCH/current` into the deployment file by file, **skipping `src/index.ts`**. @@ -207,9 +207,10 @@ first, so cross them bottom-up: start at the oldest one still above this deployment's pin and work back up the page, because each boundary assumes the older ones are already done. -### 0.20.0 → 0.21.0 +### 0.20.0 → 0.21.1 -This is additive for Node and existing Clerk deployments. The new Worker path +0.21.1 adds no deployment migration beyond 0.21.0. The boundary is additive +for Node and existing Clerk deployments. The new Worker path uses Cloudflare Access identity directly and removes Clerk only after the edge cutover has been verified. An agent can perform every repository edit; a human must attach Access, choose its policy, create service credentials, and enable @@ -217,7 +218,7 @@ Managed OAuth in the Cloudflare dashboard. For a Worker currently using Clerk, keep rollback live through the cutover: -1. Bump and install 0.21.0. Add the new provider **before** the existing Clerk +1. Bump and install 0.21.1. Add the new provider **before** the existing Clerk provider, but remove nothing: ```ts diff --git a/package-lock.json b/package-lock.json index bbfe1d9..3649d69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@zackbart/connecta", - "version": "0.21.0", + "version": "0.21.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@zackbart/connecta", - "version": "0.21.0", + "version": "0.21.1", "license": "MIT", "dependencies": { "@cfworker/json-schema": "^4.1.1", diff --git a/package.json b/package.json index 588371c..5075d05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zackbart/connecta", - "version": "0.21.0", + "version": "0.21.1", "type": "module", "sideEffects": false, "description": "One MCP to rule them all — a single MCP endpoint aggregating many downstream connectors behind a code-first surface of seven meta-tools.", diff --git a/records/provider-audit.md b/records/provider-audit.md index 8b3e41d..a1dffc8 100644 --- a/records/provider-audit.md +++ b/records/provider-audit.md @@ -115,7 +115,7 @@ deletion workflows rather than missing fields on the five existing writes | P2 identity | meets | required `purpose`, `instructions` appended, classification untouchable from there; purpose states deployment routing intent and the guide says it is not proof of authenticated account identity | | P3 routing fact | meets | OAuth metadata states mixed account scope and the guide resolves mode from `livemode`; fixed header credentials retain their mode in every routing surface | | P4 endpoint default | meets | OAuth has no connector-wide mode to default; static headers require one, and construction throws when a recognizable key prefix contradicts it | -| P5 classification | meets | including the two verdicts that needed an argument — `stripe_api_read` is a read because the tool is the boundary, `create_refund` is destructive despite its name | +| P5 classification | meets | `stripe_api_read` is a read because the tool is the boundary; `stripe_implementation_planner` and `stripe_analytics` are additive because their shared tool boundaries can create guide or query-run state without changing an existing Stripe resource | | P6 catalog varies | **missed → fixed** | the doc already knew this (`get_balance_summary` is Treasury and gated; a `create_customer` example survives in Stripe's prose but not its tool table), but the *guide* did not say it, and the guide is what reaches the agent. Added | | P7 reduction advice | **missed → fixed** | OAuth has a mixed-scope summary; fixed credentials keep mode-shaped summaries. `required` stays unset because the four generic tools remain the routing decision | | P8 identity resolution | **missed → fixed** | The guide names typed object ids and their read sources. For OAuth it requires `list_available_accounts_or_orgs`, then carries the returned `stripe_context` and `livemode` unchanged; ambiguity stops ([#404](https://github.com/zackbart/connecta/issues/404), [#414](https://github.com/zackbart/connecta/issues/414)) | @@ -138,7 +138,7 @@ Linear's reasoning. | P3 routing fact | **missed → fixed** | region is exactly the fact P3 names, and it appeared in neither the default title (`"Mixpanel"`) nor the guide's first line. A project lives in one residency, so a question pointed at the wrong connector comes back empty rather than wrong — which reads as the project having no data. The title now carries it (`Mixpanel (us)`, `(eu)`, `(in)`) and the guide opens with it | | P4 endpoint default | meets | three published endpoints, an option that selects between them, and `"us"` as the default because that is where a project lives unless it was explicitly created elsewhere. Unlike Linear's, this default is honest: a wrong region cannot cause an irrecoverable write, only an empty read. Construction now also rejects a region there is no endpoint for | | P5 classification | meets | reads and writes named, unlisted fails closed, reviewed destructive beats a contradictory `readOnlyHint: true` | -| P6 catalog varies | **missed → fixed** | the provider doc knew that 15 of the 63 classified tools are beta surfaces; the guide did not say so. Added, naming experiments, feature flags, session replay, and issue triage as the usual absentees | +| P6 catalog varies | **missed → fixed** | the provider doc knew that beta surfaces vary by account; the guide did not say so. Added, naming experiments, feature flags, session replay, and issue triage as the usual absentees. The 2026-08-30 review classified the new `Fill-Event-Metadata` write and refreshed three beta experiment schemas (#512) | | P7 reduction advice | **missed → fixed** | bare string, derived summary. Now structured with a declared, region-shaped summary. `required` unset: the project-then-context sequence is worth reading before an analysis, not before every call | | P8 identity resolution | **missed → fixed** | the guide told an agent not to guess event and property *spelling* but said nothing about ids, and Mixpanel's `Get-`, `Update-`, and `Delete-` tools all take them. Added, naming the `List-` tools that produce each one | | P9 authentication | meets | OAuth default, `requireHttps`, service account documented as a secret | @@ -149,9 +149,9 @@ Linear's reasoning. ## RevenueCat — hosted-MCP proxy -Written after the conventions existed, so it has no misses to record — only two -places where the honest answer departs from the obvious one, both argued below. -Ninety-five documented tools, ninety-four classified, one deliberately not. +Written after the conventions existed, so it has no misses to record, only two +places where the honest answer departs from the obvious one. Ninety-six +documented tools, ninety-five classified, one deliberately not. | Convention | Verdict | Notes | | --- | --- | --- | @@ -159,7 +159,7 @@ Ninety-five documented tools, ninety-four classified, one deliberately not. | P2 identity | meets | required `purpose` (blank throws), `instructions` appended under `## Project instructions`, and appended text cannot reach the classification | | P3 routing fact | meets, with the fact split in two | the routing fact is scope, and it has two halves. The *shape* — one project versus every project the account can reach — is knowable at construction and rides the default title (`RevenueCat (single project)` versus `RevenueCat`). *Which* project a key opens is not knowable without calling something, which P10 forbids, so it rides the guide's first line and the declared summary, built from the operator's `purpose`. That makes this the one maintained proxy with a purpose-bearing summary rather than a static one, and the reason is P3's own cost: two `sk_` connectors share a title, an endpoint, and a catalog, so a static summary would leave them indistinguishable in the only field search returns | | P4 endpoint default | n/a — one endpoint, and the scope rides the credential | RevenueCat publishes a single MCP endpoint, so there is nothing to select between. The scope difference comes from the credential shape itself, which the constructor reads rather than asks for: `auth.type === "headers"` *is* the single-project declaration. There is no mode to default and no mode to contradict, so the P4 machinery Stripe needs has nothing to do here | -| P5 classification | meets | 50 reads, 15 additive writes, 29 destructive writes named; `render-paywall-screenshot` is on neither list because RevenueCat's reference gives it no access column, and it fails closed. Nine borderline verdicts are argued beside the rows they decide, and asserted in the suite so a silent flip fails | +| P5 classification | meets | 51 reads, 15 additive writes, 29 destructive writes named; `get-refund-request-preferences` is the added read. `render-paywall-screenshot` remains unclassified because RevenueCat's reference gives it no access column; its current explicit read-only annotation is preserved, and silence fails closed. Nine borderline verdicts are argued beside the rows they decide, and asserted in the suite so a silent flip fails | | P6 catalog varies | meets | the guide names paywall AI editing, benchmarks, experiments, virtual currencies, and account billing as the plan-, platform-, and beta-gated areas where absence is expected, and separately names the unclassified tool so its approval prompt does not read as a bug | | P7 reduction advice | meets | structured guide, declared summary, cursor-then-reduce advice aimed at the two objects that are actually large here (customers and their event history). `required` stays unset: the project-resolution sequence is worth reading before a run, not before every call | | P8 identity resolution | meets | the guide names the whole chain — `list-projects` for the `project_id` every project-scoped call takes, then `list-apps`, `list-products`, `list-entitlements`, `list-offerings`, `list-paywalls`, `list-audiences`, and `list-customers` for the ids their `get-`, `update-`, `archive-`, and `delete-` counterparts expect — and says a plausible-looking id belongs to another project or to nobody. For OAuth it also says to stop and ask when more than one project fits | @@ -173,7 +173,7 @@ Ninety-five documented tools, ninety-four classified, one deliberately not. | Provider | Meets | Missed and fixed | Recorded exception | Open | | --- | --- | --- | --- | --- | -| Cloudflare | 9 | 5 | H5 hatch request parts | H14 keep/prune ([#350](https://github.com/zackbart/connecta/issues/350)) | +| Cloudflare | 9 | 5 | H5 hatch request parts | — | | Notion | 10 | 4 | H5 exclusive parent | — | | Linear | 11 | 2 | P4 departs from the letter | — | | Stripe | 10 | 3 | — | — | diff --git a/src/providers/linear.ts b/src/providers/linear.ts index 6925700..6295e99 100644 --- a/src/providers/linear.ts +++ b/src/providers/linear.ts @@ -75,6 +75,10 @@ const READ_ONLY_TOOLS = new Set([ "get_team", "list_users", "get_user", + "get_workspace", + // Templates + "list_templates", + "get_template", // Status updates "get_status_updates", // Releases @@ -137,6 +141,9 @@ const WRITE_TOOLS: ReadonlyMap = new Map([ ["create_attachment_from_upload", "additive"], ["create_attachment", "additive"], ["delete_attachment", "destructive"], + // Explicit issue access + ["share_issue", "destructive"], + ["unshare_issue", "destructive"], // Customer requests (plan-gated) ["save_customer", "destructive"], ["delete_customer", "destructive"], diff --git a/src/providers/mixpanel.ts b/src/providers/mixpanel.ts index 5c11dcd..b06e189 100644 --- a/src/providers/mixpanel.ts +++ b/src/providers/mixpanel.ts @@ -114,16 +114,17 @@ const WRITE_TOOLS: ReadonlyMap = new Map([ ["Update-Experiment", "destructive"], ["Create-Feature-Flag", "additive"], ["Update-Feature-Flag", "destructive"], + ["Fill-Event-Metadata", "destructive"], ]); -/** Live US hosted-MCP schemas reviewed read-only on 2026-08-13 (#395). */ +/** Live US hosted-MCP schemas reviewed read-only on 2026-08-30 (#395, #512). */ const MIXPANEL_SCHEMA_DIGESTS = { "Bulk-Edit-Events": "sha256:94512138df153de95436371f5a313e32b97faffebda3be6c0c69d09d1ff2b340", "Bulk-Edit-Properties": "sha256:93481eb896ded45b623c678612e2156439efbaad5b4d660da1f2b087e7409ee3", "Create-Cohort": "sha256:ea687f4bc607f8bfb5b1949e060239d6f7d629d3ec80fb0b72eae6cc6156ed03", "Create-Custom-Property": "sha256:d22818b812f3fd0e785a5bbbee26cf099eaa911d5b6f383f94cf458a2ed26294", "Create-Dashboard": "sha256:bd3b6e5134d27dafb04ccdf04d19e54dd23e6e2dc16989e418006e484268037b", - "Create-Experiment": "sha256:3307bd7202f5483adbc28a9ef1d562095d6942eb2c3a15308f458072c93e6888", + "Create-Experiment": "sha256:1cfc2edeb2ac2d329a531d2095671a0653ab5b94b9e233b34268ff29a48c5ac5", "Create-Feature-Flag": "sha256:3460d11d726727349cac62db5c19915ab589b7f1d8387042f0a3783356b252c4", "Create-Lookup-Table": "sha256:c2fefa33a4f19fc65f394fecdc7126cab0ae874f7c97018cf003d8462a049e4c", "Create-Metric": "sha256:315766d0c197d87bb44aedd16732f6e69fbfc93661d1fcfc177cadc1751f26ce", @@ -139,13 +140,14 @@ const MIXPANEL_SCHEMA_DIGESTS = { "Edit-Event": "sha256:28c47da0b56f57da0f11eb11435441a39ecd5175600f854442744c66a379ef93", "Edit-Property": "sha256:11975587d2a566edc468db7cb24df70167d12068f30c1fe52c84f543cf4e2399", "Explain-Experiment-Health-Check": "sha256:a50b1263247012dc78521bf6c1dd18584a7b08d46221a444b1ca49c81d3286e5", + "Fill-Event-Metadata": "sha256:53accc988f216bdd5d7a259d731f6df82549e4e6146b191f1815a0d1a72a1abe", "Find-Duplicate-Groups": "sha256:8f944cef9a147eab3c51bfcf1b873cef1409e5a6c21565d34e7ed20d23dcebb7", "Get-Business-Context": "sha256:309f5ba864c90132061096b4f0ebc1e78544a6486bac3a45bd661d16bcab2b6c", "Get-Cohort": "sha256:e18c500459edd9c2b6a614a3f6c97b668786be9abf72929a0a50086bdadb1dae", "Get-Custom-Property": "sha256:d1cbce906cee66d422846c8ef1a745bbeac2c058ecad6b1ee8dcdd2120fa86be", "Get-Dashboard": "sha256:56659fa3277723b75e24f3b4ca142ac9cd62120fb97e6c9252c221d00cdf8509", "Get-Events": "sha256:d206fe30ab47d64bf6090bffdb4d119a80cfc2a35963bbe7fd52d56060090ded", - "Get-Experiment": "sha256:f9f04a2bb136809c754ea612963e1cdfb57c2c9ecc34a53ac6e1730f8758798f", + "Get-Experiment": "sha256:ab685aa985aa2ec36d9cbeb1b3c1ef298b020e552e8fdce12e75abbeb4706f2b", "Get-Experiment-Results-Interpretation-Guidance": "sha256:e517d9759f66f4a7ad15b0b67ffaabfb605e80949afcf41d007dfafd8f3e9574", "Get-Experiment-Setup-Guidance": "sha256:e517d9759f66f4a7ad15b0b67ffaabfb605e80949afcf41d007dfafd8f3e9574", "Get-Feature-Flag": "sha256:129de58b379ffba272bdbcee2b5759262e586dd415edc2067a69e9844d4d0877", @@ -177,7 +179,7 @@ const MIXPANEL_SCHEMA_DIGESTS = { "Update-Cohort": "sha256:ebe0d5afcc00e23eef55f079cd1816c851c2c647dff9565b7045f1f1615663f2", "Update-Custom-Property": "sha256:781aaaab41c1ab5958062dc393d02b1df2ef2a09a28d3c4f21ae3177f4f53c86", "Update-Dashboard": "sha256:2cc179ba75eda476ba7488f01503d947f023a4460aa98c4806ef0ab10c3b3ce8", - "Update-Experiment": "sha256:52ebf7595e48a40e218ff463ed72942a4818abc3ce93d8da6fafa8d358326437", + "Update-Experiment": "sha256:3ea3902555c146f45127fc563b36ffb7ff04b7594765dac9ae98cc09dc432808", "Update-Feature-Flag": "sha256:9b34b19164168938d08a75d268c9becf8d189c85a1789a45061ae304041faf7c", "Update-Lookup-Table": "sha256:ef28f1ec9c9484a7a53b5e2b6659e70f79a8e71e55bda17025aa4ab3c23b6a63", "Update-Metric": "sha256:739bb6abdab19282afd4a6644a96183daabe9098a5322c44a77b840608a5ee9d", diff --git a/src/providers/revenuecat.ts b/src/providers/revenuecat.ts index dbbcd30..d56a1db 100644 --- a/src/providers/revenuecat.ts +++ b/src/providers/revenuecat.ts @@ -70,6 +70,7 @@ const READ_ONLY_TOOLS = new Set([ // Customers and subscriptions "get-customer", "get-customer-center-config", + "get-refund-request-preferences", "get-subscription", "list-customer-events", "list-customers", @@ -199,7 +200,7 @@ function sharedUsageGuide(): string { - \`get-chart-data\` is the metrics path: read \`get-chart-options-schema\` for the chart you want before calling it, rather than guessing an option name. \`get-overview-metrics\` and \`get-revenue-metric\` answer the summary questions in one call. - \`create-paywall-ai\`, \`edit-paywall-ai\`, and \`set-product-store-state\` are asynchronous. They return a task or operation id; poll it with \`get-paywall-ai-task\` or \`get-product-store-state-operation\` rather than assuming the work finished when the call returned. - This connection's tool list is not a fixed set. RevenueCat gates parts of its MCP catalog by plan, platform, and beta enrollment — paywall AI editing, benchmarks, experiments, virtual currencies, and the account-billing tools are the usual absentees — so search this connector for what it actually exposes rather than assuming a documented tool is here. -- \`render-paywall-screenshot\` is unclassified on purpose: RevenueCat's reference gives it no access column, so it fails closed onto \`call_destructive_tool\` until a release reviews it. +- \`render-paywall-screenshot\` is unclassified on purpose because RevenueCat's reference gives it no access column. The current server marks it read-only, which Connecta preserves; without that annotation it fails closed onto \`call_destructive_tool\`. - RevenueCat meters API v2 per minute and per domain, and the domains differ: 480 requests per minute for customer information and virtual currencies, 60 for project configuration and audiences, 25 for charts and metrics. It answers a breach with \`429\`, a \`Retry-After\` header, and a \`backoff_ms\` field. Back off on that rather than retrying immediately, and expect chart sweeps to hit the ceiling long before customer reads do. - Treat every create, update, archive, unarchive, attach, detach, delete, publish, unpublish, grant, assign, and submit operation as a write. Connecta routes the maintained write catalog through \`call_destructive_tool\`; newly added tools also fail closed until a release classifies them. - An \`auth_required\` failure means this connector's RevenueCat authorization is missing or expired: run \`authorize_connector\` for this connector id, then retry the same call unchanged. A rejected argument, a permission gap, or a plan restriction comes back in RevenueCat's own words instead — read it rather than re-authorizing. diff --git a/src/providers/stripe.ts b/src/providers/stripe.ts index e97688b..2ce9a1b 100644 --- a/src/providers/stripe.ts +++ b/src/providers/stripe.ts @@ -87,17 +87,17 @@ const READ_ONLY_TOOLS = new Set([ "stripe_api_search", "stripe_api_details", "stripe_api_read", - "get_stripe_account_info", "get_balance_summary", + "list_available_accounts_or_orgs", + "manage_stripe_accounts", "search_stripe_documentation", - "stripe_implementation_planner", ]); -/** Reviewed writes, including refund rationale: `documentation/stripe.md`. */ +/** Reviewed writes, including mixed read/create tools: `documentation/stripe.md`. */ const WRITE_TOOLS: ReadonlyMap = new Map([ ["stripe_api_write", "destructive"], - ["create_refund", "destructive"], - ["stripe_report", "additive"], + ["stripe_implementation_planner", "additive"], + ["stripe_analytics", "additive"], ["send_stripe_mcp_feedback", "additive"], ]); @@ -234,7 +234,8 @@ ${ function sharedUsageGuide(rate: string): string { return ` - Four generic tools reach any Stripe API method. Find the method with \`stripe_api_search\`, read its parameters with \`stripe_api_details\`, then call \`stripe_api_read\` (GET) or \`stripe_api_write\` (POST/PATCH/PUT/DELETE). Never guess a path or a parameter name — \`stripe_api_details\` is cheaper than a rejected write. -- Prefer a dedicated tool when one covers the task: \`get_stripe_account_info\` for account information, \`get_balance_summary\` for balances, \`create_refund\` for refunds, \`stripe_report\` for reports. One call instead of three, and a refund named \`create_refund\` reads far more clearly in the approval a human sees than the same refund buried in \`stripe_api_write\` arguments. +- Prefer a dedicated tool when one covers the task: \`get_balance_summary\` for balances and \`stripe_analytics\` for Sigma or Metrics reporting. Use \`stripe_api_search\` for everything else instead of assuming a retired dedicated tool still exists. +- \`stripe_implementation_planner\` and the query-execution intents of \`stripe_analytics\` create provider-side planning or query-run state. Connecta therefore routes both through \`call_destructive_tool\` as non-destructive writes; their retrieval paths stay behind the same tool boundary. - \`stripe_api_write\` carries the blast radius of the entire write API — every POST, PATCH, PUT, and DELETE, from a customer edit to a subscription cancellation. State the method and path explicitly; expect approval on every call. - Lists are cursor-paginated: \`limit\` defaults to 10 and caps at 100, \`starting_after\` and \`ending_before\` take an object id and are mutually exclusive, and \`has_more\` says whether to continue. Page inside \`execute_code\`. - Any \`stripe_api_read\` list or \`stripe_api_search\` that returns full objects belongs inside \`execute_code\`, projected to the fields the question needs before \`return\`. Neither \`limit\` nor \`expand\` substitutes for that: an unprojected list of customers or invoices truncates long before it answers, and a projected one keeps the customer's name, email, and address out of the transcript. diff --git a/src/version.ts b/src/version.ts index 3c10237..34f2b05 100644 --- a/src/version.ts +++ b/src/version.ts @@ -4,4 +4,4 @@ * a bump that forgets this file fails the build rather than shipping a stale * version to `/health` and to downstream MCP handshakes. */ -export const CONNECTA_VERSION = "0.21.0"; +export const CONNECTA_VERSION = "0.21.1"; diff --git a/templates/node/package.json b/templates/node/package.json index d6133bb..db31463 100644 --- a/templates/node/package.json +++ b/templates/node/package.json @@ -15,7 +15,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@zackbart/connecta": "0.21.0", + "@zackbart/connecta": "0.21.1", "quickjs-emscripten": "0.32.0" }, "devDependencies": { diff --git a/test/linear-provider.test.ts b/test/linear-provider.test.ts index 76562be..99f2d5f 100644 --- a/test/linear-provider.test.ts +++ b/test/linear-provider.test.ts @@ -280,13 +280,17 @@ describe("linear()", () => { }); }); - it("classifies the plan-gated customer and code-review surfaces", async () => { + it("classifies plan-gated and current workspace surfaces", async () => { mocks.listTools.mockResolvedValue([ { name: "list_customers" }, { name: "delete_customer_need" }, { name: "merge_diff" }, { name: "prepare_attachment_upload" }, { name: "search_documentation" }, + { name: "get_workspace" }, + { name: "get_template" }, + { name: "share_issue" }, + { name: "unshare_issue" }, ]); const connector = linear("tracker", { purpose: "Delivery planning", @@ -300,6 +304,10 @@ describe("linear()", () => { // An upload URL is a side effect but destroys nothing. expect(tools[3]?.annotations).toEqual({ readOnlyHint: false }); expect(tools[4]?.annotations).toMatchObject({ readOnlyHint: true }); + expect(tools[5]?.annotations).toMatchObject({ readOnlyHint: true }); + expect(tools[6]?.annotations).toMatchObject({ readOnlyHint: true }); + expect(tools[7]?.annotations).toMatchObject({ destructiveHint: true }); + expect(tools[8]?.annotations).toMatchObject({ destructiveHint: true }); }); it("rejects an empty workspace purpose at construction", () => { diff --git a/test/mixpanel-provider.test.ts b/test/mixpanel-provider.test.ts index 24c8de9..e3d6f5a 100644 --- a/test/mixpanel-provider.test.ts +++ b/test/mixpanel-provider.test.ts @@ -149,11 +149,16 @@ describe("mixpanel()", () => { }); it("records a reviewed schema digest for every classified tool", () => { - expect(MIXPANEL_VETTED_CATALOG.tools.size).toBe(63); + expect(MIXPANEL_VETTED_CATALOG.tools.size).toBe(64); for (const [name, record] of MIXPANEL_VETTED_CATALOG.tools) { expect(name).toBeTruthy(); expect(record.schemaDigest).toMatch(/^sha256:[0-9a-f]{64}$/); } + expect(MIXPANEL_VETTED_CATALOG.tools.get("Fill-Event-Metadata")).toEqual({ + verdict: "destructive", + schemaDigest: + "sha256:53accc988f216bdd5d7a259d731f6df82549e4e6146b191f1815a0d1a72a1abe", + }); }); it("puts the residency in the title and the guide's first line (P3)", () => { diff --git a/test/revenuecat-provider.test.ts b/test/revenuecat-provider.test.ts index 49ce572..5cb9b46 100644 --- a/test/revenuecat-provider.test.ts +++ b/test/revenuecat-provider.test.ts @@ -260,7 +260,7 @@ describe("revenuecat()", () => { expect(guide).toContain("call_destructive_tool"); // The unclassified tool is named rather than left to be discovered. expect(guide).toContain( - "`render-paywall-screenshot` is unclassified on purpose", + "`render-paywall-screenshot` is unclassified on purpose because", ); }); @@ -296,16 +296,19 @@ describe("revenuecat()", () => { const verdicts = REVENUECAT_VETTED_CATALOG.tools; const counts = { "read-only": 0, additive: 0, destructive: 0 }; for (const { verdict } of verdicts.values()) counts[verdict] += 1; - // The 2026-08-18 reading of RevenueCat's tool reference: 95 tools, of - // which 94 carry an access column. + // The 2026-08-30 reading of RevenueCat's tool reference: 96 tools, of + // which 95 carry an access column. expect(counts).toEqual({ - "read-only": 50, + "read-only": 51, additive: 15, destructive: 29, }); - expect(verdicts.size).toBe(94); + expect(verdicts.size).toBe(95); + expect(verdicts.get("get-refund-request-preferences")?.verdict).toBe( + "read-only", + ); expect(verdicts.has("render-paywall-screenshot")).toBe(false); - // No digests: no release has read a live RevenueCat schema (#351). + // No digests: the scoped live catalog cannot provide a complete set (#351). for (const record of verdicts.values()) { expect(record.schemaDigest).toBeUndefined(); } @@ -340,7 +343,11 @@ describe("revenuecat()", () => { () => revenuecat("revenuecat", { purpose: "Rehearsal" }), mocks, { - read: ["list-projects", "get-customer", "get-customer"], + read: [ + "list-projects", + "get-customer", + "get-refund-request-preferences", + ], write: "create-offering", destructive: "grant-customer-entitlement", unknown: ["render-paywall-screenshot", "list-new-thing", "wreck-new-thing"], diff --git a/test/stripe-provider.test.ts b/test/stripe-provider.test.ts index cf28427..b53e949 100644 --- a/test/stripe-provider.test.ts +++ b/test/stripe-provider.test.ts @@ -49,10 +49,10 @@ describe("stripe()", () => { expect(guideOf(connector)).toContain("stripe_context"); expect(guideOf(connector)).toContain("livemode"); expect(guideOf(connector)).toContain("stripe_api_details"); - // The dedicated tools must stay named: a refund routed through the generic - // `stripe_api_write` degrades the approval prompt a human actually reads. - expect(guideOf(connector)).toContain("create_refund"); - expect(guideOf(connector)).toContain("get_stripe_account_info"); + expect(guideOf(connector)).toContain("stripe_analytics"); + expect(guideOf(connector)).toContain("stripe_implementation_planner"); + expect(guideOf(connector)).not.toContain("create_refund"); + expect(guideOf(connector)).not.toContain("get_stripe_account_info"); expect(guideOf(connector)).toContain("Idempotency-Key"); expect(guideOf(connector)).toContain( "100 requests per second in live mode and 25 in sandbox mode", @@ -383,9 +383,13 @@ describe("stripe()", () => { () => stripe("billing", { purpose: "Rehearsal" }), mocks, { - read: ["stripe_api_read", "search_stripe_documentation", "get_balance_summary"], - write: "stripe_report", - destructive: "create_refund", + read: [ + "stripe_api_read", + "list_available_accounts_or_orgs", + "manage_stripe_accounts", + ], + write: "stripe_analytics", + destructive: "stripe_api_write", unknown: ["create_customer", "get_new_treasury_thing", "wreck_new_thing"], }, );