@zackbart/connecta 0.17.0 → 0.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1044,13 +1044,13 @@ export function createMetaTools(registry, baseUrl, opts = {}) {
1044
1044
  },
1045
1045
  };
1046
1046
  }
1047
- const SEARCH_DESC = `Use top-level search only for exactly one unreduced read, then call_tool, or for write-capable work, then call_destructive_tool. For read-only reduction, dependent or multiple calls, never search here: make one execute_code program that searches and calls. Use 2–4 distinctive action/object terms, not the full request; set connector to the obvious integration id to load one catalog instead of all; omit limit initially (default ${DEFAULT_SEARCH_LIMIT}), page to ${MAX_SEARCH_LIMIT} if needed. safety="readOnly" returns only calls available to call_tool/code; "approvalRequired" returns the rest; omitted/"all" returns all. This filters results, not authority. includeSchemas="compact" adds the input and any declared output shape, bounded; plain objects expose inputKeys, requiredInputKeys, and outputKeys; truncation flags mark incomplete shapes; matches also carry declared annotations. Require purpose/address fit plus compatible inputs, truncation, safety, and outputs — never the first lexical match. Empty or whitespace-only query browses all; non-empty input with no ASCII terms returns no match.`;
1048
- const CALL_DESC = 'Use for ONE tool explicitly annotated readOnlyHint: true the cheapest path for a single cold call. For two or more calls, dependent steps, loops, joins, or data reduction use execute_code, whose connecta.call and connecta.batch reach the same tools. Unannotated, write-capable, and destructive tools are refused and require call_destructive_tool. fields selects JSON dot-paths; traverse arrays with [] (for example results[].id). Misses return data plus `$connecta` feedback. resultMode "value" unwraps results, timeoutMs sets a deadline, safe maxRetries are annotation-gated, diagnostics adds timing, and large results page through get_result.';
1049
- const CALL_DESTRUCTIVE_DESC = "Invoke any tool that is not explicitly annotated readOnlyHint: true, including unannotated, write-capable, or destructive tools. Include a short reason explaining the intended consequence for the human reviewer; it grants no authority and is never passed downstream. The MCP destructiveHint on this meta-tool lets the host request human approval before execution. Use only after reviewing the downstream tool schema and consequences.";
1050
- const GET_RESULT_DESC = "Page a truncated result stashed by call_tool or call_destructive_tool; a program's oversized return is not paged, so reduce it in code instead. Input { id, offset?, maxBytes? } → { text, offset, nextOffset?, totalBytes } sliced by byte offset. maxBytes is a whole number of bytes >= 1 (omit for the deployment default) and offset a whole number of bytes >= 0; an offset inside a multi-byte character is moved back to that character's first byte and the offset served is returned. Unknown/expired id is an error.";
1047
+ const SEARCH_DESC = `Use top-level search for one unknown-address read before call_tool, or for approval-required work before call_destructive_tool. Use 2–4 action/object terms and includeSchemas="compact"; the default limit is ${DEFAULT_SEARCH_LIMIT}. Set connector when known. safety="readOnly" finds direct or program calls; "approvalRequired" finds the fail-closed complement. These filters grant no authority. For multiple, dependent, or reduced read-only calls, use one execute_code program instead. Empty query browses.`;
1048
+ const CALL_DESC = 'Call one tool explicitly annotated readOnlyHint: true. Use execute_code for multiple, dependent, or reduced read-only calls. Unannotated or write-capable tools fail closed to call_destructive_tool. fields projects JSON dot-paths; use [] through arrays. A truncated result carries a get_result action.';
1049
+ const CALL_DESTRUCTIVE_DESC = "Call any tool not explicitly annotated readOnlyHint: true. Include a short reason for the human reviewer after checking the schema and consequences. The reason grants no authority and is not sent downstream.";
1050
+ const GET_RESULT_DESC = "Page a truncated direct-call result by id and byte offset. A program result is never paged; reduce it inside execute_code. Returns text, offset, nextOffset when more remains, and totalBytes.";
1051
1051
  const AUTHORIZE_DESC = "Use after auth_required. Returns an OAuth or operator-credential handoff, or reports required deployment configuration. force=true restarts OAuth only; this tool never accepts credentials.";
