@solvapay/mcp-core 0.4.2 → 0.4.3

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/dist/index.cjs CHANGED
@@ -38,9 +38,11 @@ __export(index_exports, {
38
38
  McpBearerAuthError: () => McpBearerAuthError,
39
39
  NARRATORS: () => NARRATORS,
40
40
  OPEN_TOOL_FOR_VIEW: () => OPEN_TOOL_FOR_VIEW,
41
+ PORTAL_AUTO_RECHARGE_QUERY: () => PORTAL_AUTO_RECHARGE_QUERY,
41
42
  SOLVAPAY_BOOTSTRAP_MIME_TYPE: () => SOLVAPAY_BOOTSTRAP_MIME_TYPE,
42
43
  SOLVAPAY_BOOTSTRAP_URI: () => SOLVAPAY_BOOTSTRAP_URI,
43
44
  SOLVAPAY_DEFAULT_CSP: () => SOLVAPAY_DEFAULT_CSP,
45
+ SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS: () => SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS,
44
46
  SOLVAPAY_MCP_VIEW_KINDS: () => SOLVAPAY_MCP_VIEW_KINDS,
45
47
  SOLVAPAY_OVERVIEW_MARKDOWN: () => SOLVAPAY_OVERVIEW_MARKDOWN,
46
48
  SOLVAPAY_OVERVIEW_MIME_TYPE: () => SOLVAPAY_OVERVIEW_MIME_TYPE,
@@ -53,6 +55,7 @@ __export(index_exports, {
53
55
  VIEW_FOR_TOOL: () => VIEW_FOR_TOOL,
54
56
  applyHideToolsByAudience: () => applyHideToolsByAudience,
55
57
  assertValidProductRef: () => import_core3.assertValidProductRef,
58
+ autoRechargeUrlFrom: () => autoRechargeUrlFrom,
56
59
  balanceSummary: () => balanceSummary,
57
60
  buildAuthInfoFromBearer: () => buildAuthInfoFromBearer,
58
61
  buildPayableHandler: () => buildPayableHandler,
@@ -120,10 +123,13 @@ var MCP_PROMPT_NAMES = {
120
123
  };
121
124
 
122
125
  // src/types.ts
123
- var SOLVAPAY_MCP_VIEW_KINDS = [
126
+ var SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS = [
124
127
  "checkout",
125
128
  "account",
126
- "topup",
129
+ "topup"
130
+ ];
131
+ var SOLVAPAY_MCP_VIEW_KINDS = [
132
+ ...SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS,
127
133
  "auto-recharge"
128
134
  ];
129
135
  var TOOL_FOR_VIEW = {
@@ -756,6 +762,9 @@ function narrateTopup(data) {
756
762
  lines.push(recoveryLine(["account"]));
757
763
  return withCheckout(data, lines);
758
764
  }
765
+ function autoRechargePortalUrl(data) {
766
+ return httpsUrl(data.autoRechargeUrl) ?? httpsUrl(data.portalUrl);
767
+ }
759
768
  function narrateAutoRecharge(data) {
760
769
  const lines = [];
761
770
  lines.push(`**Auto-recharge \u2014 ${productName(data)}**`);
@@ -764,16 +773,27 @@ function narrateAutoRecharge(data) {
764
773
  const bal = balanceRow(customer);
765
774
  if (bal) lines.push(bal);
766
775
  const enabled = customer?.autoRecharge?.enabled === true;
767
- lines.push(
768
- enabled ? "Auto-recharge is on. It tops your balance up automatically so calls do not fail." : "Auto-recharge is off. Turn it on from the account page \u2014 it stores a card and tops your balance up automatically so calls do not fail."
769
- );
770
- const manage = manageRow(data);
771
- if (manage) lines.push(manage);
776
+ const failed = customer?.autoRecharge?.status === "failed";
777
+ if (failed) {
778
+ lines.push("Auto-recharge failed \u2014 update your card to resume");
779
+ } else {
780
+ lines.push(
781
+ enabled ? "Auto-recharge is on. It tops your balance up automatically so calls do not fail." : "Auto-recharge is off. Turn it on from the link below \u2014 it stores a card and tops your balance up automatically so calls do not fail."
782
+ );
783
+ }
784
+ const manageUrl = autoRechargePortalUrl(data);
785
+ if (manageUrl) {
786
+ lines.push(`Manage: ${namedManageMarkdown(manageUrl)} (${CHECKOUT_TTL})`);
787
+ }
772
788
  lines.push("");
773
789
  lines.push(recoveryLine(["account"]));
774
790
  const links = [];
775
- const portal = hostedPortalLink(data);
776
- if (portal) links.push({ ...portal, name: enabled ? "Manage auto-recharge" : "Turn on auto-recharge" });
791
+ if (manageUrl) {
792
+ links.push({
793
+ uri: manageUrl,
794
+ name: enabled ? "Manage auto-recharge" : "Turn on auto-recharge"
795
+ });
796
+ }
777
797
  return { text: lines.join("\n"), links };
778
798
  }
779
799
  var NARRATORS = {
@@ -1000,7 +1020,8 @@ var BootstrapPayloadSchema = import_zod2.z.object({
1000
1020
  plans: import_zod2.z.array(import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown())),
1001
1021
  customer: import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).nullable(),
1002
1022
  checkoutUrl: import_zod2.z.string().nullable().optional(),
1003
- portalUrl: import_zod2.z.string().nullable().optional()
1023
+ portalUrl: import_zod2.z.string().nullable().optional(),
1024
+ autoRechargeUrl: import_zod2.z.string().nullable().optional()
1004
1025
  }).passthrough();
1005
1026
 
1006
1027
  // src/derive-view.ts
@@ -1012,7 +1033,7 @@ function isOutOfCredits(customer) {
1012
1033
  const credits = customer?.balance?.credits;
1013
1034
  return credits === 0;
1014
1035
  }
1015
- function deriveDefaultView(data, enabledViews = new Set(SOLVAPAY_MCP_VIEW_KINDS)) {
1036
+ function deriveDefaultView(data, enabledViews = new Set(SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS)) {
1016
1037
  const customer = data.customer;
1017
1038
  const preferred = !hasActivePlan(customer, data.productRef) ? "checkout" : isOutOfCredits(customer) ? "topup" : "account";
1018
1039
  if (enabledViews.has(preferred)) return preferred;
@@ -1181,6 +1202,18 @@ function resetMcpConfigLogForTests() {
1181
1202
 
1182
1203
  // src/bootstrap-payload.ts
1183
1204
  var import_server3 = require("@solvapay/server");
1205
+
1206
+ // src/portal-links.ts
1207
+ var PORTAL_AUTO_RECHARGE_QUERY = "tab=credits&intent=autorecharge";
1208
+ function autoRechargeUrlFrom(customerUrl) {
1209
+ if (typeof customerUrl !== "string" || !/^https?:\/\//i.test(customerUrl)) {
1210
+ return null;
1211
+ }
1212
+ const separator = customerUrl.includes("?") ? "&" : "?";
1213
+ return `${customerUrl}${separator}${PORTAL_AUTO_RECHARGE_QUERY}`;
1214
+ }
1215
+
1216
+ // src/bootstrap-payload.ts
1184
1217
  var okOrNull = (result) => (0, import_server3.isErrorResult)(result) ? null : result;
1185
1218
  var buildProviderNotFoundMessage = () => [
1186
1219
  "Provider account not found on this SolvaPay deployment.",
@@ -1220,7 +1253,10 @@ function createBuildBootstrapPayload(options) {
1220
1253
  try {
1221
1254
  const platform = await solvaPay.apiClient.getPlatformConfig?.();
1222
1255
  return platform?.stripePublishableKey ?? null;
1223
- } catch {
1256
+ } catch (err) {
1257
+ console.warn("[solvapay] bootstrap: getPlatformConfig failed; stripePublishableKey omitted", {
1258
+ error: err instanceof Error ? err.message : String(err)
1259
+ });
1224
1260
  return null;
1225
1261
  }
1226
1262
  };
@@ -1286,6 +1322,12 @@ function createBuildBootstrapPayload(options) {
1286
1322
  if ((0, import_server3.isErrorResult)(productResult)) {
1287
1323
  throw createBootstrapProductError(productResult);
1288
1324
  }
1325
+ if ((0, import_server3.isErrorResult)(plansResult)) {
1326
+ console.warn("[solvapay] bootstrap: listPlans failed; plans omitted from payload", {
1327
+ error: plansResult.error,
1328
+ status: plansResult.status
1329
+ });
1330
+ }
1289
1331
  const plans = (0, import_server3.isErrorResult)(plansResult) ? [] : plansResult.plans;
1290
1332
  const purchase = okOrNull(resolvedPurchaseResult);
1291
1333
  const enrichedPurchase = purchase ? {
@@ -1333,7 +1375,8 @@ function createBuildBootstrapPayload(options) {
1333
1375
  plans,
1334
1376
  customer,
1335
1377
  checkoutUrl: checkout?.checkoutUrl ?? null,
1336
- portalUrl: portal?.customerUrl ?? null
1378
+ portalUrl: portal?.customerUrl ?? null,
1379
+ autoRechargeUrl: autoRechargeUrlFrom(portal?.customerUrl)
1337
1380
  };
1338
1381
  };
1339
1382
  }
@@ -1411,7 +1454,13 @@ var VIEWER_DESCRIPTION = 'Call when the user says "upgrade", "change plan", "buy
1411
1454
  var INTENT_MODE_SCHEMA = import_zod3.z.enum(["ui", "text", "auto"]).optional().describe(
1412
1455
  "Default `mode: 'auto'` returns a self-sufficient text summary (plan, price, https checkout URL) and still opens the iframe on UI hosts. Pass `mode: 'text'` to strip the iframe, or `mode: 'ui'` for a one-line placeholder that still includes the checkout URL."
1413
1456
  );
1414
- var DEFAULT_VIEWS = [...SOLVAPAY_MCP_VIEW_KINDS];
1457
+ var DEFAULT_VIEWS = [...SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS];
1458
+ function readAutoRechargeArg(value) {
1459
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
1460
+ return void 0;
1461
+ }
1462
+ return value;
1463
+ }
1415
1464
  function buildSolvaPayDescriptors(options) {
1416
1465
  const {
1417
1466
  solvaPay,
@@ -1505,9 +1554,17 @@ function buildSolvaPayDescriptors(options) {
1505
1554
  publicBaseUrl,
1506
1555
  getCustomerRef
1507
1556
  });
1508
- const enabledViewList = SOLVAPAY_MCP_VIEW_KINDS.filter((view) => enabledViews.has(view));
1509
- if (enabledViewList.length > 0) {
1510
- const viewEnum = import_zod3.z.enum(enabledViewList).optional().describe(VIEW_PARAM_DESCRIPTION);
1557
+ const advertisedViewList = SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS.filter(
1558
+ (view) => enabledViews.has(view)
1559
+ );
1560
+ if (advertisedViewList.length > 0) {
1561
+ const advertisedEnum = import_zod3.z.enum(
1562
+ advertisedViewList
1563
+ );
1564
+ const viewEnum = import_zod3.z.preprocess(
1565
+ (value) => value === "auto-recharge" ? "account" : value,
1566
+ advertisedEnum
1567
+ ).optional().describe(VIEW_PARAM_DESCRIPTION);
1511
1568
  pushTool({
1512
1569
  name: VIEWER_TOOL_NAME,
1513
1570
  title: "Account",
@@ -1518,11 +1575,11 @@ function buildSolvaPayDescriptors(options) {
1518
1575
  annotations: VIEWER_ANNOTATIONS,
1519
1576
  handler: async (args, extra) => trace(VIEWER_TOOL_NAME, args, extra, async () => {
1520
1577
  const requested = args.view === "checkout" || args.view === "account" || args.view === "topup" || args.view === "auto-recharge" ? args.view : void 0;
1521
- if (requested !== void 0 && !enabledViews.has(requested)) {
1578
+ if (requested !== void 0 && requested !== "auto-recharge" && !enabledViews.has(requested)) {
1522
1579
  return toolErrorResult({
1523
1580
  error: `view '${requested}' is not enabled on this server`,
1524
1581
  status: 400,
1525
- details: `Enabled views: ${enabledViewList.join(", ")}. Pass one of those, or omit view to let the server pick.`
1582
+ details: `Enabled views: ${advertisedViewList.join(", ")}. Pass one of those, or omit view to let the server pick.`
1526
1583
  });
1527
1584
  }
1528
1585
  const mode = parseMode(args.mode);
@@ -1577,14 +1634,22 @@ function buildSolvaPayDescriptors(options) {
1577
1634
  });
1578
1635
  pushTool({
1579
1636
  name: MCP_TOOL_NAMES.createPayment,
1580
- description: UI_ONLY_PREFIX + 'Create a Stripe payment intent for the authenticated customer. Pass purpose: "plan" to purchase a plan (returns { clientSecret, publishableKey, accountId?, customerRef }) or purpose: "topup" for a credit top-up (credits are recorded by webhook after confirmation).',
1637
+ description: UI_ONLY_PREFIX + 'Create a Stripe payment intent for the authenticated customer. Pass purpose: "plan" to purchase a plan (returns { clientSecret, publishableKey, accountId?, customerRef }) or purpose: "topup" for a credit top-up (credits are recorded by webhook after confirmation). A topup may carry autoRecharge so the card entered for the top-up is saved as the auto-recharge funding source.',
1581
1638
  inputSchema: {
1582
1639
  purpose: import_zod3.z.enum(["plan", "topup"]).describe('"plan" for plan checkout; "topup" for credit top-up.'),
1583
1640
  planRef: import_zod3.z.string().optional(),
1584
1641
  productRef: import_zod3.z.string().optional(),
1585
1642
  currency: import_zod3.z.string().optional(),
1586
1643
  amount: import_zod3.z.number().int().positive().optional(),
1587
- description: import_zod3.z.string().optional()
1644
+ description: import_zod3.z.string().optional(),
1645
+ autoRecharge: import_zod3.z.object({
1646
+ enabled: import_zod3.z.boolean(),
1647
+ triggerType: import_zod3.z.literal("balance"),
1648
+ thresholdAmountMajor: import_zod3.z.number().positive().optional(),
1649
+ topupAmountMajor: import_zod3.z.number().positive().optional(),
1650
+ maxMonthlySpendMajor: import_zod3.z.number().positive().optional(),
1651
+ currency: import_zod3.z.string().length(3)
1652
+ }).optional()
1588
1653
  },
1589
1654
  meta: uiToolMeta,
1590
1655
  annotations: solvapayTool({ readOnlyHint: false, destructiveHint: false }),
@@ -1599,6 +1664,14 @@ function buildSolvaPayDescriptors(options) {
1599
1664
  details: 'Pass purpose: "plan" or purpose: "topup".'
1600
1665
  });
1601
1666
  }
1667
+ const autoRecharge = readAutoRechargeArg(args.autoRecharge);
1668
+ if (purpose === "plan" && autoRecharge) {
1669
+ return toolErrorResult({
1670
+ error: "create_payment_intent plan does not accept autoRecharge",
1671
+ status: 400,
1672
+ details: 'autoRecharge is only honoured on purpose: "topup".'
1673
+ });
1674
+ }
1602
1675
  if (purpose === "topup") {
1603
1676
  const amount = typeof args.amount === "number" ? args.amount : 0;
1604
1677
  const currency2 = typeof args.currency === "string" ? args.currency : "";
@@ -1612,7 +1685,7 @@ function buildSolvaPayDescriptors(options) {
1612
1685
  }
1613
1686
  const result2 = await (0, import_server4.createTopupPaymentIntentCore)(
1614
1687
  buildRequest(extra, { method: "POST" }),
1615
- { amount, currency: currency2, description },
1688
+ { amount, currency: currency2, description, ...autoRecharge ? { autoRecharge } : {} },
1616
1689
  { solvaPay }
1617
1690
  );
1618
1691
  if ((0, import_server4.isErrorResult)(result2)) return toolErrorResult(result2);
@@ -2261,9 +2334,11 @@ function buildAuthInfoFromBearer(authorization, options = {}) {
2261
2334
  McpBearerAuthError,
2262
2335
  NARRATORS,
2263
2336
  OPEN_TOOL_FOR_VIEW,
2337
+ PORTAL_AUTO_RECHARGE_QUERY,
2264
2338
  SOLVAPAY_BOOTSTRAP_MIME_TYPE,
2265
2339
  SOLVAPAY_BOOTSTRAP_URI,
2266
2340
  SOLVAPAY_DEFAULT_CSP,
2341
+ SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS,
2267
2342
  SOLVAPAY_MCP_VIEW_KINDS,
2268
2343
  SOLVAPAY_OVERVIEW_MARKDOWN,
2269
2344
  SOLVAPAY_OVERVIEW_MIME_TYPE,
@@ -2276,6 +2351,7 @@ function buildAuthInfoFromBearer(authorization, options = {}) {
2276
2351
  VIEW_FOR_TOOL,
2277
2352
  applyHideToolsByAudience,
2278
2353
  assertValidProductRef,
2354
+ autoRechargeUrlFrom,
2279
2355
  balanceSummary,
2280
2356
  buildAuthInfoFromBearer,
2281
2357
  buildPayableHandler,
package/dist/index.d.cts CHANGED
@@ -201,14 +201,14 @@ interface PaywallToolResult {
201
201
  /**
202
202
  * Which view a SolvaPay MCP server knows how to bootstrap.
203
203
  *
204
- * Each kind is a landing screen on the single `account` viewer:
205
- * `checkout`, `account`, `topup`, plus `auto-recharge` which is
206
- * reached by intent only (never a default). There is no `paywall` or `nudge`
207
- * view — those
208
- * responses are plain text narrations per the text-only paywall
209
- * refactor (merchant paywall / nudge tool results ship
210
- * `content[0].text` + `structuredContent = gate` and never open the
211
- * iframe).
204
+ * Advertised landings on the `account` viewer are `checkout`,
205
+ * `account`, and `topup`. `'auto-recharge'` is a deprecated leftover
206
+ * stamp: the tool schema does not offer it, but a leftover argument
207
+ * still opens the account surface (status row + portal link). There is
208
+ * no `paywall` or `nudge` view — those responses are plain text
209
+ * narrations per the text-only paywall refactor (merchant paywall /
210
+ * nudge tool results ship `content[0].text` + `structuredContent =
211
+ * gate` and never open the iframe).
212
212
  *
213
213
  * The legacy `'about'`, `'activate'`, and `'usage'` surfaces were
214
214
  * dropped earlier — About is served by tool descriptions + docs
@@ -216,7 +216,9 @@ interface PaywallToolResult {
216
216
  * `PlanActivationDispatcher`, and Usage folds inline into the account
217
217
  * view.
218
218
  */
219
- type SolvaPayMcpViewKind = 'checkout' | 'account' | 'topup' | 'auto-recharge';
219
+ type SolvaPayMcpAdvertisedViewKind = 'checkout' | 'account' | 'topup';
220
+ type SolvaPayMcpViewKind = SolvaPayMcpAdvertisedViewKind | 'auto-recharge';
221
+ declare const SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS: readonly ["checkout", "account", "topup"];
220
222
  declare const SOLVAPAY_MCP_VIEW_KINDS: readonly ["checkout", "account", "topup", "auto-recharge"];
221
223
  /**
222
224
  * Payload returned by every `open_*` bootstrap tool and consumed by the
@@ -249,6 +251,12 @@ interface BootstrapPayload {
249
251
  checkoutUrl?: string | null;
250
252
  /** Customer portal URL for the current customer, when one could be minted. */
251
253
  portalUrl?: string | null;
254
+ /**
255
+ * Hosted portal URL that opens the auto-recharge form (`tab=credits&intent=autorecharge`).
256
+ * Null when no portal session could be minted. Suffixed in `@solvapay/mcp-core`
257
+ * from the same `customerUrl` as `portalUrl`.
258
+ */
259
+ autoRechargeUrl?: string | null;
252
260
  }
253
261
  /**
254
262
  * Content Security Policy allow-list inputs merged with the Stripe
@@ -844,6 +852,7 @@ declare const BootstrapPayloadSchema: z.ZodObject<{
844
852
  customer: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
845
853
  checkoutUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
846
854
  portalUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
855
+ autoRechargeUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
847
856
  }, z.core.$loose>;
848
857
 
849
858
  /**
@@ -1410,6 +1419,31 @@ declare const SOLVAPAY_OVERVIEW_URI = "docs://solvapay/overview.md";
1410
1419
  declare const SOLVAPAY_OVERVIEW_MIME_TYPE = "text/markdown";
1411
1420
  declare const SOLVAPAY_OVERVIEW_MARKDOWN = "# SolvaPay MCP server \u2014 overview\n\nThis MCP server connects a SolvaPay-protected product to your host so users can\nmanage their plan, usage, and billing **without leaving the chat**. It is\ndual-audience: every tool returns a UI bootstrap for hosts that render MCP UI\nresources (`basic-host`, Claude Desktop, ChatGPT, etc.) and a markdown summary\nwith clickable URLs for text-only hosts.\n\n## What the user can do\n\n- **Account** \u2014 one read-only viewer for every billing surface. Pass `view`:\n `checkout` (upgrade / change plan / buy / subscribe), `account` (current\n plan, cancel, billing), or `topup` (add credits). Omit `view` and the\n server picks (no plan \u2192 checkout, out of credits \u2192 topup, else account).\n Slash commands `/upgrade`, `/manage_account`, and `/topup` remap onto\n this tool. `account`.\n- **Activate plan** \u2014 activate a specific plan by `planRef`. Free plans\n activate immediately, usage-based plans activate when balance covers the\n configured usage, paid plans return checkout. Requires `planRef` \u2014 to list\n plans, call `account` with `view: \"checkout\"`. `/activate_plan`.\n\n## How it fits together\n\nThe viewer returns a full `BootstrapPayload` (merchant + product + plans\n+ customer snapshot) so the embedded UI never fires per-view read calls. When\na paywalled data tool hits the usage limit, its response is plain text: the\n`content[0].text` narration names the recovery tool (`account`, or\n`activate_plan` when a specific planRef is known) and inlines\n`gate.checkoutUrl` for terminal-first hosts. The structured gate rides on\n`structuredContent` for programmatic consumers. No widget iframe opens for a\ngate \u2014 the LLM reads the narration and, if the user agrees, calls the named\ntool which mounts the checkout / topup / account surface.\n\nAuth is handled by the SolvaPay OAuth bridge (see `createMcpOAuthBridge`). The\nbridge injects `customer_ref` onto every authenticated request; tools that\nneed an authenticated caller return `Unauthorized` when it is missing.\n\n## Also see\n\n- `docs://solvapay/overview.md` (this resource) \u2014 agent-facing narration.\n- `ui://<app>/mcp-app.html` \u2014 the embedded UI shell.\n- `tools/list` + `prompts/list` \u2014 programmatic discovery of the intent tools\n and their slash-command shortcuts.\n";
1412
1421
 
1422
+ /**
1423
+ * Hosted-portal deep links minted from a customer session URL.
1424
+ *
1425
+ * Phase 1 suffixes the already-minted `customerUrl` in the SDK so the
1426
+ * MCP widget can land on the auto-recharge form without a new network
1427
+ * call or a platform deploy. The query params are the same ones the
1428
+ * customer-app manage page already parses (`tab=credits&intent=autorecharge`).
1429
+ *
1430
+ * Keep this constant in lockstep with
1431
+ * `apps/customer-app/src/pages/customer/manage/index.tsx`. A rename
1432
+ * there without a matching change here degrades to the Credits tab
1433
+ * without the form open — not a dead link, but not the intended
1434
+ * destination either.
1435
+ */
1436
+ declare const PORTAL_AUTO_RECHARGE_QUERY = "tab=credits&intent=autorecharge";
1437
+ /**
1438
+ * Suffix a minted customer-portal URL so it opens the auto-recharge
1439
+ * form (or the Credits summary when auto-recharge is already on).
1440
+ *
1441
+ * Returns `null` for a missing or non-http URL — the same guard
1442
+ * `narrate.ts` uses. No silent default: a missing portal URL yields
1443
+ * `null` and the widget renders status without an action.
1444
+ */
1445
+ declare function autoRechargeUrlFrom(customerUrl: string | null | undefined): string | null;
1446
+
1413
1447
  /**
1414
1448
  * `buildPayableHandler(solvaPay, ctx, handler)` — framework-neutral
1415
1449
  * wrapper that produces an MCP tool handler enforcing the SolvaPay
@@ -1607,4 +1641,4 @@ interface BuildAuthInfoFromBearerOptions extends McpBearerCustomerRefOptions {
1607
1641
  }
1608
1642
  declare function buildAuthInfoFromBearer(authorization?: string | null, options?: BuildAuthInfoFromBearerOptions): McpToolExtra['authInfo'] | null;
1609
1643
 
1610
- export { type ActivePlanPurchaseLike, type ApplyHideToolsByAudienceContext, type ApplyHideToolsByAudienceExtra, type ApplyHideToolsByAudienceOptions, type BootstrapCustomer, type BootstrapMerchant, type BootstrapPayload, BootstrapPayloadSchema, type BootstrapPlan, type BootstrapProduct, type BuildAuthInfoFromBearerOptions, type BuildBootstrapPayloadFn, type BuildPayableHandlerContext, type BuildSolvaPayDescriptorsOptions, type BuildSolvaPayRequestOptions, type ContentBlock, type CreateBuildBootstrapPayloadOptions, type CustomerSnapshot, DEFAULT_OAUTH_PATHS, type DcrFailureDiagnosticInput, type HideToolsByAudienceBypass, INTENT_TOOL_NAMES, type IntentToolName, MCP_PROMPT_NAMES, MCP_TOOL_NAMES, type McpAdapterOptions, McpBearerAuthError, type McpBearerCustomerRefOptions, type McpConfigLogInput, type McpPromptName, type McpToolExtra, type McpToolName, NARRATORS, type NarratorOutput, type NudgeSpec, type OAuthAuthorizationServerOptions, type OAuthBridgePaths, OPEN_TOOL_FOR_VIEW, type PayableHandler, type PaywallToolResult, type PaywallToolResultContext, type ResponseContext, type ResponseOptions, type ResponseResult, SOLVAPAY_BOOTSTRAP_MIME_TYPE, SOLVAPAY_BOOTSTRAP_URI, SOLVAPAY_DEFAULT_CSP, SOLVAPAY_MCP_VIEW_KINDS, SOLVAPAY_OVERVIEW_MARKDOWN, SOLVAPAY_OVERVIEW_MIME_TYPE, SOLVAPAY_OVERVIEW_URI, type SolvaPayBootstrapResourceDescriptor, type SolvaPayCallToolResult, type SolvaPayDescriptorBundle, type SolvaPayDocsResourceDescriptor, type SolvaPayMcpCsp, type SolvaPayMcpViewKind, type SolvaPayMerchantBranding, type SolvaPayPromptDescriptor, type SolvaPayPromptResult, type SolvaPayResourceDescriptor, type SolvaPayToolAnnotations, type SolvaPayToolDescriptor, type SolvaPayToolIcon, type SolvaPayToolMode, TOOL_FOR_VIEW, ToolErrorEnvelopeSchema, VIEWER_TOOL_NAME, VIEW_FOR_OPEN_TOOL, VIEW_FOR_TOOL, applyHideToolsByAudience, balanceSummary, buildAuthInfoFromBearer, buildPayableHandler, buildSolvaPayDescriptors, buildSolvaPayPrompts, buildSolvaPayRequest, createBuildBootstrapPayload, decodeJwtPayload, defaultGetCustomerRef, defaultIsChatGptRequest, deriveDefaultView, deriveIcons, enrichPurchase, extractBearerToken, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse, isPlanPurchase, logDcrFailureDiagnostic, logMcpConfigOnce, mergeCsp, narrateActivatePlan, narrateAlreadyActive, narrateAutoRecharge, narrateManageAccount, narrateTopup, narrateUpgrade, narratedToolResult, parseMode, paywallToolResult, previewJson, resetMcpConfigLogForTests, resolveOAuthPaths, selectActivePlanPurchase, toolErrorResult, toolResult, uiPlaceholder, withoutTrailingSlash };
1644
+ export { type ActivePlanPurchaseLike, type ApplyHideToolsByAudienceContext, type ApplyHideToolsByAudienceExtra, type ApplyHideToolsByAudienceOptions, type BootstrapCustomer, type BootstrapMerchant, type BootstrapPayload, BootstrapPayloadSchema, type BootstrapPlan, type BootstrapProduct, type BuildAuthInfoFromBearerOptions, type BuildBootstrapPayloadFn, type BuildPayableHandlerContext, type BuildSolvaPayDescriptorsOptions, type BuildSolvaPayRequestOptions, type ContentBlock, type CreateBuildBootstrapPayloadOptions, type CustomerSnapshot, DEFAULT_OAUTH_PATHS, type DcrFailureDiagnosticInput, type HideToolsByAudienceBypass, INTENT_TOOL_NAMES, type IntentToolName, MCP_PROMPT_NAMES, MCP_TOOL_NAMES, type McpAdapterOptions, McpBearerAuthError, type McpBearerCustomerRefOptions, type McpConfigLogInput, type McpPromptName, type McpToolExtra, type McpToolName, NARRATORS, type NarratorOutput, type NudgeSpec, type OAuthAuthorizationServerOptions, type OAuthBridgePaths, OPEN_TOOL_FOR_VIEW, PORTAL_AUTO_RECHARGE_QUERY, type PayableHandler, type PaywallToolResult, type PaywallToolResultContext, type ResponseContext, type ResponseOptions, type ResponseResult, SOLVAPAY_BOOTSTRAP_MIME_TYPE, SOLVAPAY_BOOTSTRAP_URI, SOLVAPAY_DEFAULT_CSP, SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS, SOLVAPAY_MCP_VIEW_KINDS, SOLVAPAY_OVERVIEW_MARKDOWN, SOLVAPAY_OVERVIEW_MIME_TYPE, SOLVAPAY_OVERVIEW_URI, type SolvaPayBootstrapResourceDescriptor, type SolvaPayCallToolResult, type SolvaPayDescriptorBundle, type SolvaPayDocsResourceDescriptor, type SolvaPayMcpAdvertisedViewKind, type SolvaPayMcpCsp, type SolvaPayMcpViewKind, type SolvaPayMerchantBranding, type SolvaPayPromptDescriptor, type SolvaPayPromptResult, type SolvaPayResourceDescriptor, type SolvaPayToolAnnotations, type SolvaPayToolDescriptor, type SolvaPayToolIcon, type SolvaPayToolMode, TOOL_FOR_VIEW, ToolErrorEnvelopeSchema, VIEWER_TOOL_NAME, VIEW_FOR_OPEN_TOOL, VIEW_FOR_TOOL, applyHideToolsByAudience, autoRechargeUrlFrom, balanceSummary, buildAuthInfoFromBearer, buildPayableHandler, buildSolvaPayDescriptors, buildSolvaPayPrompts, buildSolvaPayRequest, createBuildBootstrapPayload, decodeJwtPayload, defaultGetCustomerRef, defaultIsChatGptRequest, deriveDefaultView, deriveIcons, enrichPurchase, extractBearerToken, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse, isPlanPurchase, logDcrFailureDiagnostic, logMcpConfigOnce, mergeCsp, narrateActivatePlan, narrateAlreadyActive, narrateAutoRecharge, narrateManageAccount, narrateTopup, narrateUpgrade, narratedToolResult, parseMode, paywallToolResult, previewJson, resetMcpConfigLogForTests, resolveOAuthPaths, selectActivePlanPurchase, toolErrorResult, toolResult, uiPlaceholder, withoutTrailingSlash };
package/dist/index.d.ts CHANGED
@@ -201,14 +201,14 @@ interface PaywallToolResult {
201
201
  /**
202
202
  * Which view a SolvaPay MCP server knows how to bootstrap.
203
203
  *
204
- * Each kind is a landing screen on the single `account` viewer:
205
- * `checkout`, `account`, `topup`, plus `auto-recharge` which is
206
- * reached by intent only (never a default). There is no `paywall` or `nudge`
207
- * view — those
208
- * responses are plain text narrations per the text-only paywall
209
- * refactor (merchant paywall / nudge tool results ship
210
- * `content[0].text` + `structuredContent = gate` and never open the
211
- * iframe).
204
+ * Advertised landings on the `account` viewer are `checkout`,
205
+ * `account`, and `topup`. `'auto-recharge'` is a deprecated leftover
206
+ * stamp: the tool schema does not offer it, but a leftover argument
207
+ * still opens the account surface (status row + portal link). There is
208
+ * no `paywall` or `nudge` view — those responses are plain text
209
+ * narrations per the text-only paywall refactor (merchant paywall /
210
+ * nudge tool results ship `content[0].text` + `structuredContent =
211
+ * gate` and never open the iframe).
212
212
  *
213
213
  * The legacy `'about'`, `'activate'`, and `'usage'` surfaces were
214
214
  * dropped earlier — About is served by tool descriptions + docs
@@ -216,7 +216,9 @@ interface PaywallToolResult {
216
216
  * `PlanActivationDispatcher`, and Usage folds inline into the account
217
217
  * view.
218
218
  */
219
- type SolvaPayMcpViewKind = 'checkout' | 'account' | 'topup' | 'auto-recharge';
219
+ type SolvaPayMcpAdvertisedViewKind = 'checkout' | 'account' | 'topup';
220
+ type SolvaPayMcpViewKind = SolvaPayMcpAdvertisedViewKind | 'auto-recharge';
221
+ declare const SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS: readonly ["checkout", "account", "topup"];
220
222
  declare const SOLVAPAY_MCP_VIEW_KINDS: readonly ["checkout", "account", "topup", "auto-recharge"];
221
223
  /**
222
224
  * Payload returned by every `open_*` bootstrap tool and consumed by the
@@ -249,6 +251,12 @@ interface BootstrapPayload {
249
251
  checkoutUrl?: string | null;
250
252
  /** Customer portal URL for the current customer, when one could be minted. */
251
253
  portalUrl?: string | null;
254
+ /**
255
+ * Hosted portal URL that opens the auto-recharge form (`tab=credits&intent=autorecharge`).
256
+ * Null when no portal session could be minted. Suffixed in `@solvapay/mcp-core`
257
+ * from the same `customerUrl` as `portalUrl`.
258
+ */
259
+ autoRechargeUrl?: string | null;
252
260
  }
253
261
  /**
254
262
  * Content Security Policy allow-list inputs merged with the Stripe
@@ -844,6 +852,7 @@ declare const BootstrapPayloadSchema: z.ZodObject<{
844
852
  customer: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
845
853
  checkoutUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
846
854
  portalUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
855
+ autoRechargeUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
847
856
  }, z.core.$loose>;
848
857
 
849
858
  /**
@@ -1410,6 +1419,31 @@ declare const SOLVAPAY_OVERVIEW_URI = "docs://solvapay/overview.md";
1410
1419
  declare const SOLVAPAY_OVERVIEW_MIME_TYPE = "text/markdown";
1411
1420
  declare const SOLVAPAY_OVERVIEW_MARKDOWN = "# SolvaPay MCP server \u2014 overview\n\nThis MCP server connects a SolvaPay-protected product to your host so users can\nmanage their plan, usage, and billing **without leaving the chat**. It is\ndual-audience: every tool returns a UI bootstrap for hosts that render MCP UI\nresources (`basic-host`, Claude Desktop, ChatGPT, etc.) and a markdown summary\nwith clickable URLs for text-only hosts.\n\n## What the user can do\n\n- **Account** \u2014 one read-only viewer for every billing surface. Pass `view`:\n `checkout` (upgrade / change plan / buy / subscribe), `account` (current\n plan, cancel, billing), or `topup` (add credits). Omit `view` and the\n server picks (no plan \u2192 checkout, out of credits \u2192 topup, else account).\n Slash commands `/upgrade`, `/manage_account`, and `/topup` remap onto\n this tool. `account`.\n- **Activate plan** \u2014 activate a specific plan by `planRef`. Free plans\n activate immediately, usage-based plans activate when balance covers the\n configured usage, paid plans return checkout. Requires `planRef` \u2014 to list\n plans, call `account` with `view: \"checkout\"`. `/activate_plan`.\n\n## How it fits together\n\nThe viewer returns a full `BootstrapPayload` (merchant + product + plans\n+ customer snapshot) so the embedded UI never fires per-view read calls. When\na paywalled data tool hits the usage limit, its response is plain text: the\n`content[0].text` narration names the recovery tool (`account`, or\n`activate_plan` when a specific planRef is known) and inlines\n`gate.checkoutUrl` for terminal-first hosts. The structured gate rides on\n`structuredContent` for programmatic consumers. No widget iframe opens for a\ngate \u2014 the LLM reads the narration and, if the user agrees, calls the named\ntool which mounts the checkout / topup / account surface.\n\nAuth is handled by the SolvaPay OAuth bridge (see `createMcpOAuthBridge`). The\nbridge injects `customer_ref` onto every authenticated request; tools that\nneed an authenticated caller return `Unauthorized` when it is missing.\n\n## Also see\n\n- `docs://solvapay/overview.md` (this resource) \u2014 agent-facing narration.\n- `ui://<app>/mcp-app.html` \u2014 the embedded UI shell.\n- `tools/list` + `prompts/list` \u2014 programmatic discovery of the intent tools\n and their slash-command shortcuts.\n";
1412
1421
 
1422
+ /**
1423
+ * Hosted-portal deep links minted from a customer session URL.
1424
+ *
1425
+ * Phase 1 suffixes the already-minted `customerUrl` in the SDK so the
1426
+ * MCP widget can land on the auto-recharge form without a new network
1427
+ * call or a platform deploy. The query params are the same ones the
1428
+ * customer-app manage page already parses (`tab=credits&intent=autorecharge`).
1429
+ *
1430
+ * Keep this constant in lockstep with
1431
+ * `apps/customer-app/src/pages/customer/manage/index.tsx`. A rename
1432
+ * there without a matching change here degrades to the Credits tab
1433
+ * without the form open — not a dead link, but not the intended
1434
+ * destination either.
1435
+ */
1436
+ declare const PORTAL_AUTO_RECHARGE_QUERY = "tab=credits&intent=autorecharge";
1437
+ /**
1438
+ * Suffix a minted customer-portal URL so it opens the auto-recharge
1439
+ * form (or the Credits summary when auto-recharge is already on).
1440
+ *
1441
+ * Returns `null` for a missing or non-http URL — the same guard
1442
+ * `narrate.ts` uses. No silent default: a missing portal URL yields
1443
+ * `null` and the widget renders status without an action.
1444
+ */
1445
+ declare function autoRechargeUrlFrom(customerUrl: string | null | undefined): string | null;
1446
+
1413
1447
  /**
1414
1448
  * `buildPayableHandler(solvaPay, ctx, handler)` — framework-neutral
1415
1449
  * wrapper that produces an MCP tool handler enforcing the SolvaPay
@@ -1607,4 +1641,4 @@ interface BuildAuthInfoFromBearerOptions extends McpBearerCustomerRefOptions {
1607
1641
  }
1608
1642
  declare function buildAuthInfoFromBearer(authorization?: string | null, options?: BuildAuthInfoFromBearerOptions): McpToolExtra['authInfo'] | null;
1609
1643
 
1610
- export { type ActivePlanPurchaseLike, type ApplyHideToolsByAudienceContext, type ApplyHideToolsByAudienceExtra, type ApplyHideToolsByAudienceOptions, type BootstrapCustomer, type BootstrapMerchant, type BootstrapPayload, BootstrapPayloadSchema, type BootstrapPlan, type BootstrapProduct, type BuildAuthInfoFromBearerOptions, type BuildBootstrapPayloadFn, type BuildPayableHandlerContext, type BuildSolvaPayDescriptorsOptions, type BuildSolvaPayRequestOptions, type ContentBlock, type CreateBuildBootstrapPayloadOptions, type CustomerSnapshot, DEFAULT_OAUTH_PATHS, type DcrFailureDiagnosticInput, type HideToolsByAudienceBypass, INTENT_TOOL_NAMES, type IntentToolName, MCP_PROMPT_NAMES, MCP_TOOL_NAMES, type McpAdapterOptions, McpBearerAuthError, type McpBearerCustomerRefOptions, type McpConfigLogInput, type McpPromptName, type McpToolExtra, type McpToolName, NARRATORS, type NarratorOutput, type NudgeSpec, type OAuthAuthorizationServerOptions, type OAuthBridgePaths, OPEN_TOOL_FOR_VIEW, type PayableHandler, type PaywallToolResult, type PaywallToolResultContext, type ResponseContext, type ResponseOptions, type ResponseResult, SOLVAPAY_BOOTSTRAP_MIME_TYPE, SOLVAPAY_BOOTSTRAP_URI, SOLVAPAY_DEFAULT_CSP, SOLVAPAY_MCP_VIEW_KINDS, SOLVAPAY_OVERVIEW_MARKDOWN, SOLVAPAY_OVERVIEW_MIME_TYPE, SOLVAPAY_OVERVIEW_URI, type SolvaPayBootstrapResourceDescriptor, type SolvaPayCallToolResult, type SolvaPayDescriptorBundle, type SolvaPayDocsResourceDescriptor, type SolvaPayMcpCsp, type SolvaPayMcpViewKind, type SolvaPayMerchantBranding, type SolvaPayPromptDescriptor, type SolvaPayPromptResult, type SolvaPayResourceDescriptor, type SolvaPayToolAnnotations, type SolvaPayToolDescriptor, type SolvaPayToolIcon, type SolvaPayToolMode, TOOL_FOR_VIEW, ToolErrorEnvelopeSchema, VIEWER_TOOL_NAME, VIEW_FOR_OPEN_TOOL, VIEW_FOR_TOOL, applyHideToolsByAudience, balanceSummary, buildAuthInfoFromBearer, buildPayableHandler, buildSolvaPayDescriptors, buildSolvaPayPrompts, buildSolvaPayRequest, createBuildBootstrapPayload, decodeJwtPayload, defaultGetCustomerRef, defaultIsChatGptRequest, deriveDefaultView, deriveIcons, enrichPurchase, extractBearerToken, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse, isPlanPurchase, logDcrFailureDiagnostic, logMcpConfigOnce, mergeCsp, narrateActivatePlan, narrateAlreadyActive, narrateAutoRecharge, narrateManageAccount, narrateTopup, narrateUpgrade, narratedToolResult, parseMode, paywallToolResult, previewJson, resetMcpConfigLogForTests, resolveOAuthPaths, selectActivePlanPurchase, toolErrorResult, toolResult, uiPlaceholder, withoutTrailingSlash };
1644
+ export { type ActivePlanPurchaseLike, type ApplyHideToolsByAudienceContext, type ApplyHideToolsByAudienceExtra, type ApplyHideToolsByAudienceOptions, type BootstrapCustomer, type BootstrapMerchant, type BootstrapPayload, BootstrapPayloadSchema, type BootstrapPlan, type BootstrapProduct, type BuildAuthInfoFromBearerOptions, type BuildBootstrapPayloadFn, type BuildPayableHandlerContext, type BuildSolvaPayDescriptorsOptions, type BuildSolvaPayRequestOptions, type ContentBlock, type CreateBuildBootstrapPayloadOptions, type CustomerSnapshot, DEFAULT_OAUTH_PATHS, type DcrFailureDiagnosticInput, type HideToolsByAudienceBypass, INTENT_TOOL_NAMES, type IntentToolName, MCP_PROMPT_NAMES, MCP_TOOL_NAMES, type McpAdapterOptions, McpBearerAuthError, type McpBearerCustomerRefOptions, type McpConfigLogInput, type McpPromptName, type McpToolExtra, type McpToolName, NARRATORS, type NarratorOutput, type NudgeSpec, type OAuthAuthorizationServerOptions, type OAuthBridgePaths, OPEN_TOOL_FOR_VIEW, PORTAL_AUTO_RECHARGE_QUERY, type PayableHandler, type PaywallToolResult, type PaywallToolResultContext, type ResponseContext, type ResponseOptions, type ResponseResult, SOLVAPAY_BOOTSTRAP_MIME_TYPE, SOLVAPAY_BOOTSTRAP_URI, SOLVAPAY_DEFAULT_CSP, SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS, SOLVAPAY_MCP_VIEW_KINDS, SOLVAPAY_OVERVIEW_MARKDOWN, SOLVAPAY_OVERVIEW_MIME_TYPE, SOLVAPAY_OVERVIEW_URI, type SolvaPayBootstrapResourceDescriptor, type SolvaPayCallToolResult, type SolvaPayDescriptorBundle, type SolvaPayDocsResourceDescriptor, type SolvaPayMcpAdvertisedViewKind, type SolvaPayMcpCsp, type SolvaPayMcpViewKind, type SolvaPayMerchantBranding, type SolvaPayPromptDescriptor, type SolvaPayPromptResult, type SolvaPayResourceDescriptor, type SolvaPayToolAnnotations, type SolvaPayToolDescriptor, type SolvaPayToolIcon, type SolvaPayToolMode, TOOL_FOR_VIEW, ToolErrorEnvelopeSchema, VIEWER_TOOL_NAME, VIEW_FOR_OPEN_TOOL, VIEW_FOR_TOOL, applyHideToolsByAudience, autoRechargeUrlFrom, balanceSummary, buildAuthInfoFromBearer, buildPayableHandler, buildSolvaPayDescriptors, buildSolvaPayPrompts, buildSolvaPayRequest, createBuildBootstrapPayload, decodeJwtPayload, defaultGetCustomerRef, defaultIsChatGptRequest, deriveDefaultView, deriveIcons, enrichPurchase, extractBearerToken, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse, isPlanPurchase, logDcrFailureDiagnostic, logMcpConfigOnce, mergeCsp, narrateActivatePlan, narrateAlreadyActive, narrateAutoRecharge, narrateManageAccount, narrateTopup, narrateUpgrade, narratedToolResult, parseMode, paywallToolResult, previewJson, resetMcpConfigLogForTests, resolveOAuthPaths, selectActivePlanPurchase, toolErrorResult, toolResult, uiPlaceholder, withoutTrailingSlash };
package/dist/index.js CHANGED
@@ -19,10 +19,13 @@ var MCP_PROMPT_NAMES = {
19
19
  };
20
20
 
21
21
  // src/types.ts
22
- var SOLVAPAY_MCP_VIEW_KINDS = [
22
+ var SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS = [
23
23
  "checkout",
24
24
  "account",
25
- "topup",
25
+ "topup"
26
+ ];
27
+ var SOLVAPAY_MCP_VIEW_KINDS = [
28
+ ...SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS,
26
29
  "auto-recharge"
27
30
  ];
28
31
  var TOOL_FOR_VIEW = {
@@ -668,6 +671,9 @@ function narrateTopup(data) {
668
671
  lines.push(recoveryLine(["account"]));
669
672
  return withCheckout(data, lines);
670
673
  }
674
+ function autoRechargePortalUrl(data) {
675
+ return httpsUrl(data.autoRechargeUrl) ?? httpsUrl(data.portalUrl);
676
+ }
671
677
  function narrateAutoRecharge(data) {
672
678
  const lines = [];
673
679
  lines.push(`**Auto-recharge \u2014 ${productName(data)}**`);
@@ -676,16 +682,27 @@ function narrateAutoRecharge(data) {
676
682
  const bal = balanceRow(customer);
677
683
  if (bal) lines.push(bal);
678
684
  const enabled = customer?.autoRecharge?.enabled === true;
679
- lines.push(
680
- enabled ? "Auto-recharge is on. It tops your balance up automatically so calls do not fail." : "Auto-recharge is off. Turn it on from the account page \u2014 it stores a card and tops your balance up automatically so calls do not fail."
681
- );
682
- const manage = manageRow(data);
683
- if (manage) lines.push(manage);
685
+ const failed = customer?.autoRecharge?.status === "failed";
686
+ if (failed) {
687
+ lines.push("Auto-recharge failed \u2014 update your card to resume");
688
+ } else {
689
+ lines.push(
690
+ enabled ? "Auto-recharge is on. It tops your balance up automatically so calls do not fail." : "Auto-recharge is off. Turn it on from the link below \u2014 it stores a card and tops your balance up automatically so calls do not fail."
691
+ );
692
+ }
693
+ const manageUrl = autoRechargePortalUrl(data);
694
+ if (manageUrl) {
695
+ lines.push(`Manage: ${namedManageMarkdown(manageUrl)} (${CHECKOUT_TTL})`);
696
+ }
684
697
  lines.push("");
685
698
  lines.push(recoveryLine(["account"]));
686
699
  const links = [];
687
- const portal = hostedPortalLink(data);
688
- if (portal) links.push({ ...portal, name: enabled ? "Manage auto-recharge" : "Turn on auto-recharge" });
700
+ if (manageUrl) {
701
+ links.push({
702
+ uri: manageUrl,
703
+ name: enabled ? "Manage auto-recharge" : "Turn on auto-recharge"
704
+ });
705
+ }
689
706
  return { text: lines.join("\n"), links };
690
707
  }
691
708
  var NARRATORS = {
@@ -919,7 +936,8 @@ var BootstrapPayloadSchema = z2.object({
919
936
  plans: z2.array(z2.record(z2.string(), z2.unknown())),
920
937
  customer: z2.record(z2.string(), z2.unknown()).nullable(),
921
938
  checkoutUrl: z2.string().nullable().optional(),
922
- portalUrl: z2.string().nullable().optional()
939
+ portalUrl: z2.string().nullable().optional(),
940
+ autoRechargeUrl: z2.string().nullable().optional()
923
941
  }).passthrough();
924
942
 
925
943
  // src/derive-view.ts
@@ -931,7 +949,7 @@ function isOutOfCredits(customer) {
931
949
  const credits = customer?.balance?.credits;
932
950
  return credits === 0;
933
951
  }
934
- function deriveDefaultView(data, enabledViews = new Set(SOLVAPAY_MCP_VIEW_KINDS)) {
952
+ function deriveDefaultView(data, enabledViews = new Set(SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS)) {
935
953
  const customer = data.customer;
936
954
  const preferred = !hasActivePlan(customer, data.productRef) ? "checkout" : isOutOfCredits(customer) ? "topup" : "account";
937
955
  if (enabledViews.has(preferred)) return preferred;
@@ -1128,6 +1146,18 @@ import {
1128
1146
  listPlansCore,
1129
1147
  nextActionFor
1130
1148
  } from "@solvapay/server";
1149
+
1150
+ // src/portal-links.ts
1151
+ var PORTAL_AUTO_RECHARGE_QUERY = "tab=credits&intent=autorecharge";
1152
+ function autoRechargeUrlFrom(customerUrl) {
1153
+ if (typeof customerUrl !== "string" || !/^https?:\/\//i.test(customerUrl)) {
1154
+ return null;
1155
+ }
1156
+ const separator = customerUrl.includes("?") ? "&" : "?";
1157
+ return `${customerUrl}${separator}${PORTAL_AUTO_RECHARGE_QUERY}`;
1158
+ }
1159
+
1160
+ // src/bootstrap-payload.ts
1131
1161
  var okOrNull = (result) => isErrorResult(result) ? null : result;
1132
1162
  var buildProviderNotFoundMessage = () => [
1133
1163
  "Provider account not found on this SolvaPay deployment.",
@@ -1167,7 +1197,10 @@ function createBuildBootstrapPayload(options) {
1167
1197
  try {
1168
1198
  const platform = await solvaPay.apiClient.getPlatformConfig?.();
1169
1199
  return platform?.stripePublishableKey ?? null;
1170
- } catch {
1200
+ } catch (err) {
1201
+ console.warn("[solvapay] bootstrap: getPlatformConfig failed; stripePublishableKey omitted", {
1202
+ error: err instanceof Error ? err.message : String(err)
1203
+ });
1171
1204
  return null;
1172
1205
  }
1173
1206
  };
@@ -1233,6 +1266,12 @@ function createBuildBootstrapPayload(options) {
1233
1266
  if (isErrorResult(productResult)) {
1234
1267
  throw createBootstrapProductError(productResult);
1235
1268
  }
1269
+ if (isErrorResult(plansResult)) {
1270
+ console.warn("[solvapay] bootstrap: listPlans failed; plans omitted from payload", {
1271
+ error: plansResult.error,
1272
+ status: plansResult.status
1273
+ });
1274
+ }
1236
1275
  const plans = isErrorResult(plansResult) ? [] : plansResult.plans;
1237
1276
  const purchase = okOrNull(resolvedPurchaseResult);
1238
1277
  const enrichedPurchase = purchase ? {
@@ -1280,7 +1319,8 @@ function createBuildBootstrapPayload(options) {
1280
1319
  plans,
1281
1320
  customer,
1282
1321
  checkoutUrl: checkout?.checkoutUrl ?? null,
1283
- portalUrl: portal?.customerUrl ?? null
1322
+ portalUrl: portal?.customerUrl ?? null,
1323
+ autoRechargeUrl: autoRechargeUrlFrom(portal?.customerUrl)
1284
1324
  };
1285
1325
  };
1286
1326
  }
@@ -1358,7 +1398,13 @@ var VIEWER_DESCRIPTION = 'Call when the user says "upgrade", "change plan", "buy
1358
1398
  var INTENT_MODE_SCHEMA = z3.enum(["ui", "text", "auto"]).optional().describe(
1359
1399
  "Default `mode: 'auto'` returns a self-sufficient text summary (plan, price, https checkout URL) and still opens the iframe on UI hosts. Pass `mode: 'text'` to strip the iframe, or `mode: 'ui'` for a one-line placeholder that still includes the checkout URL."
1360
1400
  );
1361
- var DEFAULT_VIEWS = [...SOLVAPAY_MCP_VIEW_KINDS];
1401
+ var DEFAULT_VIEWS = [...SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS];
1402
+ function readAutoRechargeArg(value) {
1403
+ if (value == null || typeof value !== "object" || Array.isArray(value)) {
1404
+ return void 0;
1405
+ }
1406
+ return value;
1407
+ }
1362
1408
  function buildSolvaPayDescriptors(options) {
1363
1409
  const {
1364
1410
  solvaPay,
@@ -1452,9 +1498,17 @@ function buildSolvaPayDescriptors(options) {
1452
1498
  publicBaseUrl,
1453
1499
  getCustomerRef
1454
1500
  });
1455
- const enabledViewList = SOLVAPAY_MCP_VIEW_KINDS.filter((view) => enabledViews.has(view));
1456
- if (enabledViewList.length > 0) {
1457
- const viewEnum = z3.enum(enabledViewList).optional().describe(VIEW_PARAM_DESCRIPTION);
1501
+ const advertisedViewList = SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS.filter(
1502
+ (view) => enabledViews.has(view)
1503
+ );
1504
+ if (advertisedViewList.length > 0) {
1505
+ const advertisedEnum = z3.enum(
1506
+ advertisedViewList
1507
+ );
1508
+ const viewEnum = z3.preprocess(
1509
+ (value) => value === "auto-recharge" ? "account" : value,
1510
+ advertisedEnum
1511
+ ).optional().describe(VIEW_PARAM_DESCRIPTION);
1458
1512
  pushTool({
1459
1513
  name: VIEWER_TOOL_NAME,
1460
1514
  title: "Account",
@@ -1465,11 +1519,11 @@ function buildSolvaPayDescriptors(options) {
1465
1519
  annotations: VIEWER_ANNOTATIONS,
1466
1520
  handler: async (args, extra) => trace(VIEWER_TOOL_NAME, args, extra, async () => {
1467
1521
  const requested = args.view === "checkout" || args.view === "account" || args.view === "topup" || args.view === "auto-recharge" ? args.view : void 0;
1468
- if (requested !== void 0 && !enabledViews.has(requested)) {
1522
+ if (requested !== void 0 && requested !== "auto-recharge" && !enabledViews.has(requested)) {
1469
1523
  return toolErrorResult({
1470
1524
  error: `view '${requested}' is not enabled on this server`,
1471
1525
  status: 400,
1472
- details: `Enabled views: ${enabledViewList.join(", ")}. Pass one of those, or omit view to let the server pick.`
1526
+ details: `Enabled views: ${advertisedViewList.join(", ")}. Pass one of those, or omit view to let the server pick.`
1473
1527
  });
1474
1528
  }
1475
1529
  const mode = parseMode(args.mode);
@@ -1524,14 +1578,22 @@ function buildSolvaPayDescriptors(options) {
1524
1578
  });
1525
1579
  pushTool({
1526
1580
  name: MCP_TOOL_NAMES.createPayment,
1527
- description: UI_ONLY_PREFIX + 'Create a Stripe payment intent for the authenticated customer. Pass purpose: "plan" to purchase a plan (returns { clientSecret, publishableKey, accountId?, customerRef }) or purpose: "topup" for a credit top-up (credits are recorded by webhook after confirmation).',
1581
+ description: UI_ONLY_PREFIX + 'Create a Stripe payment intent for the authenticated customer. Pass purpose: "plan" to purchase a plan (returns { clientSecret, publishableKey, accountId?, customerRef }) or purpose: "topup" for a credit top-up (credits are recorded by webhook after confirmation). A topup may carry autoRecharge so the card entered for the top-up is saved as the auto-recharge funding source.',
1528
1582
  inputSchema: {
1529
1583
  purpose: z3.enum(["plan", "topup"]).describe('"plan" for plan checkout; "topup" for credit top-up.'),
1530
1584
  planRef: z3.string().optional(),
1531
1585
  productRef: z3.string().optional(),
1532
1586
  currency: z3.string().optional(),
1533
1587
  amount: z3.number().int().positive().optional(),
1534
- description: z3.string().optional()
1588
+ description: z3.string().optional(),
1589
+ autoRecharge: z3.object({
1590
+ enabled: z3.boolean(),
1591
+ triggerType: z3.literal("balance"),
1592
+ thresholdAmountMajor: z3.number().positive().optional(),
1593
+ topupAmountMajor: z3.number().positive().optional(),
1594
+ maxMonthlySpendMajor: z3.number().positive().optional(),
1595
+ currency: z3.string().length(3)
1596
+ }).optional()
1535
1597
  },
1536
1598
  meta: uiToolMeta,
1537
1599
  annotations: solvapayTool({ readOnlyHint: false, destructiveHint: false }),
@@ -1546,6 +1608,14 @@ function buildSolvaPayDescriptors(options) {
1546
1608
  details: 'Pass purpose: "plan" or purpose: "topup".'
1547
1609
  });
1548
1610
  }
1611
+ const autoRecharge = readAutoRechargeArg(args.autoRecharge);
1612
+ if (purpose === "plan" && autoRecharge) {
1613
+ return toolErrorResult({
1614
+ error: "create_payment_intent plan does not accept autoRecharge",
1615
+ status: 400,
1616
+ details: 'autoRecharge is only honoured on purpose: "topup".'
1617
+ });
1618
+ }
1549
1619
  if (purpose === "topup") {
1550
1620
  const amount = typeof args.amount === "number" ? args.amount : 0;
1551
1621
  const currency2 = typeof args.currency === "string" ? args.currency : "";
@@ -1559,7 +1629,7 @@ function buildSolvaPayDescriptors(options) {
1559
1629
  }
1560
1630
  const result2 = await createTopupPaymentIntentCore(
1561
1631
  buildRequest(extra, { method: "POST" }),
1562
- { amount, currency: currency2, description },
1632
+ { amount, currency: currency2, description, ...autoRecharge ? { autoRecharge } : {} },
1563
1633
  { solvaPay }
1564
1634
  );
1565
1635
  if (isErrorResult2(result2)) return toolErrorResult(result2);
@@ -2207,9 +2277,11 @@ export {
2207
2277
  McpBearerAuthError,
2208
2278
  NARRATORS,
2209
2279
  OPEN_TOOL_FOR_VIEW,
2280
+ PORTAL_AUTO_RECHARGE_QUERY,
2210
2281
  SOLVAPAY_BOOTSTRAP_MIME_TYPE,
2211
2282
  SOLVAPAY_BOOTSTRAP_URI,
2212
2283
  SOLVAPAY_DEFAULT_CSP,
2284
+ SOLVAPAY_MCP_ADVERTISED_VIEW_KINDS,
2213
2285
  SOLVAPAY_MCP_VIEW_KINDS,
2214
2286
  SOLVAPAY_OVERVIEW_MARKDOWN,
2215
2287
  SOLVAPAY_OVERVIEW_MIME_TYPE,
@@ -2222,6 +2294,7 @@ export {
2222
2294
  VIEW_FOR_TOOL,
2223
2295
  applyHideToolsByAudience,
2224
2296
  assertValidProductRef2 as assertValidProductRef,
2297
+ autoRechargeUrlFrom,
2225
2298
  balanceSummary,
2226
2299
  buildAuthInfoFromBearer,
2227
2300
  buildPayableHandler,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solvapay/mcp-core",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Framework-neutral MCP contracts for the SolvaPay SDK (tool names, descriptors, payable handler, paywall meta, CSP, bootstrap payload, OAuth discovery JSON builders, bearer/JWT helpers).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -41,7 +41,7 @@
41
41
  "typescript": "^5.9.3",
42
42
  "vitest": "^4.1.2",
43
43
  "zod": "^4.3.6",
44
- "@solvapay/server": "2.6.0",
44
+ "@solvapay/server": "2.7.0",
45
45
  "@solvapay/test-utils": "^0.0.0"
46
46
  },
47
47
  "scripts": {