@solvapay/server 1.0.1-preview.2 → 1.0.1-preview.4

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
@@ -4314,9 +4314,11 @@ __export(index_exports, {
4314
4314
  McpBearerAuthError: () => McpBearerAuthError,
4315
4315
  PaywallError: () => PaywallError,
4316
4316
  VIRTUAL_TOOL_DEFINITIONS: () => VIRTUAL_TOOL_DEFINITIONS,
4317
+ buildAuthInfoFromBearer: () => buildAuthInfoFromBearer,
4317
4318
  cancelPurchaseCore: () => cancelPurchaseCore,
4318
4319
  createCheckoutSessionCore: () => createCheckoutSessionCore,
4319
4320
  createCustomerSessionCore: () => createCustomerSessionCore,
4321
+ createMcpOAuthBridge: () => createMcpOAuthBridge,
4320
4322
  createPaymentIntentCore: () => createPaymentIntentCore,
4321
4323
  createSolvaPay: () => createSolvaPay,
4322
4324
  createSolvaPayClient: () => createSolvaPayClient,
@@ -4326,10 +4328,15 @@ __export(index_exports, {
4326
4328
  getAuthenticatedUserCore: () => getAuthenticatedUserCore,
4327
4329
  getCustomerRefFromBearerAuthHeader: () => getCustomerRefFromBearerAuthHeader,
4328
4330
  getCustomerRefFromJwtPayload: () => getCustomerRefFromJwtPayload,
4331
+ getOAuthAuthorizationServerResponse: () => getOAuthAuthorizationServerResponse,
4332
+ getOAuthProtectedResourceResponse: () => getOAuthProtectedResourceResponse,
4329
4333
  handleRouteError: () => handleRouteError,
4330
4334
  isErrorResult: () => isErrorResult,
4335
+ jsonSchemaToZodRawShape: () => jsonSchemaToZodRawShape,
4331
4336
  listPlansCore: () => listPlansCore,
4337
+ paywallErrorToClientPayload: () => paywallErrorToClientPayload,
4332
4338
  processPaymentIntentCore: () => processPaymentIntentCore,
4339
+ registerVirtualToolsMcpImpl: () => registerVirtualToolsMcpImpl,
4333
4340
  syncCustomerCore: () => syncCustomerCore,
4334
4341
  verifyWebhook: () => verifyWebhook,
4335
4342
  withRetry: () => withRetry
@@ -4497,6 +4504,21 @@ function createSolvaPayClient(opts) {
4497
4504
  }
4498
4505
  return await res.json();
4499
4506
  },
4507
+ // PUT: /v1/sdk/products/{productRef}/mcp/plans
4508
+ async configureMcpPlans(productRef, params) {
4509
+ const url = `${base}/v1/sdk/products/${productRef}/mcp/plans`;
4510
+ const res = await fetch(url, {
4511
+ method: "PUT",
4512
+ headers,
4513
+ body: JSON.stringify(params)
4514
+ });
4515
+ if (!res.ok) {
4516
+ const error = await res.text();
4517
+ log(`\u274C API Error: ${res.status} - ${error}`);
4518
+ throw new import_core.SolvaPayError(`Configure MCP plans failed (${res.status}): ${error}`);
4519
+ }
4520
+ return await res.json();
4521
+ },
4500
4522
  // DELETE: /v1/sdk/products/{productRef}
4501
4523
  async deleteProduct(productRef) {
4502
4524
  const url = `${base}/v1/sdk/products/${productRef}`;
@@ -4895,6 +4917,24 @@ var PaywallError = class extends Error {
4895
4917
  this.name = "PaywallError";
4896
4918
  }
4897
4919
  };
4920
+ function paywallErrorToClientPayload(error) {
4921
+ const sc = error.structuredContent;
4922
+ const base = {
4923
+ success: false,
4924
+ error: sc.kind === "activation_required" ? "Activation required" : "Payment required",
4925
+ product: sc.product,
4926
+ checkoutUrl: sc.checkoutUrl,
4927
+ message: sc.message
4928
+ };
4929
+ if (sc.kind === "activation_required") {
4930
+ base.kind = "activation_required";
4931
+ if (sc.plans !== void 0) base.plans = sc.plans;
4932
+ if (sc.balance !== void 0) base.balance = sc.balance;
4933
+ if (sc.productDetails !== void 0) base.productDetails = sc.productDetails;
4934
+ if (sc.confirmationUrl !== void 0) base.confirmationUrl = sc.confirmationUrl;
4935
+ }
4936
+ return base;
4937
+ }
4898
4938
  var sharedCustomerLookupDeduplicator = createRequestDeduplicator({
4899
4939
  cacheTTL: 6e4,
4900
4940
  // Cache results for 60 seconds (reduces API calls significantly)
@@ -4954,6 +4994,7 @@ var SolvaPayPaywall = class {
4954
4994
  let withinLimits;
4955
4995
  let remaining;
4956
4996
  let checkoutUrl;
4997
+ let lastLimitsCheck;
4957
4998
  const hasFreshCachedLimits = cachedLimits && now - cachedLimits.timestamp < this.limitsCacheTTL;
4958
4999
  if (hasFreshCachedLimits) {
4959
5000
  checkoutUrl = cachedLimits.checkoutUrl;
@@ -4980,6 +5021,7 @@ var SolvaPayPaywall = class {
4980
5021
  ...configuredPlanRef ? { planRef: configuredPlanRef } : {},
4981
5022
  meterName: usageType
4982
5023
  });
5024
+ lastLimitsCheck = limitsCheck;
4983
5025
  withinLimits = limitsCheck.withinLimits;
4984
5026
  remaining = limitsCheck.remaining;
4985
5027
  checkoutUrl = limitsCheck.checkoutUrl;
@@ -5008,6 +5050,20 @@ var SolvaPayPaywall = class {
5008
5050
  requestId,
5009
5051
  latencyMs2
5010
5052
  );
5053
+ if (lastLimitsCheck?.activationRequired) {
5054
+ const confirmationUrl = lastLimitsCheck.confirmationUrl;
5055
+ const payCheckoutUrl = confirmationUrl || lastLimitsCheck.checkoutUrl || checkoutUrl || "";
5056
+ throw new PaywallError("Activation required", {
5057
+ kind: "activation_required",
5058
+ product,
5059
+ message: "Product activation is required before this tool can be used.",
5060
+ checkoutUrl: payCheckoutUrl,
5061
+ confirmationUrl,
5062
+ plans: lastLimitsCheck.plans,
5063
+ balance: lastLimitsCheck.balance,
5064
+ productDetails: lastLimitsCheck.product
5065
+ });
5066
+ }
5011
5067
  throw new PaywallError("Payment required", {
5012
5068
  kind: "payment_required",
5013
5069
  product,
@@ -5110,7 +5166,8 @@ var SolvaPayPaywall = class {
5110
5166
  this.customerCreationAttempts.add(customerRef);
5111
5167
  try {
5112
5168
  const createParams = {
5113
- email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`
5169
+ email: options?.email || `${customerRef}-${Date.now()}@auto-created.local`,
5170
+ metadata: {}
5114
5171
  };
5115
5172
  if (options?.name) {
5116
5173
  createParams.name = options.name;
@@ -5134,7 +5191,10 @@ var SolvaPayPaywall = class {
5134
5191
  return searchResult.customerRef;
5135
5192
  }
5136
5193
  } catch (lookupError) {
5137
- this.log(`\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`, lookupError instanceof Error ? lookupError.message : lookupError);
5194
+ this.log(
5195
+ `\u26A0\uFE0F Failed to lookup existing customer by externalRef after 409:`,
5196
+ lookupError instanceof Error ? lookupError.message : lookupError
5197
+ );
5138
5198
  }
5139
5199
  }
5140
5200
  const isEmailConflict = errorMessage.includes("email") || errorMessage.includes("identifier email");
@@ -5157,7 +5217,8 @@ var SolvaPayPaywall = class {
5157
5217
  try {
5158
5218
  const retryParams = {
5159
5219
  email: `${customerRef}-${Date.now()}@auto-created.local`,
5160
- externalRef
5220
+ externalRef,
5221
+ metadata: {}
5161
5222
  };
5162
5223
  if (options?.name) {
5163
5224
  retryParams.name = options.name;
@@ -5254,10 +5315,10 @@ async function createAdapterHandler(adapter, paywall, metadata, businessLogic) {
5254
5315
  const backendRefCache = /* @__PURE__ */ new Map();
5255
5316
  const getCustomerRef = (args) => args.auth?.customer_ref || "anonymous";
5256
5317
  const protectedHandler = await paywall.protect(businessLogic, metadata, getCustomerRef);
5257
- return async (context) => {
5318
+ return async (context, extra) => {
5258
5319
  try {
5259
5320
  const args = await adapter.extractArgs(context);
5260
- const customerRef = await adapter.getCustomerRef(context);
5321
+ const customerRef = await adapter.getCustomerRef(context, extra);
5261
5322
  let backendRef = backendRefCache.get(customerRef);
5262
5323
  if (!backendRef) {
5263
5324
  backendRef = await paywall.ensureCustomer(customerRef, customerRef);
@@ -5448,18 +5509,18 @@ var McpAdapter = class {
5448
5509
  extractArgs(args) {
5449
5510
  return args;
5450
5511
  }
5451
- async getCustomerRef(args) {
5512
+ async getCustomerRef(args, extra) {
5452
5513
  if (this.options.getCustomerRef) {
5453
- const ref = await this.options.getCustomerRef(args);
5514
+ const ref = await this.options.getCustomerRef(args, extra);
5454
5515
  return AdapterUtils.ensureCustomerRef(ref);
5455
5516
  }
5456
- const auth = args?.auth;
5457
- const customerRef = auth?.customer_ref || "anonymous";
5517
+ const customerRefFromExtra = extra?.authInfo?.extra?.customer_ref;
5518
+ const customerRef = typeof customerRefFromExtra === "string" && customerRefFromExtra.trim() ? customerRefFromExtra.trim() : "anonymous";
5458
5519
  return AdapterUtils.ensureCustomerRef(customerRef);
5459
5520
  }
5460
5521
  formatResponse(result, _context) {
5461
5522
  const transformed = this.options.transformResponse ? this.options.transformResponse(result) : result;
5462
- return {
5523
+ const response = {
5463
5524
  content: [
5464
5525
  {
5465
5526
  type: "text",
@@ -5467,6 +5528,10 @@ var McpAdapter = class {
5467
5528
  }
5468
5529
  ]
5469
5530
  };
5531
+ if (transformed && typeof transformed === "object" && !Array.isArray(transformed)) {
5532
+ response.structuredContent = transformed;
5533
+ }
5534
+ return response;
5470
5535
  }
5471
5536
  formatError(error, _context) {
5472
5537
  if (error instanceof PaywallError) {
@@ -5474,17 +5539,7 @@ var McpAdapter = class {
5474
5539
  content: [
5475
5540
  {
5476
5541
  type: "text",
5477
- text: JSON.stringify(
5478
- {
5479
- success: false,
5480
- error: "Payment required",
5481
- product: error.structuredContent.product,
5482
- checkoutUrl: error.structuredContent.checkoutUrl,
5483
- message: error.structuredContent.message
5484
- },
5485
- null,
5486
- 2
5487
- )
5542
+ text: JSON.stringify(paywallErrorToClientPayload(error), null, 2)
5488
5543
  }
5489
5544
  ],
5490
5545
  isError: true,
@@ -5554,8 +5609,8 @@ function mcpErrorResult(message2) {
5554
5609
  return { content: [{ type: "text", text: JSON.stringify({ error: message2 }) }], isError: true };
5555
5610
  }
5556
5611
  function createGetUserInfoHandler(apiClient, productRef, getCustomerRef) {
5557
- return async (args) => {
5558
- const customerRef = getCustomerRef(args);
5612
+ return async (args, extra) => {
5613
+ const customerRef = getCustomerRef(args, extra);
5559
5614
  try {
5560
5615
  if (!apiClient.getUserInfo) {
5561
5616
  return mcpErrorResult("getUserInfo is not available on this API client");
@@ -5570,12 +5625,12 @@ function createGetUserInfoHandler(apiClient, productRef, getCustomerRef) {
5570
5625
  };
5571
5626
  }
5572
5627
  function createUpgradeHandler(apiClient, productRef, getCustomerRef) {
5573
- return async (args) => {
5574
- const customerRef = getCustomerRef(args);
5628
+ return async (args, extra) => {
5629
+ const customerRef = getCustomerRef(args, extra);
5575
5630
  const planRef = typeof args.planRef === "string" ? args.planRef : void 0;
5576
5631
  try {
5577
5632
  const result = await apiClient.createCheckoutSession({
5578
- customerReference: customerRef,
5633
+ customerRef,
5579
5634
  productRef,
5580
5635
  ...planRef && { planRef }
5581
5636
  });
@@ -5602,8 +5657,8 @@ You'll be able to compare options and select the one that's right for you.`;
5602
5657
  };
5603
5658
  }
5604
5659
  function createManageAccountHandler(apiClient, productRef, getCustomerRef) {
5605
- return async (args) => {
5606
- const customerRef = getCustomerRef(args);
5660
+ return async (args, extra) => {
5661
+ const customerRef = getCustomerRef(args, extra);
5607
5662
  try {
5608
5663
  const session = await apiClient.createCustomerSession({ customerRef, productRef });
5609
5664
  const portalUrl = session.customerUrl;
@@ -5646,6 +5701,98 @@ function createVirtualTools(apiClient, options) {
5646
5701
  return allTools.filter((t) => !excludeSet.has(t.name));
5647
5702
  }
5648
5703
 
5704
+ // src/register-virtual-tools-mcp.ts
5705
+ var import_node_module = require("module");
5706
+ function getNodeRequire() {
5707
+ try {
5708
+ return (0, eval)("require");
5709
+ } catch {
5710
+ return (0, import_node_module.createRequire)(`${process.cwd()}/package.json`);
5711
+ }
5712
+ }
5713
+ var nodeRequire = getNodeRequire();
5714
+ function getZod() {
5715
+ try {
5716
+ const zodModule = nodeRequire("zod");
5717
+ return zodModule.z ?? zodModule;
5718
+ } catch {
5719
+ throw new Error(
5720
+ "zod is required to use registerVirtualToolsMcp(). Install it as a dependency in your MCP server project."
5721
+ );
5722
+ }
5723
+ }
5724
+ function isJsonSchemaObject(value) {
5725
+ return Boolean(value && typeof value === "object");
5726
+ }
5727
+ function toZodSchema(property) {
5728
+ const z = getZod();
5729
+ if (Array.isArray(property.enum) && property.enum.length > 0) {
5730
+ if (property.enum.every((value) => typeof value === "string")) {
5731
+ const [first, ...rest] = property.enum;
5732
+ return z.enum([first, ...rest]);
5733
+ }
5734
+ if (property.enum.length === 1) {
5735
+ return z.literal(property.enum[0]);
5736
+ }
5737
+ return z.union(property.enum.map((value) => z.literal(value)));
5738
+ }
5739
+ switch (property.type) {
5740
+ case "string":
5741
+ return z.string();
5742
+ case "number":
5743
+ return z.number();
5744
+ case "integer":
5745
+ return z.number().int();
5746
+ case "boolean":
5747
+ return z.boolean();
5748
+ case "array":
5749
+ return z.array(
5750
+ isJsonSchemaObject(property.items) ? toZodSchema(property.items) : z.any()
5751
+ );
5752
+ case "object":
5753
+ return z.object(jsonSchemaToZodRawShape(property.properties || {}));
5754
+ default:
5755
+ return z.any();
5756
+ }
5757
+ }
5758
+ function jsonSchemaToZodRawShape(properties, required = []) {
5759
+ const requiredSet = new Set(required);
5760
+ return Object.fromEntries(
5761
+ Object.entries(properties).map(([name, property]) => {
5762
+ const schema = toZodSchema(property);
5763
+ const finalSchema = requiredSet.has(name) ? schema : schema.optional();
5764
+ return [name, finalSchema];
5765
+ })
5766
+ );
5767
+ }
5768
+ function defaultGetCustomerRef(_args, extra) {
5769
+ const fromExtra = extra?.authInfo?.extra?.customer_ref;
5770
+ return typeof fromExtra === "string" && fromExtra.trim() ? fromExtra.trim() : "anonymous";
5771
+ }
5772
+ function registerVirtualToolsMcpImpl(server, apiClient, options) {
5773
+ const { filter, mapDefinition, wrapHandler, getCustomerRef, ...virtualToolsOptions } = options;
5774
+ const virtualTools = createVirtualTools(apiClient, {
5775
+ ...virtualToolsOptions,
5776
+ getCustomerRef: getCustomerRef || defaultGetCustomerRef
5777
+ });
5778
+ for (const toolDefinition of virtualTools) {
5779
+ if (filter && !filter(toolDefinition)) continue;
5780
+ const mappedDefinition = mapDefinition ? mapDefinition(toolDefinition) : toolDefinition;
5781
+ const wrappedHandler = wrapHandler ? wrapHandler(mappedDefinition.handler, mappedDefinition) : mappedDefinition.handler;
5782
+ server.registerTool(
5783
+ mappedDefinition.name,
5784
+ {
5785
+ description: mappedDefinition.description,
5786
+ inputSchema: jsonSchemaToZodRawShape(
5787
+ mappedDefinition.inputSchema.properties,
5788
+ mappedDefinition.inputSchema.required || []
5789
+ )
5790
+ },
5791
+ wrappedHandler
5792
+ );
5793
+ }
5794
+ }
5795
+
5649
5796
  // src/factory.ts
5650
5797
  function createSolvaPay(config) {
5651
5798
  let resolvedConfig;
@@ -5695,14 +5842,14 @@ function createSolvaPay(config) {
5695
5842
  if (!apiClient.createCustomer) {
5696
5843
  throw new import_core2.SolvaPayError("createCustomer is not available on this API client");
5697
5844
  }
5698
- return apiClient.createCustomer(params);
5845
+ return apiClient.createCustomer({ ...params, metadata: params.metadata ?? {} });
5699
5846
  },
5700
5847
  getCustomer(params) {
5701
5848
  return apiClient.getCustomer(params);
5702
5849
  },
5703
5850
  createCheckoutSession(params) {
5704
5851
  return apiClient.createCheckoutSession({
5705
- customerReference: params.customerRef,
5852
+ customerRef: params.customerRef,
5706
5853
  productRef: params.productRef,
5707
5854
  planRef: params.planRef
5708
5855
  });
@@ -5716,9 +5863,18 @@ function createSolvaPay(config) {
5716
5863
  }
5717
5864
  return apiClient.bootstrapMcpProduct(params);
5718
5865
  },
5866
+ configureMcpPlans(productRef, params) {
5867
+ if (!apiClient.configureMcpPlans) {
5868
+ throw new import_core2.SolvaPayError("configureMcpPlans is not available on this API client");
5869
+ }
5870
+ return apiClient.configureMcpPlans(productRef, params);
5871
+ },
5719
5872
  getVirtualTools(options) {
5720
5873
  return createVirtualTools(apiClient, options);
5721
5874
  },
5875
+ async registerVirtualToolsMcp(server, options) {
5876
+ await registerVirtualToolsMcpImpl(server, apiClient, options);
5877
+ },
5722
5878
  // Payable API for framework-specific handlers
5723
5879
  payable(options = {}) {
5724
5880
  const product = options.productRef || options.product || process.env.SOLVAPAY_PRODUCT || "default-product";
@@ -5757,9 +5913,9 @@ function createSolvaPay(config) {
5757
5913
  getCustomerRef: adapterOptions?.getCustomerRef || options.getCustomerRef
5758
5914
  });
5759
5915
  const handlerPromise = createAdapterHandler(adapter, paywall, metadata, businessLogic);
5760
- return async (args) => {
5916
+ return async (args, extra) => {
5761
5917
  const handler = await handlerPromise;
5762
- return handler(args);
5918
+ return handler(args, extra);
5763
5919
  };
5764
5920
  },
5765
5921
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -5829,6 +5985,167 @@ function getCustomerRefFromBearerAuthHeader(authorization, options = {}) {
5829
5985
  return getCustomerRefFromJwtPayload(payload, options);
5830
5986
  }
5831
5987
 
5988
+ // src/mcp/auth-bridge.ts
5989
+ function getClientId(payload, explicitClientId) {
5990
+ if (explicitClientId) return explicitClientId;
5991
+ const payloadClientId = typeof payload.client_id === "string" && payload.client_id || typeof payload.azp === "string" && payload.azp || typeof payload.aud === "string" && payload.aud || null;
5992
+ return payloadClientId || "solvapay-mcp-client";
5993
+ }
5994
+ function getScopes(payload, defaultScopes) {
5995
+ if (Array.isArray(payload.scp)) {
5996
+ return payload.scp.filter((scope) => typeof scope === "string");
5997
+ }
5998
+ if (typeof payload.scope === "string" && payload.scope.trim()) {
5999
+ return payload.scope.split(/\s+/).map((scope) => scope.trim()).filter(Boolean);
6000
+ }
6001
+ return defaultScopes;
6002
+ }
6003
+ function getExpiresAt(payload) {
6004
+ return typeof payload.exp === "number" ? payload.exp : void 0;
6005
+ }
6006
+ function buildAuthInfoFromBearer(authorization, options = {}) {
6007
+ const token = extractBearerToken(authorization);
6008
+ if (!token) return null;
6009
+ const payload = decodeJwtPayload(token);
6010
+ const customerRef = getCustomerRefFromJwtPayload(payload, options);
6011
+ const clientId = getClientId(payload, options.clientId);
6012
+ const scopes = getScopes(payload, options.defaultScopes || []);
6013
+ const expiresAt = getExpiresAt(payload);
6014
+ return {
6015
+ token,
6016
+ clientId,
6017
+ scopes,
6018
+ expiresAt,
6019
+ extra: {
6020
+ customer_ref: customerRef,
6021
+ ...options.includePayload ? { payload } : {}
6022
+ }
6023
+ };
6024
+ }
6025
+
6026
+ // src/mcp/oauth-bridge.ts
6027
+ function withoutTrailingSlash(value) {
6028
+ return value.replace(/\/$/, "");
6029
+ }
6030
+ function getRequestAuthHeader(req) {
6031
+ const header = req.headers?.authorization;
6032
+ if (typeof header === "string") return header;
6033
+ if (Array.isArray(header)) return header[0] || null;
6034
+ return null;
6035
+ }
6036
+ function getRequestJsonRpcId(body) {
6037
+ if (body && typeof body === "object" && "id" in body) {
6038
+ const id = body.id;
6039
+ return id ?? null;
6040
+ }
6041
+ return null;
6042
+ }
6043
+ function makeUnauthorizedJsonRpc(id) {
6044
+ return {
6045
+ jsonrpc: "2.0",
6046
+ id,
6047
+ error: {
6048
+ code: -32001,
6049
+ message: "Unauthorized"
6050
+ }
6051
+ };
6052
+ }
6053
+ function setMcpChallengeHeader(res, publicBaseUrl, protectedResourcePath) {
6054
+ res.setHeader(
6055
+ "WWW-Authenticate",
6056
+ `Bearer resource_metadata="${withoutTrailingSlash(publicBaseUrl)}${protectedResourcePath}"`
6057
+ );
6058
+ }
6059
+ function getOAuthProtectedResourceResponse(publicBaseUrl) {
6060
+ const resource = withoutTrailingSlash(publicBaseUrl);
6061
+ return {
6062
+ resource,
6063
+ authorization_servers: [resource],
6064
+ scopes_supported: ["openid", "profile", "email"]
6065
+ };
6066
+ }
6067
+ function getOAuthAuthorizationServerResponse({
6068
+ apiBaseUrl,
6069
+ productRef
6070
+ }) {
6071
+ const normalizedApiBaseUrl = withoutTrailingSlash(apiBaseUrl);
6072
+ const registrationEndpoint = `${normalizedApiBaseUrl}/v1/customer/auth/register?product_ref=${encodeURIComponent(productRef)}`;
6073
+ return {
6074
+ issuer: normalizedApiBaseUrl,
6075
+ authorization_endpoint: `${normalizedApiBaseUrl}/v1/customer/auth/authorize`,
6076
+ token_endpoint: `${normalizedApiBaseUrl}/v1/customer/auth/token`,
6077
+ registration_endpoint: registrationEndpoint,
6078
+ token_endpoint_auth_methods_supported: ["client_secret_basic", "client_secret_post"],
6079
+ response_types_supported: ["code"],
6080
+ grant_types_supported: ["authorization_code", "refresh_token"],
6081
+ scopes_supported: ["openid", "profile", "email"],
6082
+ code_challenge_methods_supported: ["S256"]
6083
+ };
6084
+ }
6085
+ function createMcpOAuthBridge(options) {
6086
+ const {
6087
+ publicBaseUrl,
6088
+ apiBaseUrl,
6089
+ productRef,
6090
+ mcpPath = "/mcp",
6091
+ requireAuth = true,
6092
+ authInfo,
6093
+ protectedResourcePath = "/.well-known/oauth-protected-resource",
6094
+ authorizationServerPath = "/.well-known/oauth-authorization-server"
6095
+ } = options;
6096
+ const protectedResourceMiddleware = (req, res, next) => {
6097
+ if (req.method !== "GET" || req.path !== protectedResourcePath) {
6098
+ next();
6099
+ return;
6100
+ }
6101
+ res.json(getOAuthProtectedResourceResponse(publicBaseUrl));
6102
+ };
6103
+ const authorizationServerMiddleware = (req, res, next) => {
6104
+ if (req.method !== "GET" || req.path !== authorizationServerPath) {
6105
+ next();
6106
+ return;
6107
+ }
6108
+ if (!productRef) {
6109
+ res.status(500).json({ error: "SOLVAPAY_PRODUCT_REF missing" });
6110
+ return;
6111
+ }
6112
+ res.json(
6113
+ getOAuthAuthorizationServerResponse({
6114
+ apiBaseUrl,
6115
+ productRef
6116
+ })
6117
+ );
6118
+ };
6119
+ const mcpAuthMiddleware = (req, res, next) => {
6120
+ if (req.path !== mcpPath) {
6121
+ next();
6122
+ return;
6123
+ }
6124
+ const authHeader = getRequestAuthHeader(req);
6125
+ const id = getRequestJsonRpcId(req.body);
6126
+ if (!authHeader && !requireAuth) {
6127
+ next();
6128
+ return;
6129
+ }
6130
+ try {
6131
+ const auth = buildAuthInfoFromBearer(authHeader, authInfo);
6132
+ if (!auth) {
6133
+ throw new McpBearerAuthError("Missing bearer token");
6134
+ }
6135
+ req.auth = auth;
6136
+ next();
6137
+ } catch {
6138
+ setMcpChallengeHeader(res, publicBaseUrl, protectedResourcePath);
6139
+ if (req.method === "POST") {
6140
+ res.status(401).json(makeUnauthorizedJsonRpc(id));
6141
+ return;
6142
+ }
6143
+ res.status(401).json({ error: "Unauthorized" });
6144
+ }
6145
+ };
6146
+ return [protectedResourceMiddleware, authorizationServerMiddleware, mcpAuthMiddleware];
6147
+ }
6148
+
5832
6149
  // src/helpers/error.ts
5833
6150
  var import_core3 = require("@solvapay/core");
5834
6151
  function isErrorResult(result) {
@@ -6183,9 +6500,11 @@ function verifyWebhook({
6183
6500
  McpBearerAuthError,
6184
6501
  PaywallError,
6185
6502
  VIRTUAL_TOOL_DEFINITIONS,
6503
+ buildAuthInfoFromBearer,
6186
6504
  cancelPurchaseCore,
6187
6505
  createCheckoutSessionCore,
6188
6506
  createCustomerSessionCore,
6507
+ createMcpOAuthBridge,
6189
6508
  createPaymentIntentCore,
6190
6509
  createSolvaPay,
6191
6510
  createSolvaPayClient,
@@ -6195,10 +6514,15 @@ function verifyWebhook({
6195
6514
  getAuthenticatedUserCore,
6196
6515
  getCustomerRefFromBearerAuthHeader,
6197
6516
  getCustomerRefFromJwtPayload,
6517
+ getOAuthAuthorizationServerResponse,
6518
+ getOAuthProtectedResourceResponse,
6198
6519
  handleRouteError,
6199
6520
  isErrorResult,
6521
+ jsonSchemaToZodRawShape,
6200
6522
  listPlansCore,
6523
+ paywallErrorToClientPayload,
6201
6524
  processPaymentIntentCore,
6525
+ registerVirtualToolsMcpImpl,
6202
6526
  syncCustomerCore,
6203
6527
  verifyWebhook,
6204
6528
  withRetry