1052
- const SKILLS_DESC = 'List or fetch concise guidance for choosing among Connecta meta-tools. Call skills({ name: "usage" }) once when the routing workflow is unfamiliar; do not refetch it in the same task.';
1053
- const SEARCH_WITH_DESCRIBE_DESC = `${SEARCH_DESC} Expand an ambiguous compact shape, or read exact JSON constraints, with connecta.describe inside execute_code.`;
1052
+ const SKILLS_DESC = 'List or fetch on-demand guidance. Fetch usage once per task for program syntax, selection, repair, examples, and runtime details.';
1053
+ const SEARCH_WITH_DESCRIBE_DESC = SEARCH_DESC;
1054
1054
  /**
1055
1055
  * Sentences appended to a meta-tool description only when this connection
1056
1056
  * actually has connector guides. Tool descriptions are always-loaded context,
@@ -1060,9 +1060,9 @@ const SEARCH_WITH_DESCRIBE_DESC = `${SEARCH_DESC} Expand an ambiguous compact sh
1060
1060
  * Registration is per connection and reads the configured connector set.
1061
1061
  */
1062
1062
  const GUIDE_NOTES = {
1063
- skills: " skills({}) also lists this deployment's scoped connector guides; fetch only an exact name listed there or carried by discovery, never one inferred from a connector id.",
1064
- search: " A result carrying `guide` also carries a bounded `guideSummary`. `guideRequired: true` is a hard stop: fetch that exact guide before calling. `guideRequiredReasons` explains why — `connector_required` and `approval_required` stand however you expand the schema; `schema_truncated` clears once describe returns the exact one. Otherwise fetch only when the summary names a connector convention relevant to the task. A complete, unambiguous read-only schema needs no otherwise-irrelevant guide fetch.",
1065
- destructive: " Before a consequential call, inspect the address through discovery or describe and fetch any connector guide it names.",
1063
+ skills: " Also lists this deployment's connector guides by exact name.",
1064
+ search: " A result with guideRequired: true requires its exact named connector guide before the call.",
1065
+ destructive: " Fetch any exact connector guide named by discovery before the call.",
1066
1066
  };
1067
1067
  /** `base`, plus its guide note when any VISIBLE connector carries a guide. */
