@payments-ai/mcp 1.1.2 → 1.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.
Files changed (3) hide show
  1. package/dist/main.http.js +1761 -0
  2. package/dist/main.js +219 -104
  3. package/package.json +14 -6
package/dist/main.js CHANGED
@@ -47,7 +47,8 @@ var require_tool_names = __commonJS({
47
47
  GET_MERCHANT_ACTIVATION_STATUS: "get_merchant_activation_status",
48
48
  SEARCH_PAYMENTS_AI_DOCS: "search_payments_ai_docs",
49
49
  CREATE_PRODUCT: "create_product",
50
- UPLOAD_PRODUCT_IMAGE: "upload_product_image",
50
+ GET_PRODUCT_IMAGE_UPLOAD_URL: "get_product_image_upload_url",
51
+ CONFIRM_PRODUCT_IMAGE: "confirm_product_image",
51
52
  GET_CHECKOUT_CUSTOMIZATION: "get_checkout_customization",
52
53
  UPDATE_CHECKOUT_CUSTOMIZATION: "update_checkout_customization"
53
54
  };
@@ -103,43 +104,49 @@ var require_create_product = __commonJS({
103
104
  }
104
105
  });
105
106
 
106
- // ../../packages/mcp-contracts/dist/upload-product-image.js
107
- var require_upload_product_image = __commonJS({
108
- "../../packages/mcp-contracts/dist/upload-product-image.js"(exports) {
107
+ // ../../packages/mcp-contracts/dist/product-image-presigned-upload.js
108
+ var require_product_image_presigned_upload = __commonJS({
109
+ "../../packages/mcp-contracts/dist/product-image-presigned-upload.js"(exports) {
109
110
  "use strict";
110
111
  Object.defineProperty(exports, "__esModule", { value: true });
111
- exports.PRODUCT_IMAGE_MIME_TYPES = exports.MAX_PRODUCT_IMAGE_BYTES = exports.uploadMcpProductImageOutputSchema = exports.uploadMcpProductImageInputSchema = exports.uploadMcpProductImageBodySchema = void 0;
112
+ exports.PRODUCT_IMAGE_MIME_TYPES = exports.MAX_PRODUCT_IMAGE_UPLOAD_BYTES = exports.confirmProductImageOutputSchema = exports.confirmProductImageInputSchema = exports.confirmProductImageBodySchema = exports.productImageUploadUrlOutputSchema = exports.productImageUploadUrlInputSchema = exports.productImageUploadUrlBodySchema = void 0;
112
113
  var zod_1 = __require("zod");
113
114
  var PRODUCT_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/jpg", "image/webp"];
114
115
  exports.PRODUCT_IMAGE_MIME_TYPES = PRODUCT_IMAGE_MIME_TYPES;
115
- var MAX_PRODUCT_IMAGE_BYTES = 358400;
116
- exports.MAX_PRODUCT_IMAGE_BYTES = MAX_PRODUCT_IMAGE_BYTES;
116
+ var MAX_PRODUCT_IMAGE_UPLOAD_BYTES = 5242880;
117
+ exports.MAX_PRODUCT_IMAGE_UPLOAD_BYTES = MAX_PRODUCT_IMAGE_UPLOAD_BYTES;
117
118
  var mimeTypeSchema = zod_1.z.enum(PRODUCT_IMAGE_MIME_TYPES, {
118
119
  message: "mimeType must be image/png, image/jpeg, image/jpg, or image/webp. Recovery: convert the file to a supported format."
119
120
  });
120
- var fileContentBase64Schema = zod_1.z.string().trim().min(1, "fileContentBase64 is required. Recovery: provide the image encoded as base64.").transform((value) => value.replace(/\s/g, "")).refine((value) => value.length > 0, {
121
- message: "fileContentBase64 is empty after stripping whitespace."
121
+ var fileNameSchema = zod_1.z.string().trim().min(1, "fileName is required.").max(255, "fileName must be at most 255 characters.");
122
+ exports.productImageUploadUrlBodySchema = zod_1.z.object({
123
+ fileName: fileNameSchema,
124
+ mimeType: mimeTypeSchema
125
+ }).strict();
126
+ exports.productImageUploadUrlInputSchema = exports.productImageUploadUrlBodySchema.extend({
127
+ merchantId: zod_1.z.uuid(),
128
+ productId: zod_1.z.uuid()
122
129
  });
123
- exports.uploadMcpProductImageBodySchema = zod_1.z.object({
124
- fileName: zod_1.z.string().trim().min(1, "fileName is required.").max(255, "fileName must be at most 255 characters."),
125
- mimeType: mimeTypeSchema,
126
- fileContentBase64: fileContentBase64Schema
127
- }).superRefine((body, ctx) => {
128
- const estimatedBytes = Math.floor(body.fileContentBase64.length * 0.75);
129
- if (estimatedBytes > MAX_PRODUCT_IMAGE_BYTES) {
130
- ctx.addIssue({
131
- code: "custom",
132
- message: `Image exceeds the 350 KB limit (estimated ${estimatedBytes} bytes). Recovery: compress or resize the image.`,
133
- path: ["fileContentBase64"]
134
- });
135
- }
130
+ exports.productImageUploadUrlOutputSchema = zod_1.z.object({
131
+ // S3 POST endpoint the client submits the multipart/form-data upload to.
132
+ url: zod_1.z.string(),
133
+ // Form fields the client must include in the multipart body (policy, signature,
134
+ // key, Content-Type, ...). The `file` field must be added last per S3's spec.
135
+ fields: zod_1.z.record(zod_1.z.string(), zod_1.z.string()),
136
+ // Opaque storage key the client must echo back to confirmProductImage. Bound to the
137
+ // merchant; confirm rejects keys outside the caller's own prefix.
138
+ key: zod_1.z.string(),
139
+ expiresIn: zod_1.z.number().int().positive()
136
140
  });
137
- exports.uploadMcpProductImageInputSchema = exports.uploadMcpProductImageBodySchema.extend({
141
+ exports.confirmProductImageBodySchema = zod_1.z.object({
142
+ key: zod_1.z.string().trim().min(1, "key is required. Recovery: use the key returned by the upload-url step.")
143
+ }).strict();
144
+ exports.confirmProductImageInputSchema = exports.confirmProductImageBodySchema.extend({
138
145
  merchantId: zod_1.z.uuid(),
139
146
  productId: zod_1.z.uuid()
140
147
  });
141
- exports.uploadMcpProductImageOutputSchema = zod_1.z.object({
142
- success: zod_1.z.literal(true)
148
+ exports.confirmProductImageOutputSchema = zod_1.z.object({
149
+ imageUrl: zod_1.z.string()
143
150
  });
144
151
  }
145
152
  });
@@ -275,7 +282,7 @@ var require_dist = __commonJS({
275
282
  "../../packages/mcp-contracts/dist/index.js"(exports) {
276
283
  "use strict";
277
284
  Object.defineProperty(exports, "__esModule", { value: true });
278
- exports.checkoutCustomizationOutputSchema = exports.updateCheckoutCustomizationInputSchema = exports.getCheckoutCustomizationInputSchema = exports.checkoutCustomizationFieldsSchema = exports.searchPaymentsAiDocsOutputSchema = exports.searchPaymentsAiDocsResultSchema = exports.searchPaymentsAiDocsInputSchema = exports.paymentsAiHealthOutputSchema = exports.paymentsAiHealthBackendSchema = exports.paymentsAiHealthBackendProbeSchema = exports.activationStatusOutputSchema = exports.activationStatusEnumSchema = exports.activationStatusInputSchema = exports.createMerchantAccountOutputSchema = exports.createMerchantAccountInputSchema = exports.PRODUCT_IMAGE_MIME_TYPES = exports.MAX_PRODUCT_IMAGE_BYTES = exports.uploadMcpProductImageOutputSchema = exports.uploadMcpProductImageInputSchema = exports.uploadMcpProductImageBodySchema = exports.createMcpProductOutputSchema = exports.createMcpProductInputSchema = exports.createMcpProductBodySchema = exports.MCP_TOOL_NAMES = void 0;
285
+ exports.checkoutCustomizationOutputSchema = exports.updateCheckoutCustomizationInputSchema = exports.getCheckoutCustomizationInputSchema = exports.checkoutCustomizationFieldsSchema = exports.searchPaymentsAiDocsOutputSchema = exports.searchPaymentsAiDocsResultSchema = exports.searchPaymentsAiDocsInputSchema = exports.paymentsAiHealthOutputSchema = exports.paymentsAiHealthBackendSchema = exports.paymentsAiHealthBackendProbeSchema = exports.activationStatusOutputSchema = exports.activationStatusEnumSchema = exports.activationStatusInputSchema = exports.createMerchantAccountOutputSchema = exports.createMerchantAccountInputSchema = exports.PRODUCT_IMAGE_MIME_TYPES = exports.MAX_PRODUCT_IMAGE_UPLOAD_BYTES = exports.confirmProductImageOutputSchema = exports.confirmProductImageInputSchema = exports.confirmProductImageBodySchema = exports.productImageUploadUrlOutputSchema = exports.productImageUploadUrlInputSchema = exports.productImageUploadUrlBodySchema = exports.createMcpProductOutputSchema = exports.createMcpProductInputSchema = exports.createMcpProductBodySchema = exports.MCP_TOOL_NAMES = void 0;
279
286
  var tool_names_js_1 = require_tool_names();
280
287
  Object.defineProperty(exports, "MCP_TOOL_NAMES", { enumerable: true, get: function() {
281
288
  return tool_names_js_1.MCP_TOOL_NAMES;
@@ -290,21 +297,30 @@ var require_dist = __commonJS({
290
297
  Object.defineProperty(exports, "createMcpProductOutputSchema", { enumerable: true, get: function() {
291
298
  return create_product_js_1.createMcpProductOutputSchema;
292
299
  } });
293
- var upload_product_image_js_1 = require_upload_product_image();
294
- Object.defineProperty(exports, "uploadMcpProductImageBodySchema", { enumerable: true, get: function() {
295
- return upload_product_image_js_1.uploadMcpProductImageBodySchema;
300
+ var product_image_presigned_upload_js_1 = require_product_image_presigned_upload();
301
+ Object.defineProperty(exports, "productImageUploadUrlBodySchema", { enumerable: true, get: function() {
302
+ return product_image_presigned_upload_js_1.productImageUploadUrlBodySchema;
303
+ } });
304
+ Object.defineProperty(exports, "productImageUploadUrlInputSchema", { enumerable: true, get: function() {
305
+ return product_image_presigned_upload_js_1.productImageUploadUrlInputSchema;
296
306
  } });
297
- Object.defineProperty(exports, "uploadMcpProductImageInputSchema", { enumerable: true, get: function() {
298
- return upload_product_image_js_1.uploadMcpProductImageInputSchema;
307
+ Object.defineProperty(exports, "productImageUploadUrlOutputSchema", { enumerable: true, get: function() {
308
+ return product_image_presigned_upload_js_1.productImageUploadUrlOutputSchema;
299
309
  } });
300
- Object.defineProperty(exports, "uploadMcpProductImageOutputSchema", { enumerable: true, get: function() {
301
- return upload_product_image_js_1.uploadMcpProductImageOutputSchema;
310
+ Object.defineProperty(exports, "confirmProductImageBodySchema", { enumerable: true, get: function() {
311
+ return product_image_presigned_upload_js_1.confirmProductImageBodySchema;
302
312
  } });
303
- Object.defineProperty(exports, "MAX_PRODUCT_IMAGE_BYTES", { enumerable: true, get: function() {
304
- return upload_product_image_js_1.MAX_PRODUCT_IMAGE_BYTES;
313
+ Object.defineProperty(exports, "confirmProductImageInputSchema", { enumerable: true, get: function() {
314
+ return product_image_presigned_upload_js_1.confirmProductImageInputSchema;
315
+ } });
316
+ Object.defineProperty(exports, "confirmProductImageOutputSchema", { enumerable: true, get: function() {
317
+ return product_image_presigned_upload_js_1.confirmProductImageOutputSchema;
318
+ } });
319
+ Object.defineProperty(exports, "MAX_PRODUCT_IMAGE_UPLOAD_BYTES", { enumerable: true, get: function() {
320
+ return product_image_presigned_upload_js_1.MAX_PRODUCT_IMAGE_UPLOAD_BYTES;
305
321
  } });
306
322
  Object.defineProperty(exports, "PRODUCT_IMAGE_MIME_TYPES", { enumerable: true, get: function() {
307
- return upload_product_image_js_1.PRODUCT_IMAGE_MIME_TYPES;
323
+ return product_image_presigned_upload_js_1.PRODUCT_IMAGE_MIME_TYPES;
308
324
  } });
309
325
  var create_merchant_account_js_1 = require_create_merchant_account();
310
326
  Object.defineProperty(exports, "createMerchantAccountInputSchema", { enumerable: true, get: function() {
@@ -482,8 +498,11 @@ function backendActivationStatusPath(merchantId) {
482
498
  function backendGetCheckoutCustomizationPath(merchantId) {
483
499
  return `/api/v1/mcp/merchants/${merchantId}/checkout/customization`;
484
500
  }
485
- function backendUploadProductImagePath(merchantId, productId) {
486
- return `/api/v1/mcp/merchants/${merchantId}/products/${productId}/image`;
501
+ function backendProductImageUploadUrlPath(merchantId, productId) {
502
+ return `/api/v1/mcp/merchants/${merchantId}/products/${productId}/image/upload-url`;
503
+ }
504
+ function backendConfirmProductImagePath(merchantId, productId) {
505
+ return `/api/v1/mcp/merchants/${merchantId}/products/${productId}/image/confirm`;
487
506
  }
488
507
  function backendUpdateCheckoutCustomizationPath(merchantId) {
489
508
  return `/api/v1/mcp/merchants/${merchantId}/checkout/customization`;
@@ -624,7 +643,7 @@ function createInstrumentedMcpServer(options2) {
624
643
  }
625
644
 
626
645
  // src/tools/register-tools.ts
627
- var import_mcp_contracts8 = __toESM(require_dist(), 1);
646
+ var import_mcp_contracts9 = __toESM(require_dist(), 1);
628
647
 
629
648
  // src/tools/merchant/activation-status.tool.ts
630
649
  var import_mcp_contracts = __toESM(require_dist(), 1);
@@ -729,56 +748,117 @@ ${formatZodIssuesForTool(parsed.error.issues)}`
729
748
  return { ok: true, data: parsed.data };
730
749
  }
731
750
 
732
- // src/tools/product/upload-product-image.tool.ts
751
+ // src/tools/product/get-product-image-upload-url.tool.ts
733
752
  var import_mcp_contracts3 = __toESM(require_dist(), 1);
734
- function runUploadProductImageHandlerValidation(raw) {
735
- const parsed = import_mcp_contracts3.uploadMcpProductImageInputSchema.safeParse(raw);
753
+ function runGetProductImageUploadUrlValidation(raw) {
754
+ const parsed = import_mcp_contracts3.productImageUploadUrlInputSchema.safeParse(raw);
736
755
  if (!parsed.success) {
737
756
  return {
738
757
  ok: false,
739
- errorText: `Invalid arguments for upload_product_image.
758
+ errorText: `Invalid arguments for get_product_image_upload_url.
740
759
  ${formatZodIssuesForTool(parsed.error.issues)}`
741
760
  };
742
761
  }
743
762
  return { ok: true, data: parsed.data };
744
763
  }
745
- function mapUploadHttpError(status, message) {
764
+ function mapHttpError(status, message) {
746
765
  if (status === 404) {
747
766
  return `Product not found for this merchant.
748
767
  ${message}
749
- Recovery: call create_product first and pass the returned id as productId. If the product was just created, retry upload_product_image with the same productId.`;
768
+ Recovery: call create_product first and pass the returned id as productId.`;
750
769
  }
751
770
  if (status === 422) {
752
771
  return `Cannot upload an image for this product.
753
772
  ${message}
754
- Recovery: archived products cannot receive images. Create a new product or restore the product in the dashboard.`;
773
+ Recovery: archived products cannot receive images.`;
774
+ }
775
+ return `Backend get product image upload URL request failed.
776
+ ${message}
777
+ Recovery: verify BACKEND_BASE_URL and that POST /api/v1/mcp/merchants/:merchantId/products/:productId/image/upload-url is reachable.`;
778
+ }
779
+ async function runGetProductImageUploadUrlHandler(backendClient, input) {
780
+ const { merchantId, productId, fileName, mimeType } = input;
781
+ const result = await backendClient.post(backendProductImageUploadUrlPath(merchantId, productId), {
782
+ fileName,
783
+ mimeType
784
+ });
785
+ if (!result.ok) {
786
+ return { ok: false, errorText: mapHttpError(result.status, result.message) };
787
+ }
788
+ let parsedJson;
789
+ try {
790
+ parsedJson = JSON.parse(result.bodyText);
791
+ } catch {
792
+ return {
793
+ ok: false,
794
+ errorText: "Backend returned non-JSON for get product image upload URL. Recovery: confirm the API matches this MCP server version."
795
+ };
796
+ }
797
+ const out = import_mcp_contracts3.productImageUploadUrlOutputSchema.safeParse(parsedJson);
798
+ if (!out.success) {
799
+ return {
800
+ ok: false,
801
+ errorText: "Backend response did not match expected upload URL output schema. Recovery: inspect backend deployment and shared @pai-managed/mcp-contracts version."
802
+ };
803
+ }
804
+ return { ok: true, data: out.data };
805
+ }
806
+
807
+ // src/tools/product/confirm-product-image.tool.ts
808
+ var import_mcp_contracts4 = __toESM(require_dist(), 1);
809
+ function runConfirmProductImageValidation(raw) {
810
+ const parsed = import_mcp_contracts4.confirmProductImageInputSchema.safeParse(raw);
811
+ if (!parsed.success) {
812
+ return {
813
+ ok: false,
814
+ errorText: `Invalid arguments for confirm_product_image.
815
+ ${formatZodIssuesForTool(parsed.error.issues)}`
816
+ };
817
+ }
818
+ return { ok: true, data: parsed.data };
819
+ }
820
+ function mapHttpError2(status, message) {
821
+ if (status === 404) {
822
+ return `Product not found for this merchant.
823
+ ${message}
824
+ Recovery: pass the same productId used with get_product_image_upload_url.`;
825
+ }
826
+ if (status === 422) {
827
+ return `Cannot upload an image for this product.
828
+ ${message}
829
+ Recovery: archived products cannot receive images.`;
755
830
  }
756
831
  if (status === 400) {
757
- return `Image upload rejected by the backend.
832
+ return `Uploaded image rejected by the backend.
758
833
  ${message}
759
- Recovery: use PNG, JPEG, or WebP under 350 KB and ensure fileContentBase64 matches the declared mimeType.`;
834
+ Recovery: POST the bytes (multipart/form-data with all returned fields, then the file field last) to the upload URL first, use PNG/JPEG/WebP under 5 MB, and pass the exact key returned by get_product_image_upload_url.`;
760
835
  }
761
- return `Backend upload product image request failed.
836
+ return `Backend confirm product image request failed.
762
837
  ${message}
763
- Recovery: verify BACKEND_BASE_URL and that POST /api/v1/mcp/merchants/:merchantId/products/:productId/image is reachable. If create_product succeeded but upload failed, retry upload_product_image with the same productId.`;
838
+ Recovery: verify BACKEND_BASE_URL and that POST /api/v1/mcp/merchants/:merchantId/products/:productId/image/confirm is reachable.`;
764
839
  }
765
- async function runUploadProductImageHandler(backendClient, input) {
766
- const { merchantId, productId, fileName, mimeType, fileContentBase64 } = input;
767
- const result = await backendClient.postNoContent(
768
- backendUploadProductImagePath(merchantId, productId),
769
- { fileName, mimeType, fileContentBase64 }
770
- );
840
+ async function runConfirmProductImageHandler(backendClient, input) {
841
+ const { merchantId, productId, key } = input;
842
+ const result = await backendClient.post(backendConfirmProductImagePath(merchantId, productId), {
843
+ key
844
+ });
771
845
  if (!result.ok) {
846
+ return { ok: false, errorText: mapHttpError2(result.status, result.message) };
847
+ }
848
+ let parsedJson;
849
+ try {
850
+ parsedJson = JSON.parse(result.bodyText);
851
+ } catch {
772
852
  return {
773
853
  ok: false,
774
- errorText: mapUploadHttpError(result.status, result.message)
854
+ errorText: "Backend returned non-JSON for confirm product image. Recovery: confirm the API matches this MCP server version."
775
855
  };
776
856
  }
777
- const out = import_mcp_contracts3.uploadMcpProductImageOutputSchema.safeParse({ success: true });
857
+ const out = import_mcp_contracts4.confirmProductImageOutputSchema.safeParse(parsedJson);
778
858
  if (!out.success) {
779
859
  return {
780
860
  ok: false,
781
- errorText: "Internal error building upload product image output. Recovery: contact your platform administrator."
861
+ errorText: "Backend response did not match expected confirm product image output schema. Recovery: inspect backend deployment and shared @pai-managed/mcp-contracts version."
782
862
  };
783
863
  }
784
864
  return { ok: true, data: out.data };
@@ -795,16 +875,18 @@ var DOC_SECTIONS = [
795
875
  title: "Create a Product",
796
876
  path: "/docs/products-plans#create-a-product",
797
877
  content: `1. Call **create_product** with merchantId, name, and at least one plan.
798
- 2. Optionally call **upload_product_image** with the returned product id as productId, plus fileName, mimeType, and fileContentBase64 (PNG/JPEG/WebP, max 350 KB).
878
+ 2. Optionally attach an image via the presigned upload flow: call **get_product_image_upload_url** with the returned product id, POST the raw bytes (multipart/form-data with the returned fields + file last) to the returned URL, then call **confirm_product_image** with the returned key.
799
879
  3. Use a returned planId to build a checkout URL: {checkout-base-url}/payment/{planId}`
800
880
  },
801
881
  {
802
882
  title: "Upload a Product Image (MCP)",
803
883
  path: "/docs/products-plans#upload-product-image-mcp",
804
884
  content: `After create_product returns an id:
805
- 1. Call **upload_product_image** with merchantId, productId (the create_product id), fileName, mimeType, and fileContentBase64.
806
- 2. Supported formats: PNG, JPEG, WebP. Maximum size: 350 KB.
807
- 3. If upload fails after create, retry upload_product_image with the same productId \u2014 the product row is kept.`
885
+ 1. Call **get_product_image_upload_url** with merchantId, productId, fileName, and mimeType. It returns { url, fields, key, expiresIn }.
886
+ 2. HTTP POST the raw file bytes to \`url\` as \`multipart/form-data\`: add every entry from \`fields\` as a form field FIRST, then add the \`file\` field last (S3 requires the file field last). Example: \`curl -X POST -F "key=<fields.key>" -F "Content-Type=<mimeType>" -F "policy=<fields.policy>" -F "x-amz-signature=<fields['x-amz-signature']>" ... -F "file=@./logo.png" "<url>"\`.
887
+ 3. Call **confirm_product_image** with merchantId, productId, and the returned key to validate and attach the image.
888
+ 4. Supported formats: PNG, JPEG, WebP. Maximum size: 5 MB (enforced by S3 at upload time via a \`content-length-range\` policy condition \u2014 oversized bodies return a 403 from S3, before the backend ever sees them).
889
+ 5. If confirm fails, retry get_product_image_upload_url + POST + confirm_product_image with the same productId \u2014 the product row is kept; the previous rejected upload was cleaned up by the backend.`
808
890
  },
809
891
  {
810
892
  title: "Plan Fields",
@@ -883,7 +965,7 @@ function runSearchPaymentsAiDocsHandler(query) {
883
965
  }
884
966
 
885
967
  // src/tools/health/payments-ai-health.tool.ts
886
- var import_mcp_contracts4 = __toESM(require_dist(), 1);
968
+ var import_mcp_contracts5 = __toESM(require_dist(), 1);
887
969
  async function buildPaymentsAiHealthOutput(backendClient) {
888
970
  const base = {
889
971
  serverName: "pai-managed-mcp-server",
@@ -892,11 +974,11 @@ async function buildPaymentsAiHealthOutput(backendClient) {
892
974
  backend: { baseUrlConfigured: false }
893
975
  };
894
976
  if (!backendClient) {
895
- return import_mcp_contracts4.paymentsAiHealthOutputSchema.parse(base);
977
+ return import_mcp_contracts5.paymentsAiHealthOutputSchema.parse(base);
896
978
  }
897
979
  const result = await backendClient.get(BACKEND_HTTP_HEALTH_PATH);
898
980
  if (!result.ok) {
899
- return import_mcp_contracts4.paymentsAiHealthOutputSchema.parse({
981
+ return import_mcp_contracts5.paymentsAiHealthOutputSchema.parse({
900
982
  ...base,
901
983
  backend: {
902
984
  baseUrlConfigured: true,
@@ -918,7 +1000,7 @@ async function buildPaymentsAiHealthOutput(backendClient) {
918
1000
  } catch {
919
1001
  message = result.bodyText.slice(0, 500) || message;
920
1002
  }
921
- return import_mcp_contracts4.paymentsAiHealthOutputSchema.parse({
1003
+ return import_mcp_contracts5.paymentsAiHealthOutputSchema.parse({
922
1004
  ...base,
923
1005
  backend: {
924
1006
  baseUrlConfigured: true,
@@ -933,15 +1015,15 @@ async function buildPaymentsAiHealthOutput(backendClient) {
933
1015
  }
934
1016
 
935
1017
  // src/tools/merchant/create-merchant-account.tool.ts
936
- var import_mcp_contracts5 = __toESM(require_dist(), 1);
1018
+ var import_mcp_contracts6 = __toESM(require_dist(), 1);
937
1019
  var STUB_MERCHANT = {
938
1020
  merchantId: "01930000-0000-7000-8000-000000000001"
939
1021
  };
940
1022
  function buildCreateMerchantAccountStub(_input) {
941
- return import_mcp_contracts5.createMerchantAccountOutputSchema.parse(STUB_MERCHANT);
1023
+ return import_mcp_contracts6.createMerchantAccountOutputSchema.parse(STUB_MERCHANT);
942
1024
  }
943
1025
  function runCreateMerchantAccountHandler(raw) {
944
- const parsed = import_mcp_contracts5.createMerchantAccountInputSchema.safeParse(raw);
1026
+ const parsed = import_mcp_contracts6.createMerchantAccountInputSchema.safeParse(raw);
945
1027
  if (!parsed.success) {
946
1028
  return {
947
1029
  ok: false,
@@ -974,7 +1056,7 @@ Recovery: verify BACKEND_BASE_URL and that POST ${BACKEND_CREATE_MERCHANT_ACCOUN
974
1056
  errorText: "Backend returned non-JSON for create_merchant_account. Recovery: confirm the API matches this MCP server version."
975
1057
  };
976
1058
  }
977
- const out = import_mcp_contracts5.createMerchantAccountOutputSchema.safeParse(parsedJson);
1059
+ const out = import_mcp_contracts6.createMerchantAccountOutputSchema.safeParse(parsedJson);
978
1060
  if (!out.success) {
979
1061
  return {
980
1062
  ok: false,
@@ -985,9 +1067,9 @@ Recovery: verify BACKEND_BASE_URL and that POST ${BACKEND_CREATE_MERCHANT_ACCOUN
985
1067
  }
986
1068
 
987
1069
  // src/tools/checkout/get-checkout-customization.tool.ts
988
- var import_mcp_contracts6 = __toESM(require_dist(), 1);
1070
+ var import_mcp_contracts7 = __toESM(require_dist(), 1);
989
1071
  function runGetCheckoutCustomizationValidation(raw) {
990
- const parsed = import_mcp_contracts6.getCheckoutCustomizationInputSchema.safeParse(raw);
1072
+ const parsed = import_mcp_contracts7.getCheckoutCustomizationInputSchema.safeParse(raw);
991
1073
  if (!parsed.success) {
992
1074
  return {
993
1075
  ok: false,
@@ -1017,7 +1099,7 @@ Recovery: verify BACKEND_BASE_URL and that GET /api/v1/mcp/merchants/:merchantId
1017
1099
  };
1018
1100
  }
1019
1101
  const customization = parsedJson?.customization;
1020
- const out = import_mcp_contracts6.checkoutCustomizationOutputSchema.safeParse(customization);
1102
+ const out = import_mcp_contracts7.checkoutCustomizationOutputSchema.safeParse(customization);
1021
1103
  if (!out.success) {
1022
1104
  return {
1023
1105
  ok: false,
@@ -1028,9 +1110,9 @@ Recovery: verify BACKEND_BASE_URL and that GET /api/v1/mcp/merchants/:merchantId
1028
1110
  }
1029
1111
 
1030
1112
  // src/tools/checkout/update-checkout-customization.tool.ts
1031
- var import_mcp_contracts7 = __toESM(require_dist(), 1);
1113
+ var import_mcp_contracts8 = __toESM(require_dist(), 1);
1032
1114
  function runUpdateCheckoutCustomizationValidation(raw) {
1033
- const parsed = import_mcp_contracts7.updateCheckoutCustomizationInputSchema.safeParse(raw);
1115
+ const parsed = import_mcp_contracts8.updateCheckoutCustomizationInputSchema.safeParse(raw);
1034
1116
  if (!parsed.success) {
1035
1117
  return {
1036
1118
  ok: false,
@@ -1063,7 +1145,7 @@ Recovery: verify BACKEND_BASE_URL and that PATCH /api/v1/mcp/merchants/:merchant
1063
1145
  };
1064
1146
  }
1065
1147
  const updated = parsedJson?.customization;
1066
- const out = import_mcp_contracts7.checkoutCustomizationOutputSchema.safeParse(updated);
1148
+ const out = import_mcp_contracts8.checkoutCustomizationOutputSchema.safeParse(updated);
1067
1149
  if (!out.success) {
1068
1150
  return {
1069
1151
  ok: false,
@@ -1092,10 +1174,10 @@ function registerTools(server, options2) {
1092
1174
  const backendClient = options2?.backendClient ?? createBackendClientFromEnv();
1093
1175
  const mcpTokenPayload = options2?.mcpTokenPayload;
1094
1176
  server.registerTool(
1095
- import_mcp_contracts8.MCP_TOOL_NAMES.PAYMENTS_AI_HEALTH,
1177
+ import_mcp_contracts9.MCP_TOOL_NAMES.PAYMENTS_AI_HEALTH,
1096
1178
  {
1097
1179
  description: "Check whether the PAI Managed MCP server is healthy. Returns server version and connectivity status.",
1098
- outputSchema: import_mcp_contracts8.paymentsAiHealthOutputSchema
1180
+ outputSchema: import_mcp_contracts9.paymentsAiHealthOutputSchema
1099
1181
  },
1100
1182
  async () => {
1101
1183
  if (!isScopeAllowed("health:read", mcpTokenPayload)) {
@@ -1109,11 +1191,11 @@ function registerTools(server, options2) {
1109
1191
  }
1110
1192
  );
1111
1193
  server.registerTool(
1112
- import_mcp_contracts8.MCP_TOOL_NAMES.CREATE_MERCHANT_ACCOUNT,
1194
+ import_mcp_contracts9.MCP_TOOL_NAMES.CREATE_MERCHANT_ACCOUNT,
1113
1195
  {
1114
1196
  description: "Create a merchant account on PAI Managed. Provide a business name and email; returns a merchant ID you can use with other tools such as create_product.",
1115
- inputSchema: import_mcp_contracts8.createMerchantAccountInputSchema,
1116
- outputSchema: import_mcp_contracts8.createMerchantAccountOutputSchema
1197
+ inputSchema: import_mcp_contracts9.createMerchantAccountInputSchema,
1198
+ outputSchema: import_mcp_contracts9.createMerchantAccountOutputSchema
1117
1199
  },
1118
1200
  async (args) => {
1119
1201
  if (!isScopeAllowed("merchant:create", mcpTokenPayload)) {
@@ -1134,11 +1216,11 @@ function registerTools(server, options2) {
1134
1216
  }
1135
1217
  );
1136
1218
  server.registerTool(
1137
- import_mcp_contracts8.MCP_TOOL_NAMES.GET_MERCHANT_ACTIVATION_STATUS,
1219
+ import_mcp_contracts9.MCP_TOOL_NAMES.GET_MERCHANT_ACTIVATION_STATUS,
1138
1220
  {
1139
1221
  description: 'Get the production activation status of a merchant account on PAI Managed. Returns "pending" (account exists, activation in progress) or "not_provisioned" (not yet set up).',
1140
- inputSchema: import_mcp_contracts8.activationStatusInputSchema,
1141
- outputSchema: import_mcp_contracts8.activationStatusOutputSchema
1222
+ inputSchema: import_mcp_contracts9.activationStatusInputSchema,
1223
+ outputSchema: import_mcp_contracts9.activationStatusOutputSchema
1142
1224
  },
1143
1225
  async (args) => {
1144
1226
  if (!isScopeAllowed("merchant:read", mcpTokenPayload)) {
@@ -1158,11 +1240,11 @@ function registerTools(server, options2) {
1158
1240
  }
1159
1241
  );
1160
1242
  server.registerTool(
1161
- import_mcp_contracts8.MCP_TOOL_NAMES.SEARCH_PAYMENTS_AI_DOCS,
1243
+ import_mcp_contracts9.MCP_TOOL_NAMES.SEARCH_PAYMENTS_AI_DOCS,
1162
1244
  {
1163
1245
  description: "Search the PAI Managed documentation knowledge base to find guides, API references, UI walkthroughs, and feature explanations. Returns matching sections with titles and direct documentation links. Use this tool to understand how PAI Managed works, find step-by-step instructions for dashboard tasks, or locate specific features such as creating products, configuring plans, or generating checkout links.",
1164
- inputSchema: import_mcp_contracts8.searchPaymentsAiDocsInputSchema,
1165
- outputSchema: import_mcp_contracts8.searchPaymentsAiDocsOutputSchema
1246
+ inputSchema: import_mcp_contracts9.searchPaymentsAiDocsInputSchema,
1247
+ outputSchema: import_mcp_contracts9.searchPaymentsAiDocsOutputSchema
1166
1248
  },
1167
1249
  async (args) => {
1168
1250
  if (!isScopeAllowed("docs:read", mcpTokenPayload)) {
@@ -1176,11 +1258,11 @@ function registerTools(server, options2) {
1176
1258
  }
1177
1259
  );
1178
1260
  server.registerTool(
1179
- import_mcp_contracts8.MCP_TOOL_NAMES.CREATE_PRODUCT,
1261
+ import_mcp_contracts9.MCP_TOOL_NAMES.CREATE_PRODUCT,
1180
1262
  {
1181
- description: "Create a product with one or more pricing plans for a merchant on the PAI Managed platform. Products represent digital offerings (software, subscriptions, services) that customers can purchase via a checkout link. Each plan defines the price (in USD cents), billing type (one-time, recurring, or free-access), and billing period. Returns the product ID and the plan IDs; use a plan ID to build a checkout URL for your customers. Product images are not supported on create \u2014 after success, call upload_product_image with the returned id as productId.",
1182
- inputSchema: import_mcp_contracts8.createMcpProductInputSchema,
1183
- outputSchema: import_mcp_contracts8.createMcpProductOutputSchema
1263
+ description: "Create a product with one or more pricing plans for a merchant on the PAI Managed platform. Products represent digital offerings (software, subscriptions, services) that customers can purchase via a checkout link. Each plan defines the price (in USD cents), billing type (one-time, recurring, or free-access), and billing period. Returns the product ID and the plan IDs; use a plan ID to build a checkout URL for your customers. Product images are not supported on create \u2014 after success, call get_product_image_upload_url with the returned id as productId, POST the file bytes (multipart/form-data) to the returned URL, then call confirm_product_image with the returned key.",
1264
+ inputSchema: import_mcp_contracts9.createMcpProductInputSchema,
1265
+ outputSchema: import_mcp_contracts9.createMcpProductOutputSchema
1184
1266
  },
1185
1267
  async (args) => {
1186
1268
  if (!isScopeAllowed("product:create", mcpTokenPayload)) {
@@ -1209,17 +1291,50 @@ function registerTools(server, options2) {
1209
1291
  }
1210
1292
  );
1211
1293
  server.registerTool(
1212
- import_mcp_contracts8.MCP_TOOL_NAMES.UPLOAD_PRODUCT_IMAGE,
1294
+ import_mcp_contracts9.MCP_TOOL_NAMES.GET_PRODUCT_IMAGE_UPLOAD_URL,
1295
+ {
1296
+ description: 'Get a presigned POST policy to upload a product image directly to storage \u2014 the image bytes never pass through the tool call, and S3 enforces the 5 MB cap at upload time via `content-length-range` (oversized bodies get a 403 from S3, not the backend). Use this on any surface that can issue an HTTP multipart/form-data POST (CLI, agent, dashboard). The product must already exist (call create_product first). Provide merchantId, productId, fileName, and mimeType (image/png, image/jpeg, image/jpg, or image/webp). Returns { url, fields, key, expiresIn }: issue an HTTP POST to `url` with Content-Type: multipart/form-data. Add every entry from `fields` as a form field FIRST, then add the `file` field last (S3 requires the file field last), e.g. `curl -X POST <all -F fields...> -F "file=@./logo.png" "<url>"`. Then call confirm_product_image with the returned key to validate and attach the image.',
1297
+ inputSchema: import_mcp_contracts9.productImageUploadUrlInputSchema,
1298
+ outputSchema: import_mcp_contracts9.productImageUploadUrlOutputSchema
1299
+ },
1300
+ async (args) => {
1301
+ if (!isScopeAllowed("product:create", mcpTokenPayload)) {
1302
+ return toolError("Insufficient scope.");
1303
+ }
1304
+ const validation = runGetProductImageUploadUrlValidation(args);
1305
+ if (!validation.ok) {
1306
+ return toolError(validation.errorText);
1307
+ }
1308
+ if (!isMerchantAllowed(validation.data.merchantId, mcpTokenPayload)) {
1309
+ return toolError("Merchant not authorized for this MCP key.");
1310
+ }
1311
+ if (!backendClient) {
1312
+ return toolError(
1313
+ "The PAI Managed backend is not reachable. Contact your platform administrator to configure backend connectivity."
1314
+ );
1315
+ }
1316
+ const result = await runGetProductImageUploadUrlHandler(backendClient, validation.data);
1317
+ if (!result.ok) {
1318
+ return toolError(result.errorText);
1319
+ }
1320
+ return {
1321
+ content: [{ type: "text", text: JSON.stringify(result.data) }],
1322
+ structuredContent: result.data
1323
+ };
1324
+ }
1325
+ );
1326
+ server.registerTool(
1327
+ import_mcp_contracts9.MCP_TOOL_NAMES.CONFIRM_PRODUCT_IMAGE,
1213
1328
  {
1214
- description: "Upload a product image for an existing product on PAI Managed. The product must already exist (call create_product first). Requires merchantId, productId, fileName, mimeType (image/png, image/jpeg, image/jpg, or image/webp), and fileContentBase64 (max 350 KB). Replaces any existing product image. On failure after create_product, retry upload_product_image with the same productId.",
1215
- inputSchema: import_mcp_contracts8.uploadMcpProductImageInputSchema,
1216
- outputSchema: import_mcp_contracts8.uploadMcpProductImageOutputSchema
1329
+ description: "Confirm a product image uploaded via a presigned POST policy from get_product_image_upload_url. Call this after the multipart/form-data POST succeeds. Provide merchantId, productId, and the key returned by get_product_image_upload_url. The backend validates the stored object (format, size, magic bytes) and attaches it to the product. Returns { imageUrl } for verification. Replaces any existing product image.",
1330
+ inputSchema: import_mcp_contracts9.confirmProductImageInputSchema,
1331
+ outputSchema: import_mcp_contracts9.confirmProductImageOutputSchema
1217
1332
  },
1218
1333
  async (args) => {
1219
1334
  if (!isScopeAllowed("product:create", mcpTokenPayload)) {
1220
1335
  return toolError("Insufficient scope.");
1221
1336
  }
1222
- const validation = runUploadProductImageHandlerValidation(args);
1337
+ const validation = runConfirmProductImageValidation(args);
1223
1338
  if (!validation.ok) {
1224
1339
  return toolError(validation.errorText);
1225
1340
  }
@@ -1231,7 +1346,7 @@ function registerTools(server, options2) {
1231
1346
  "The PAI Managed backend is not reachable. Contact your platform administrator to configure backend connectivity."
1232
1347
  );
1233
1348
  }
1234
- const result = await runUploadProductImageHandler(backendClient, validation.data);
1349
+ const result = await runConfirmProductImageHandler(backendClient, validation.data);
1235
1350
  if (!result.ok) {
1236
1351
  return toolError(result.errorText);
1237
1352
  }
@@ -1242,11 +1357,11 @@ function registerTools(server, options2) {
1242
1357
  }
1243
1358
  );
1244
1359
  server.registerTool(
1245
- import_mcp_contracts8.MCP_TOOL_NAMES.GET_CHECKOUT_CUSTOMIZATION,
1360
+ import_mcp_contracts9.MCP_TOOL_NAMES.GET_CHECKOUT_CUSTOMIZATION,
1246
1361
  {
1247
1362
  description: "Get a merchant's checkout customization on PAI Managed \u2014 theme mode, colors (background, button, button text, font), font family, input style, and the current logo URL (read-only). Use this to inspect current branding before changing it.",
1248
- inputSchema: import_mcp_contracts8.getCheckoutCustomizationInputSchema,
1249
- outputSchema: import_mcp_contracts8.checkoutCustomizationOutputSchema
1363
+ inputSchema: import_mcp_contracts9.getCheckoutCustomizationInputSchema,
1364
+ outputSchema: import_mcp_contracts9.checkoutCustomizationOutputSchema
1250
1365
  },
1251
1366
  async (args) => {
1252
1367
  if (!isScopeAllowed("checkout:read", mcpTokenPayload)) {
@@ -1275,11 +1390,11 @@ function registerTools(server, options2) {
1275
1390
  }
1276
1391
  );
1277
1392
  server.registerTool(
1278
- import_mcp_contracts8.MCP_TOOL_NAMES.UPDATE_CHECKOUT_CUSTOMIZATION,
1393
+ import_mcp_contracts9.MCP_TOOL_NAMES.UPDATE_CHECKOUT_CUSTOMIZATION,
1279
1394
  {
1280
1395
  description: "Update a merchant's checkout customization on PAI Managed. Fields are MERGED: send only the fields you want to change \u2014 any field you omit is left unchanged. To remove/reset a field, use the dashboard (clearing is not supported over MCP). Colors are 6-digit hex like '#1A2B3C'; fontFamily is 'Inter' or 'Roboto'; themeMode is 'light' or 'dark'; inputStyle is 'rounded' or 'square'. The logo is set via the dashboard. Returns the full customization after the merge.",
1281
- inputSchema: import_mcp_contracts8.updateCheckoutCustomizationInputSchema,
1282
- outputSchema: import_mcp_contracts8.checkoutCustomizationOutputSchema
1396
+ inputSchema: import_mcp_contracts9.updateCheckoutCustomizationInputSchema,
1397
+ outputSchema: import_mcp_contracts9.checkoutCustomizationOutputSchema
1283
1398
  },
1284
1399
  async (args) => {
1285
1400
  if (!isScopeAllowed("checkout:write", mcpTokenPayload)) {
package/package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "name": "@payments-ai/mcp",
3
- "version": "1.1.2",
3
+ "version": "1.4.3",
4
4
  "private": false,
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
5
8
  "description": "MCP for Payments AI",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/paymentsai/pai-managed.git",
12
+ "directory": "apps/mcp-server"
13
+ },
6
14
  "bin": {
7
15
  "payments-ai-mcp": "./dist/main.js"
8
16
  },
@@ -15,23 +23,23 @@
15
23
  "start": "node dist/main.http.js",
16
24
  "dev": "tsx watch src/main.http.ts",
17
25
  "build": "node esbuild.config.mjs",
18
- "build:http": "pnpm exec tsgo -p tsconfig.json",
26
+ "build:types": "pnpm exec tsgo -p tsconfig.build.json",
19
27
  "typecheck": "pnpm exec tsgo --noEmit -p tsconfig.json",
20
28
  "lint": "oxlint .",
21
29
  "lint:fix": "oxlint --fix .",
22
30
  "test": "tsx --test test/**/*.test.ts",
23
- "clean": "rm -rf dist",
24
- "prepublishOnly": "pnpm run clean && pnpm run build"
31
+ "clean": "rm -rf dist dist-types",
32
+ "prepublishOnly": "pnpm --filter @pai-managed/mcp-contracts run build && pnpm run clean && pnpm run build"
25
33
  },
26
34
  "dependencies": {
27
35
  "@modelcontextprotocol/sdk": "1.29.0",
28
- "@pai-managed/mcp-contracts": "workspace:*",
29
36
  "@sentry/node": "10.58.0",
30
37
  "cors": "2.8.6",
31
38
  "dotenv": "17.3.1",
32
39
  "zod": "4.4.3"
33
40
  },
34
41
  "devDependencies": {
42
+ "@pai-managed/mcp-contracts": "workspace:*",
35
43
  "@tsconfig/node24": "24.0.4",
36
44
  "@types/cors": "2.8.19",
37
45
  "@types/express": "5.0.6",
@@ -43,5 +51,5 @@
43
51
  "engines": {
44
52
  "node": ">=24"
45
53
  },
46
- "mcpName": "io.github.szarl/payments-ai-mcp"
54
+ "mcpName": "ai.payments.managed/payments-ai-mcp"
47
55
  }