1068
1068
  function describedFor(registry, base, note) {
@@ -184,7 +184,10 @@ Account purpose: ${purpose}
184
184
  - \`Get-Business-Context\` requires either \`project_id\` or \`organization_id\`. Its schema marks both optional, but the hosted tool rejects a call with neither.
185
185
  - \`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.
186
186
  - \`List-Properties\` accepts \`names\` or \`query\`, never both. Use exact \`names\` for known properties and \`query\` for substring discovery.
187
- - For a new analysis, fetch \`Get-Query-Schema\` before \`Run-Query\`. Reduce query results inside \`execute_code\` before returning them.
187
+ - 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.
188
+ - 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.
189
+ - \`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.
190
+ - 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.
188
191
  - Use \`Get-Report\` when the request names an existing saved report. Use \`Run-Query\` for a new question.
189
192
  - 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.
190
193
  - 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.
@@ -0,0 +1,77 @@
1
+ import { type RemoteMcpAuth } from "../connectors/remote-mcp.js";
2
+ import type { Connector, ConnectorCallAdmissionPolicy } from "../types.js";
3
+ /** RevenueCat publishes one hosted MCP endpoint, streamable HTTP. */
4
+ export declare const REVENUECAT_MCP_ENDPOINT = "https://mcp.revenuecat.ai/mcp";
5
+ export interface RevenueCatOptions {
6
+ /**
7
+ * Human-readable display name; defaults to "RevenueCat" for OAuth and
8
+ * "RevenueCat (single project)" for a static API v2 secret key. The scope
9
+ * shape rides the title because it is the one routing fact connecta can
10
+ * know at construction: an `sk_` key reaches exactly one project, an OAuth
11
+ * session reaches every project the account can. *Which* project a key
12
+ * reaches is not knowable here (P10 — no credential test), so the guide's
13
+ * first line carries the operator's stated purpose instead.
14
+ */
15
+ title?: string;
16
+ /**
17
+ * Which project this connector is for and what decisions it answers. With
18
+ * headers auth this is the only place the project a key reaches is named,
19
+ * so it goes in the guide's first line and its summary.
20
+ */
21
+ purpose: string;
22
+ /**
23
+ * OAuth by default; static headers support a RevenueCat API v2 secret key
24
+ * as `Authorization: Bearer sk_…`.
25
+ */
26
+ auth?: RemoteMcpAuth;
27
+ /** Project-specific conventions appended to the maintained provider guide. */
28
+ instructions?: string;
29
+ /** Connector-specific inline result limit; omit to inherit the deployment. */
30
+ maxResultBytes?: number;
31
+ /**
32
+ * Optional per-runtime call-admission policy. Deliberately not defaulted,
33
+ * even though RevenueCat does publish numbers.
34
+ *
35
+ * API v2 meters per *domain*, and the domains disagree by a factor of
36
+ * nineteen: Customer Information 480/min, Virtual Currencies 480/min,
37
+ * Subscription Transactions Refunds 480/min, Audiences 60/min, Project
38
+ * Configuration 60/min, Charts & Metrics 25/min
39
+ * (https://www.revenuecat.com/docs/api-v2#tag/Rate-Limit, read 2026-08-18).
40
+ * A `ConnectorCallAdmissionPolicy` carries exactly one rule, so a
41
+ * connector-wide budget has to pick one of those six numbers for all ninety-
42
+ * five tools. Transcribing 25 would throttle a customer read loop to a
43
+ * nineteenth of its documented allowance; transcribing 480 would leave a
44
+ * chart sweep unprotected. Neither is the provider's limit, and both would
45
+ * look like RevenueCat being flaky.
46
+ *
47
+ * The metering scope says the same thing again: the limit applies per API
48
+ * key for app-level keys and per *developer* for developer-level keys, so
49
+ * an OAuth session shares one budget with everything else that developer
50
+ * does — which a per-runtime counter cannot approximate in either
51
+ * direction. So the number stays with the operator who knows the account,
52
+ * exactly as P12 prescribes; `documentation/revenuecat.md` shows how to
53
+ * supply one.
54
+ */
55
+ callAdmission?: ConnectorCallAdmissionPolicy;
56
+ }
57
+ /**
58
+ * The manifest this release reviewed: both lists in one place, which is what
59
+ * makes the classification the connector applies and the drift check that runs
60
+ * beside it the same fact (P13). Ninety-four of the ninety-five tools
61
+ * RevenueCat's reference lists on 2026-08-18 are classified; the ninety-fifth,
62
+ * `render-paywall-screenshot`, has no access column to classify from and fails
63
+ * closed.
64
+ *
65
+ * No schema digests. No release has read RevenueCat's live schemas and written
66
+ * them down — that needs a live project and a maintainer's own `sk_` key — and
67
+ * an invented digest would report a change that never happened.
68
+ * `npm run drift:check -- --record` reads them from a live catalog and prints
69
+ * the block to paste in
70
+ * ([#351](https://github.com/zackbart/connecta/issues/351)).
71
+ *
72
+ * Exported because the maintainer-run check compares against this manifest and
73
+ * *names* what moved, which the runtime check deliberately cannot.
74
+ */
75
+ export declare const REVENUECAT_VETTED_CATALOG: import("../catalog-drift.js").VettedCatalog;
76
+ /** A maintained RevenueCat hosted-MCP connection. */
77
+ export declare function revenuecat(id: string, options: RevenueCatOptions): Connector;
@@ -0,0 +1,314 @@
1
+ import { remoteMcp, } from "../connectors/remote-mcp.js";
2
+ import { vettedCatalog, withVettedCatalog } from "../catalog-drift.js";
3
+ /** RevenueCat publishes one hosted MCP endpoint, streamable HTTP. */
4
+ export const REVENUECAT_MCP_ENDPOINT = "https://mcp.revenuecat.ai/mcp";
5
+ /**
6
+ * Tools whose official contract is observational rather than mutating.
7
+ *
8
+ * Every name here carries `Read` in RevenueCat's own tool reference
9
+ * (https://www.revenuecat.com/docs/tools/mcp/tools-reference, read
10
+ * 2026-08-18). The list is a superset by design (P5): a name a project never
11
+ * serves costs nothing, while an unclassified new one fails closed onto
12
+ * `call_destructive_tool`.
13
+ */
14
+ const READ_ONLY_TOOLS = new Set([
15
+ // Projects and apps
16
+ "get-account-billing",
17
+ "get-app",
18
+ "get-project-ui-config",
19
+ "list-account-billing-invoices",
20
+ "list-app-public-api-keys",
21
+ "list-apps",
22
+ "list-audit-logs",
23
+ "list-collaborators",
24
+ "list-projects",
25
+ // Products and prices
26
+ "get-product",
27
+ "get-product-store-state",
28
+ "get-product-store-state-operation",
29
+ "list-products",
30
+ // Entitlements
31
+ "get-entitlement",
32
+ "get-products-from-entitlement",
33
+ "list-entitlements",
34
+ // Offerings and packages
35
+ "get-offering",
36
+ "get-offering-prices",
37
+ "list-offerings",
38
+ "list-packages",
39
+ // Targeting and audiences
40
+ "get-audience",
41
+ "get-audience-filter-options",
42
+ "get-targeting-rule",
43
+ "list-audiences",
44
+ "list-targeting-rules",
45
+ // Paywalls
46
+ "get-paywall",
47
+ "list-paywalls",
48
+ // Customers and subscriptions
49
+ "get-customer",
50
+ "get-customer-center-config",
51
+ "get-subscription",
52
+ "list-customer-events",
53
+ "list-customers",
54
+ "list-purchases",
55
+ "list-subscriptions",
56
+ "list-virtual-currencies-balances",
57
+ // Virtual currencies
58
+ "get-virtual-currency",
59
+ "list-virtual-currencies",
60
+ // Charts, metrics, and experiments
61
+ "get-benchmarks",
62
+ "get-chart-data",
63
+ "get-chart-options-schema",
64
+ "get-experiment",
65
+ "get-experiment-results",
66
+ "get-overview-metrics",
67
+ "get-revenue-metric",
68
+ "list-experiments",
69
+ // Integrations and webhooks
70
+ "get-webhook-integration",
71
+ "list-webhook-integrations",
72
+ // SDK compatibility
73
+ "list-sdk-feature-gates",
74
+ "list-sdk-versions",
75
+ // Paywall editing. Polling an async task is a read; the two tools that
76
+ // *start* one are writes and sit below.
77
+ "get-paywall-ai-task",
78
+ ]);
79
+ /**
80
+ * The maintained write catalog. `"destructive"` tools modify or remove state
81
+ * that already exists; `"additive"` ones only bring something new into being.
82
+ * Both leave the read-only path — the distinction only decides whether the
83
+ * connection asserts `destructiveHint`, which shapes the host's approval copy.
84
+ *
85
+ * Every name here carries `Write` in RevenueCat's tool reference. The mass
86
+ * verdicts follow the verb: `archive-*` and `unarchive-*` flip an existing
87
+ * object's active state, `update-*` and `delete-*` and `publish-*` and
88
+ * `unpublish-*` and `detach-*` change or remove something that already exists,
89
+ * and a plain `create-*` brings a new object into being beside the old ones.
90
+ *
91
+ * Nine verdicts are not decided by the verb, and each is argued where it sits:
92
+ * `create-product-prices`, `equalize-subscription-prices`,
93
+ * `validate-app-credentials`, `upload-product-store-state-screenshot`,
94
+ * `attach-products-to-entitlement`, `attach-products-to-package`,
95
+ * `duplicate-paywall`, `create-paywall-ai`, and `edit-paywall-ai`.
96
+ *
97
+ * `render-paywall-screenshot` is deliberately on neither list. RevenueCat's
98
+ * reference gives it no access column at all, and a tool nobody has classified
99
+ * fails closed (P5) rather than being guessed into the read path because its
100
+ * name sounds harmless.
101
+ */
102
+ const WRITE_TOOLS = new Map([
103
+ // Projects and apps
104
+ ["create-app", "additive"],
105
+ ["create-project", "additive"],
106
+ ["update-app", "destructive"],
107
+ ["update-project-ui-config", "destructive"],
108
+ // "Checks one saved App Store or Google Play credential set." RevenueCat
109
+ // files it Write, so it does not reach the read path — but it leaves the
110
+ // credentials themselves alone and only records the outcome of a check.
111
+ // Additive: a verdict comes into being, nothing existing is overwritten.
112
+ ["validate-app-credentials", "additive"],
113
+ // Products and prices
114
+ ["archive-product", "destructive"],
115
+ ["create-product", "additive"],
116
+ // Named `create-`, described "Configure prices for a product". A product's
117
+ // price set already exists, and configuring it replaces what is there
118
+ // rather than adding a second price beside the first. Money-facing and
119
+ // overwriting, so: destructive, whatever the verb says.
120
+ ["create-product-prices", "destructive"],
121
+ // "Fills missing App Store subscription territory prices." By RevenueCat's
122
+ // own word it only writes where a price is absent, so nothing already set
123
+ // is changed. Additive.
124
+ ["equalize-subscription-prices", "additive"],
125
+ ["set-product-store-state", "destructive"],
126
+ ["submit-products-to-store", "destructive"],
127
+ ["unarchive-product", "destructive"],
128
+ ["update-product", "destructive"],
129
+ // "Reserves an App Store Connect review screenshot slot." A new slot comes
130
+ // into being; no existing screenshot is replaced by the reservation.
131
+ ["upload-product-store-state-screenshot", "additive"],
132
+ // Entitlements
133
+ ["archive-entitlement", "destructive"],
134
+ // Attach adds a product to a membership set and removes nothing;
135
+ // `detach-products-from-entitlement` is its destructive counterpart. Filing
136
+ // both destructive would make the pair read identically in the approval copy
137
+ // a human is shown, which is exactly the inflation P5 warns about.
138
+ ["attach-products-to-entitlement", "additive"],
139
+ ["create-entitlement", "additive"],
140
+ ["detach-products-from-entitlement", "destructive"],
141
+ ["unarchive-entitlement", "destructive"],
142
+ ["update-entitlement", "destructive"],
143
+ // Offerings and packages
144
+ ["archive-offering", "destructive"],
145
+ // The same attach/detach argument one level down.
146
+ ["attach-products-to-package", "additive"],
147
+ ["create-offering", "additive"],
148
+ ["create-packages", "additive"],
149
+ ["delete-package-from-offering", "destructive"],
150
+ ["detach-products-from-package", "destructive"],
151
+ ["unarchive-offering", "destructive"],
152
+ ["update-offering", "destructive"],
153
+ // Targeting and audiences
154
+ ["create-audience", "additive"],
155
+ ["update-audience", "destructive"],
156
+ // Paywalls
157
+ // "Duplicates an existing paywall's current draft." The original is
158
+ // untouched and a new paywall appears beside it. Additive.
159
+ ["duplicate-paywall", "additive"],
160
+ ["publish-paywall", "destructive"],
161
+ ["unpublish-paywall", "destructive"],
162
+ // Customers and subscriptions. Neither verb is in the mass rule and neither
163
+ // removes anything, so the file's own criterion would read them additive.
164
+ // They are destructive on consequence, the way `create_refund` is in
165
+ // `stripe.ts`: `assign-customer-offering` overrides which offering a live
166
+ // customer's app serves, and `grant-customer-entitlement` opens paid access
167
+ // to a real person without a store purchase (the promotional subscription it
168
+ // creates is the mechanism, not the point). Both change what a customer
169
+ // gets today, and both deserve the destructive approval copy.
170
+ ["assign-customer-offering", "destructive"],
171
+ ["grant-customer-entitlement", "destructive"],
172
+ // Virtual currencies
173
+ ["archive-virtual-currency", "destructive"],
174
+ ["create-virtual-currency", "additive"],
175
+ ["unarchive-virtual-currency", "destructive"],
176
+ ["update-virtual-currency", "destructive"],
177
+ // Integrations and webhooks
178
+ // A new integration is a new object, but one that "starts delivering
179
+ // RevenueCat events to the given url" — with filters omitted, every customer
180
+ // event in the project, to a URL the caller typed. That is customer data
181
+ // leaving the account on consequence, which is the `create_refund` argument
182
+ // again: the verb says additive, the effect says destructive, and the
183
+ // approval copy should say the latter.
184
+ ["create-webhook-integration", "destructive"],
185
+ ["delete-webhook-integration", "destructive"],
186
+ ["update-webhook-integration", "destructive"],
187
+ // Paywall editing. Both start an async task; what the task does decides the
188
+ // verdict. Creating a paywall leaves every existing one alone (additive);
189
+ // editing one rewrites a draft that already exists (destructive).
190
+ ["create-paywall-ai", "additive"],
191
+ ["edit-paywall-ai", "destructive"],
192
+ ]);
193
+ /**
194
+ * The manifest this release reviewed: both lists in one place, which is what
195
+ * makes the classification the connector applies and the drift check that runs
196
+ * beside it the same fact (P13). Ninety-four of the ninety-five tools
197
+ * RevenueCat's reference lists on 2026-08-18 are classified; the ninety-fifth,
198
+ * `render-paywall-screenshot`, has no access column to classify from and fails
199
+ * closed.
200
+ *
201
+ * No schema digests. No release has read RevenueCat's live schemas and written
202
+ * them down — that needs a live project and a maintainer's own `sk_` key — and
203
+ * an invented digest would report a change that never happened.
204
+ * `npm run drift:check -- --record` reads them from a live catalog and prints
205
+ * the block to paste in
206
+ * ([#351](https://github.com/zackbart/connecta/issues/351)).
207
+ *
208
+ * Exported because the maintainer-run check compares against this manifest and
209
+ * *names* what moved, which the runtime check deliberately cannot.
210
+ */
211
+ export const REVENUECAT_VETTED_CATALOG = vettedCatalog({
212
+ reads: READ_ONLY_TOOLS,
213
+ writes: WRITE_TOOLS,
214
+ });
215
+ /** The catalog's summary bound; a longer declared value throws (`src/registry.ts`). */
216
+ const SUMMARY_BUDGET = 120;
217
+ /**
218
+ * Fit a purpose-bearing summary inside the catalog's bound.
219
+ *
220
+ * Stripe and Mixpanel declare static summaries because their routing fact is
221
+ * an enumerable variant. RevenueCat's is not: two `sk_` connectors have the
222
+ * same title, the same endpoint, and the same catalog, and differ only by the
223
+ * project the operator says each key reaches. So the summary carries that, and
224
+ * clipping is this function's job rather than the operator's.
225
+ */
226
+ function boundedSummary(prefix, purpose) {
227
+ const full = `${prefix}${purpose}`;
228
+ if (full.length <= SUMMARY_BUDGET)
229
+ return full;
230
+ return `${full.slice(0, SUMMARY_BUDGET - 1).trimEnd()}…`;
231
+ }
232
+ function sharedUsageGuide() {
233
+ return `
234
+ - Resolve ids before acting; never guess one. \`list-projects\` yields the \`project_id\` every project-scoped call takes. \`list-apps\`, \`list-products\`, \`list-entitlements\`, \`list-offerings\`, \`list-paywalls\`, \`list-audiences\`, and \`list-customers\` yield the ids their \`get-\`, \`update-\`, \`archive-\`, and \`delete-\` counterparts expect. A plausible-looking id belongs to another project or to nobody.
235
+ - Customers are addressed by the app user id your SDK set, not by an internal key. Find one with \`list-customers\` before \`get-customer\`, and carry the id it returned unchanged.
236
+ - Customer and subscription objects are large, and a customer's history is larger. Page with the cursor the list returned rather than raising the page size, and reduce inside \`execute_code\` — select the fields the question needs and return those, not the whole object.
237
+ - Whether a customer should have access is \`gives_access\` on each subscription from \`list-subscriptions\`, which RevenueCat calls the authoritative flag. \`status\` and \`expires_date\` describe the store-side state and disagree with it during grace periods, billing retries, and promotional grants — answer access questions from \`gives_access\` and say which subscription it came from.
238
+ - \`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.
239
+ - \`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.
240
+ - 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.
241
+ - \`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.
242
+ - 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.
243
+ - 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.
244
+ - 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.
245
+ `;
246
+ }
247
+ function oauthUsageGuide(purpose, instructions) {
248
+ const projectInstructions = instructions?.trim();
249
+ return `# RevenueCat usage
250
+
251
+ Account-scoped connection: this OAuth session reaches every RevenueCat project the account can see. Connector purpose: ${purpose}
252
+
253
+ Call \`list-projects\` first and carry the exact \`project_id\` it returned into every project-scoped call. Connecta does not pick a project, and the connector id, title, and purpose are routing hints rather than proof of which project a call will land in. If more than one project fits the request, stop and ask; never guess a \`project_id\`.
254
+ ${sharedUsageGuide()}${projectInstructions
255
+ ? `\n## Project instructions\n\n${projectInstructions}\n`
256
+ : ""}`;
257
+ }
258
+ function keyUsageGuide(purpose, instructions) {
259
+ const projectInstructions = instructions?.trim();
260
+ return `# RevenueCat usage
261
+
262
+ Single-project connection: ${purpose}. RevenueCat secret API keys are project-wide, so this key reaches exactly one project and nothing outside it. A second project is a second connector with its own key and its own id — never a \`project_id\` argument pointed somewhere else.
263
+
264
+ Confirm the project on first use: \`list-projects\` returns the one project this key can see, and its \`project_id\` is the one every project-scoped call takes. An empty or unexpected result means wrong connector, not missing data.
265
+
266
+ A RevenueCat secret key is issued read-only or write-enabled, and connecta cannot tell which this one is. It does not filter writes for a read-only key: every write is offered, reaches RevenueCat, and fails there in RevenueCat's own words. Read that refusal as "this key cannot write" rather than as a bad argument, and route the write to a connector configured with a write-enabled key.
267
+ ${sharedUsageGuide()}${projectInstructions
268
+ ? `\n## Project instructions\n\n${projectInstructions}\n`
269
+ : ""}`;
270
+ }
271
+ /** A maintained RevenueCat hosted-MCP connection. */
272
+ export function revenuecat(id, options) {
273
+ const purpose = options.purpose.trim();
274
+ if (!purpose) {
275
+ throw new Error("revenuecat() requires a non-empty project purpose.");
276
+ }
277
+ const auth = options.auth ?? { type: "oauth" };
278
+ const scoped = auth.type === "headers";
279
+ const connector = remoteMcp(id, {
280
+ url: REVENUECAT_MCP_ENDPOINT,
281
+ // The scope shape rides the title because browse-time discovery renders
282
+ // the title and the guide summary and nothing else, and reaching one
283
+ // project versus every project the account has is the fact an agent must
284
+ // not get wrong between two RevenueCat connections.
285
+ title: options.title ?? (scoped ? "RevenueCat (single project)" : "RevenueCat"),
286
+ description: scoped
287
+ ? `RevenueCat subscriptions and revenue (one project, static key) — ${purpose}`
288
+ : `RevenueCat subscriptions and revenue (every project the account can reach) — ${purpose}`,
289
+ auth,
290
+ requireHttps: true,
291
+ usageGuide: {
292
+ content: scoped
293
+ ? keyUsageGuide(purpose, options.instructions)
294
+ : oauthUsageGuide(purpose, options.instructions),
295
+ // Explicit rather than derived, and purpose-bearing rather than static:
296
+ // the derived summary would cut the scoping sentence mid-clause at 120
297
+ // characters, and two static summaries would leave two `sk_` connectors
298
+ // indistinguishable in the one field search returns (P3).
299
+ summary: scoped
300
+ ? boundedSummary("One project only: ", purpose)
301
+ : boundedSummary("All account projects; list-projects first: ", purpose),
302
+ // Not `required`. RevenueCat's own schemas describe each call; the guide
303
+ // carries the project-resolution sequence, which is worth reading before
304
+ // a run rather than before every call.
305
+ },
306
+ ...(options.callAdmission !== undefined
307
+ ? { callAdmission: options.callAdmission }
308
+ : {}),
309
+ ...(options.maxResultBytes !== undefined
310
+ ? { maxResultBytes: options.maxResultBytes }
311
+ : {}),
312
+ });
313
+ return withVettedCatalog(connector, REVENUECAT_VETTED_CATALOG);
314
+ }
@@ -1,37 +1,37 @@
1
1
  import { type RemoteMcpAuth } from "../connectors/remote-mcp.js";
2
2
  import type { Connector } from "../types.js";
3
- /**
4
- * Which Stripe environment this instance speaks to. Required, and deliberately
5
- * undefaulted: there is no safe guess between an account that moves real money
6
- * and one that does not.
7
- */
3
+ /** Which Stripe environment a static credential reaches. */
8
4
  export type StripeMode = "production" | "sandbox";
9
- /** Stripe publishes one hosted MCP endpoint; the credential selects the mode. */
5
+ /** Stripe publishes one hosted MCP endpoint for every account and mode. */
10
6
  export declare const STRIPE_MCP_ENDPOINT = "https://mcp.stripe.com/";
11
- export interface StripeOptions {
12
- /**
13
- * Which Stripe environment this connector reaches. Shapes the title,
14
- * description, guide, and admission budget, and is checked against a
15
- * recognizable key prefix in `auth` headers.
16
- */
17
- mode: StripeMode;
18
- /** Human-readable display name; defaults to "Stripe (<mode>)". */
7
+ interface StripeCommonOptions {
8
+ /** Human-readable display name; defaults to "Stripe" for OAuth. */
19
9
  title?: string;
20
10
  /** Which business purpose and Stripe context this connector is for. */
21
11
  purpose: string;
22
- /** OAuth by default; static headers support restricted API keys. */
23
- auth?: RemoteMcpAuth;
24
- /**
25
- * Connect platform only: act as this connected account (`acct_...`) by
26
- * sending Stripe's `Stripe-Account` header. Stripe does not support OAuth on
27
- * connected-account calls, so this requires `headers` auth.
28
- */
29
- connectedAccount?: string;
30
12
  /** Connector-specific conventions appended to the maintained provider guide. */
31
13
  instructions?: string;
32
14
  /** Connector-specific inline result limit; omit to inherit the deployment. */
33
15
  maxResultBytes?: number;
34
16
  }
17
+ /** OAuth sessions discover account and mode together from Stripe's live tools. */
18
+ export interface StripeOAuthOptions extends StripeCommonOptions {
19
+ auth?: {
20
+ type: "oauth";
21
+ };
22
+ mode?: never;
23
+ connectedAccount?: never;
24
+ }
25
+ /** Static credentials have one fixed mode, including Stripe Connect calls. */
26
+ export interface StripeHeaderOptions extends StripeCommonOptions {
27
+ auth: Extract<RemoteMcpAuth, {
28
+ type: "headers";
29
+ }>;
30
+ mode: StripeMode;
31
+ /** Act as one Connect account by sending Stripe's `Stripe-Account` header. */
32
+ connectedAccount?: string;
33
+ }
34
+ export type StripeOptions = StripeOAuthOptions | StripeHeaderOptions;
35
35
  /**
36
36
  * The manifest this release reviewed: both lists in one place, which is what
37
37
  * makes the classification the connector applies and the drift check that runs
@@ -47,3 +47,4 @@ export interface StripeOptions {
47
47
  export declare const STRIPE_VETTED_CATALOG: import("../catalog-drift.js").VettedCatalog;
48
48
  /** A maintained Stripe hosted-MCP connection. */
49
49
  export declare function stripe(id: string, options: StripeOptions): Connector;
50
+ export {};