@payments-ai/mcp 1.1.3 → 1.6.0

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 +34522 -0
  2. package/package.json +23 -12
  3. package/dist/main.js +0 -1330
package/dist/main.js DELETED
@@ -1,1330 +0,0 @@
1
- #!/usr/bin/env node
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
- }) : x)(function(x) {
11
- if (typeof require !== "undefined") return require.apply(this, arguments);
12
- throw Error('Dynamic require of "' + x + '" is not supported');
13
- });
14
- var __commonJS = (cb, mod) => function __require2() {
15
- try {
16
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
17
- } catch (e) {
18
- throw mod = 0, e;
19
- }
20
- };
21
- var __copyProps = (to, from, except, desc) => {
22
- if (from && typeof from === "object" || typeof from === "function") {
23
- for (let key of __getOwnPropNames(from))
24
- if (!__hasOwnProp.call(to, key) && key !== except)
25
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
26
- }
27
- return to;
28
- };
29
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
30
- // If the importer is in node compatibility mode or this is not an ESM
31
- // file that has been converted to a CommonJS file using a Babel-
32
- // compatible transform (i.e. "__esModule" has not been set), then set
33
- // "default" to the CommonJS "module.exports" for node compatibility.
34
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
35
- mod
36
- ));
37
-
38
- // ../../packages/mcp-contracts/dist/tool-names.js
39
- var require_tool_names = __commonJS({
40
- "../../packages/mcp-contracts/dist/tool-names.js"(exports) {
41
- "use strict";
42
- Object.defineProperty(exports, "__esModule", { value: true });
43
- exports.MCP_TOOL_NAMES = void 0;
44
- exports.MCP_TOOL_NAMES = {
45
- PAYMENTS_AI_HEALTH: "payments_ai_health",
46
- CREATE_MERCHANT_ACCOUNT: "create_merchant_account",
47
- GET_MERCHANT_ACTIVATION_STATUS: "get_merchant_activation_status",
48
- SEARCH_PAYMENTS_AI_DOCS: "search_payments_ai_docs",
49
- CREATE_PRODUCT: "create_product",
50
- UPLOAD_PRODUCT_IMAGE: "upload_product_image",
51
- GET_CHECKOUT_CUSTOMIZATION: "get_checkout_customization",
52
- UPDATE_CHECKOUT_CUSTOMIZATION: "update_checkout_customization"
53
- };
54
- }
55
- });
56
-
57
- // ../../packages/mcp-contracts/dist/create-product.js
58
- var require_create_product = __commonJS({
59
- "../../packages/mcp-contracts/dist/create-product.js"(exports) {
60
- "use strict";
61
- Object.defineProperty(exports, "__esModule", { value: true });
62
- exports.createMcpProductOutputSchema = exports.createMcpProductInputSchema = exports.createMcpProductBodySchema = void 0;
63
- var zod_1 = __require("zod");
64
- var PLAN_TYPES = ["free-access", "one-time", "recurring"];
65
- var BILLING_PERIODS = ["day", "week", "month", "year"];
66
- var PRODUCT_STATUSES = ["draft", "active", "archived"];
67
- var createMcpPlanSchema = zod_1.z.object({
68
- name: zod_1.z.string().trim().min(1).max(30),
69
- description: zod_1.z.string().trim().max(80).optional(),
70
- richDescription: zod_1.z.string().trim().max(65535).optional(),
71
- currency: zod_1.z.string().trim().length(3).transform((v) => v.toLowerCase()),
72
- amount: zod_1.z.number().nonnegative(),
73
- type: zod_1.z.enum(PLAN_TYPES),
74
- billingPeriod: zod_1.z.enum(BILLING_PERIODS),
75
- periodLength: zod_1.z.number().int().positive(),
76
- freeTrial: zod_1.z.number().int().min(0).optional()
77
- }).superRefine((plan, ctx) => {
78
- if (plan.type === "free-access")
79
- return;
80
- if (plan.amount <= 0) {
81
- ctx.addIssue({
82
- code: "custom",
83
- message: "Amount must be greater than 0 for paid plans",
84
- path: ["amount"]
85
- });
86
- }
87
- });
88
- exports.createMcpProductBodySchema = zod_1.z.object({
89
- name: zod_1.z.string().trim().min(1).max(30),
90
- description: zod_1.z.string().trim().min(1).max(80).optional(),
91
- taxCodeId: zod_1.z.string().trim().min(1).max(255).optional(),
92
- status: zod_1.z.enum(PRODUCT_STATUSES).optional().default("draft"),
93
- discountCode: zod_1.z.string().trim().min(1).max(255).optional(),
94
- plans: zod_1.z.array(createMcpPlanSchema).min(1)
95
- }).strict();
96
- exports.createMcpProductInputSchema = exports.createMcpProductBodySchema.extend({
97
- merchantId: zod_1.z.uuid()
98
- });
99
- exports.createMcpProductOutputSchema = zod_1.z.object({
100
- id: zod_1.z.string(),
101
- planIds: zod_1.z.array(zod_1.z.string())
102
- });
103
- }
104
- });
105
-
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) {
109
- "use strict";
110
- 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
- var zod_1 = __require("zod");
113
- var PRODUCT_IMAGE_MIME_TYPES = ["image/png", "image/jpeg", "image/jpg", "image/webp"];
114
- 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;
117
- var mimeTypeSchema = zod_1.z.enum(PRODUCT_IMAGE_MIME_TYPES, {
118
- message: "mimeType must be image/png, image/jpeg, image/jpg, or image/webp. Recovery: convert the file to a supported format."
119
- });
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."
122
- });
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
- }
136
- });
137
- exports.uploadMcpProductImageInputSchema = exports.uploadMcpProductImageBodySchema.extend({
138
- merchantId: zod_1.z.uuid(),
139
- productId: zod_1.z.uuid()
140
- });
141
- exports.uploadMcpProductImageOutputSchema = zod_1.z.object({
142
- success: zod_1.z.literal(true)
143
- });
144
- }
145
- });
146
-
147
- // ../../packages/mcp-contracts/dist/create-merchant-account.js
148
- var require_create_merchant_account = __commonJS({
149
- "../../packages/mcp-contracts/dist/create-merchant-account.js"(exports) {
150
- "use strict";
151
- Object.defineProperty(exports, "__esModule", { value: true });
152
- exports.createMerchantAccountOutputSchema = exports.createMerchantAccountInputSchema = void 0;
153
- var zod_1 = __require("zod");
154
- exports.createMerchantAccountInputSchema = zod_1.z.object({
155
- name: zod_1.z.string().trim().min(1, {
156
- message: "Provide a non-empty business or display name. Recovery: re-send the tool call with a visible label for the merchant account."
157
- }),
158
- email: zod_1.z.email({
159
- error: "Provide a valid email. Recovery: check spelling, domain, and that the value is a single RFC-style address."
160
- })
161
- });
162
- exports.createMerchantAccountOutputSchema = zod_1.z.object({
163
- merchantId: zod_1.z.uuid()
164
- });
165
- }
166
- });
167
-
168
- // ../../packages/mcp-contracts/dist/activation-status.js
169
- var require_activation_status = __commonJS({
170
- "../../packages/mcp-contracts/dist/activation-status.js"(exports) {
171
- "use strict";
172
- Object.defineProperty(exports, "__esModule", { value: true });
173
- exports.activationStatusOutputSchema = exports.activationStatusEnumSchema = exports.activationStatusInputSchema = void 0;
174
- var zod_1 = __require("zod");
175
- exports.activationStatusInputSchema = zod_1.z.object({
176
- merchantId: zod_1.z.string().trim().min(1, {
177
- message: "Provide the merchant account identifier returned from create_merchant_account. Recovery: copy the merchantId from a prior successful create_merchant_account response."
178
- })
179
- });
180
- exports.activationStatusEnumSchema = zod_1.z.enum(["pending", "not_provisioned", "active"]);
181
- exports.activationStatusOutputSchema = zod_1.z.object({
182
- status: exports.activationStatusEnumSchema
183
- });
184
- }
185
- });
186
-
187
- // ../../packages/mcp-contracts/dist/payments-ai-health.js
188
- var require_payments_ai_health = __commonJS({
189
- "../../packages/mcp-contracts/dist/payments-ai-health.js"(exports) {
190
- "use strict";
191
- Object.defineProperty(exports, "__esModule", { value: true });
192
- exports.paymentsAiHealthOutputSchema = exports.paymentsAiHealthBackendSchema = exports.paymentsAiHealthBackendProbeSchema = void 0;
193
- var zod_1 = __require("zod");
194
- exports.paymentsAiHealthBackendProbeSchema = zod_1.z.object({
195
- path: zod_1.z.string(),
196
- ok: zod_1.z.boolean(),
197
- httpStatus: zod_1.z.number().optional(),
198
- message: zod_1.z.string()
199
- });
200
- exports.paymentsAiHealthBackendSchema = zod_1.z.object({
201
- baseUrlConfigured: zod_1.z.boolean(),
202
- probe: exports.paymentsAiHealthBackendProbeSchema.optional()
203
- });
204
- exports.paymentsAiHealthOutputSchema = zod_1.z.object({
205
- serverName: zod_1.z.string(),
206
- version: zod_1.z.string(),
207
- /** Always true for this PoC slice — tool remains illustrative / non-mutating. */
208
- stub: zod_1.z.literal(true),
209
- backend: exports.paymentsAiHealthBackendSchema
210
- });
211
- }
212
- });
213
-
214
- // ../../packages/mcp-contracts/dist/search-payments-ai-docs.js
215
- var require_search_payments_ai_docs = __commonJS({
216
- "../../packages/mcp-contracts/dist/search-payments-ai-docs.js"(exports) {
217
- "use strict";
218
- Object.defineProperty(exports, "__esModule", { value: true });
219
- exports.searchPaymentsAiDocsOutputSchema = exports.searchPaymentsAiDocsResultSchema = exports.searchPaymentsAiDocsInputSchema = void 0;
220
- var zod_1 = __require("zod");
221
- exports.searchPaymentsAiDocsInputSchema = zod_1.z.object({
222
- query: zod_1.z.string().min(1).max(500).describe("Search query")
223
- });
224
- exports.searchPaymentsAiDocsResultSchema = zod_1.z.object({
225
- title: zod_1.z.string().describe("Section title"),
226
- path: zod_1.z.string().describe("Documentation path/anchor"),
227
- content: zod_1.z.string().describe("Section content")
228
- });
229
- exports.searchPaymentsAiDocsOutputSchema = zod_1.z.object({
230
- results: zod_1.z.array(exports.searchPaymentsAiDocsResultSchema).describe("Matching documentation sections with titles and links")
231
- });
232
- }
233
- });
234
-
235
- // ../../packages/mcp-contracts/dist/checkout-customization.js
236
- var require_checkout_customization = __commonJS({
237
- "../../packages/mcp-contracts/dist/checkout-customization.js"(exports) {
238
- "use strict";
239
- Object.defineProperty(exports, "__esModule", { value: true });
240
- exports.checkoutCustomizationOutputSchema = exports.updateCheckoutCustomizationInputSchema = exports.getCheckoutCustomizationInputSchema = exports.checkoutCustomizationFieldsSchema = void 0;
241
- var zod_1 = __require("zod");
242
- var HEX_COLOR_REGEX = /^#(?:[0-9a-fA-F]{6})$/;
243
- var CHECKOUT_THEME_MODES = ["light", "dark"];
244
- var CHECKOUT_FONT_FAMILIES = ["Inter", "Roboto"];
245
- var CHECKOUT_INPUT_STYLES = ["rounded", "square"];
246
- var hexColor = zod_1.z.string().regex(HEX_COLOR_REGEX, {
247
- message: 'Provide a 6-digit hex color like "#1A2B3C". Recovery: include the leading "#".'
248
- });
249
- exports.checkoutCustomizationFieldsSchema = zod_1.z.object({
250
- themeMode: zod_1.z.enum(CHECKOUT_THEME_MODES).optional(),
251
- backgroundColor: hexColor.optional(),
252
- buttonColor: hexColor.optional(),
253
- buttonTextColor: hexColor.optional(),
254
- fontColor: hexColor.optional(),
255
- fontFamily: zod_1.z.enum(CHECKOUT_FONT_FAMILIES).optional(),
256
- inputStyle: zod_1.z.enum(CHECKOUT_INPUT_STYLES).optional()
257
- }).strict();
258
- exports.getCheckoutCustomizationInputSchema = zod_1.z.object({
259
- merchantId: zod_1.z.uuid()
260
- });
261
- exports.updateCheckoutCustomizationInputSchema = zod_1.z.object({
262
- merchantId: zod_1.z.uuid(),
263
- // At least one field must be present — an empty update is a no-op that would still bump
264
- // updated_at, so reject it with a clear message rather than writing nothing meaningful.
265
- customization: exports.checkoutCustomizationFieldsSchema.refine((c) => Object.keys(c).length > 0, 'Provide at least one field to change. Recovery: include e.g. "buttonColor" or "themeMode".')
266
- });
267
- exports.checkoutCustomizationOutputSchema = exports.checkoutCustomizationFieldsSchema.extend({
268
- logoUrl: zod_1.z.string().optional()
269
- }).passthrough();
270
- }
271
- });
272
-
273
- // ../../packages/mcp-contracts/dist/index.js
274
- var require_dist = __commonJS({
275
- "../../packages/mcp-contracts/dist/index.js"(exports) {
276
- "use strict";
277
- 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;
279
- var tool_names_js_1 = require_tool_names();
280
- Object.defineProperty(exports, "MCP_TOOL_NAMES", { enumerable: true, get: function() {
281
- return tool_names_js_1.MCP_TOOL_NAMES;
282
- } });
283
- var create_product_js_1 = require_create_product();
284
- Object.defineProperty(exports, "createMcpProductBodySchema", { enumerable: true, get: function() {
285
- return create_product_js_1.createMcpProductBodySchema;
286
- } });
287
- Object.defineProperty(exports, "createMcpProductInputSchema", { enumerable: true, get: function() {
288
- return create_product_js_1.createMcpProductInputSchema;
289
- } });
290
- Object.defineProperty(exports, "createMcpProductOutputSchema", { enumerable: true, get: function() {
291
- return create_product_js_1.createMcpProductOutputSchema;
292
- } });
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;
296
- } });
297
- Object.defineProperty(exports, "uploadMcpProductImageInputSchema", { enumerable: true, get: function() {
298
- return upload_product_image_js_1.uploadMcpProductImageInputSchema;
299
- } });
300
- Object.defineProperty(exports, "uploadMcpProductImageOutputSchema", { enumerable: true, get: function() {
301
- return upload_product_image_js_1.uploadMcpProductImageOutputSchema;
302
- } });
303
- Object.defineProperty(exports, "MAX_PRODUCT_IMAGE_BYTES", { enumerable: true, get: function() {
304
- return upload_product_image_js_1.MAX_PRODUCT_IMAGE_BYTES;
305
- } });
306
- Object.defineProperty(exports, "PRODUCT_IMAGE_MIME_TYPES", { enumerable: true, get: function() {
307
- return upload_product_image_js_1.PRODUCT_IMAGE_MIME_TYPES;
308
- } });
309
- var create_merchant_account_js_1 = require_create_merchant_account();
310
- Object.defineProperty(exports, "createMerchantAccountInputSchema", { enumerable: true, get: function() {
311
- return create_merchant_account_js_1.createMerchantAccountInputSchema;
312
- } });
313
- Object.defineProperty(exports, "createMerchantAccountOutputSchema", { enumerable: true, get: function() {
314
- return create_merchant_account_js_1.createMerchantAccountOutputSchema;
315
- } });
316
- var activation_status_js_1 = require_activation_status();
317
- Object.defineProperty(exports, "activationStatusInputSchema", { enumerable: true, get: function() {
318
- return activation_status_js_1.activationStatusInputSchema;
319
- } });
320
- Object.defineProperty(exports, "activationStatusEnumSchema", { enumerable: true, get: function() {
321
- return activation_status_js_1.activationStatusEnumSchema;
322
- } });
323
- Object.defineProperty(exports, "activationStatusOutputSchema", { enumerable: true, get: function() {
324
- return activation_status_js_1.activationStatusOutputSchema;
325
- } });
326
- var payments_ai_health_js_1 = require_payments_ai_health();
327
- Object.defineProperty(exports, "paymentsAiHealthBackendProbeSchema", { enumerable: true, get: function() {
328
- return payments_ai_health_js_1.paymentsAiHealthBackendProbeSchema;
329
- } });
330
- Object.defineProperty(exports, "paymentsAiHealthBackendSchema", { enumerable: true, get: function() {
331
- return payments_ai_health_js_1.paymentsAiHealthBackendSchema;
332
- } });
333
- Object.defineProperty(exports, "paymentsAiHealthOutputSchema", { enumerable: true, get: function() {
334
- return payments_ai_health_js_1.paymentsAiHealthOutputSchema;
335
- } });
336
- var search_payments_ai_docs_js_1 = require_search_payments_ai_docs();
337
- Object.defineProperty(exports, "searchPaymentsAiDocsInputSchema", { enumerable: true, get: function() {
338
- return search_payments_ai_docs_js_1.searchPaymentsAiDocsInputSchema;
339
- } });
340
- Object.defineProperty(exports, "searchPaymentsAiDocsResultSchema", { enumerable: true, get: function() {
341
- return search_payments_ai_docs_js_1.searchPaymentsAiDocsResultSchema;
342
- } });
343
- Object.defineProperty(exports, "searchPaymentsAiDocsOutputSchema", { enumerable: true, get: function() {
344
- return search_payments_ai_docs_js_1.searchPaymentsAiDocsOutputSchema;
345
- } });
346
- var checkout_customization_js_1 = require_checkout_customization();
347
- Object.defineProperty(exports, "checkoutCustomizationFieldsSchema", { enumerable: true, get: function() {
348
- return checkout_customization_js_1.checkoutCustomizationFieldsSchema;
349
- } });
350
- Object.defineProperty(exports, "getCheckoutCustomizationInputSchema", { enumerable: true, get: function() {
351
- return checkout_customization_js_1.getCheckoutCustomizationInputSchema;
352
- } });
353
- Object.defineProperty(exports, "updateCheckoutCustomizationInputSchema", { enumerable: true, get: function() {
354
- return checkout_customization_js_1.updateCheckoutCustomizationInputSchema;
355
- } });
356
- Object.defineProperty(exports, "checkoutCustomizationOutputSchema", { enumerable: true, get: function() {
357
- return checkout_customization_js_1.checkoutCustomizationOutputSchema;
358
- } });
359
- }
360
- });
361
-
362
- // src/load-env.ts
363
- import path from "node:path";
364
- import dotenv from "dotenv";
365
- var packageRoot = path.resolve(import.meta.dirname, "..");
366
- dotenv.config({ path: path.join(packageRoot, ".env"), quiet: true });
367
- dotenv.config({ path: path.join(process.cwd(), ".env"), quiet: true });
368
-
369
- // src/observability/instrument-sentry.ts
370
- import * as Sentry from "@sentry/node";
371
-
372
- // src/config/env.ts
373
- var APP_ENV_VALUES = ["local", "development", "production", "stage", "test"];
374
- function readAppEnv(env = process.env) {
375
- const raw = env.APP_ENV?.trim();
376
- if (!raw) {
377
- return void 0;
378
- }
379
- if (!APP_ENV_VALUES.includes(raw)) {
380
- throw new Error(`APP_ENV must be one of: ${APP_ENV_VALUES.join(", ")}`);
381
- }
382
- return raw;
383
- }
384
-
385
- // src/observability/sentry.config.ts
386
- function parseTracesSampleRate(value, defaultValue) {
387
- if (!value?.trim()) {
388
- return defaultValue;
389
- }
390
- const parsed = Number(value);
391
- if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
392
- return defaultValue;
393
- }
394
- return parsed;
395
- }
396
- function defaultTracesSampleRateForAppEnv(appEnv) {
397
- if (appEnv === "stage" || appEnv === "production") {
398
- return 0.2;
399
- }
400
- return 1;
401
- }
402
- function readSentryDsn(env = process.env) {
403
- const dsn = env.SENTRY_DSN?.trim();
404
- return dsn || void 0;
405
- }
406
- function resolveSentryEnvironment(env = process.env) {
407
- const fromEnv = env.SENTRY_ENVIRONMENT?.trim();
408
- if (fromEnv) {
409
- return fromEnv;
410
- }
411
- return readAppEnv(env) ?? "local";
412
- }
413
- function readSentryTracesSampleRate(env = process.env) {
414
- const appEnv = readAppEnv(env);
415
- return parseTracesSampleRate(
416
- env.SENTRY_TRACES_SAMPLE_RATE,
417
- defaultTracesSampleRateForAppEnv(appEnv)
418
- );
419
- }
420
- function scrubAuthorizationHeader(headers) {
421
- if (!headers) {
422
- return headers;
423
- }
424
- const scrubbed = { ...headers };
425
- for (const key of Object.keys(scrubbed)) {
426
- if (key.toLowerCase() === "authorization") {
427
- scrubbed[key] = "[Filtered]";
428
- }
429
- }
430
- return scrubbed;
431
- }
432
- function scrubEventRequestHeaders(event) {
433
- const request = event.request;
434
- if (!request?.headers) {
435
- return event;
436
- }
437
- return {
438
- ...event,
439
- request: {
440
- ...request,
441
- headers: scrubAuthorizationHeader(request.headers)
442
- }
443
- };
444
- }
445
- function buildSentryInitOptions(env = process.env) {
446
- const dsn = readSentryDsn(env);
447
- if (!dsn) {
448
- return void 0;
449
- }
450
- return {
451
- dsn,
452
- environment: resolveSentryEnvironment(env),
453
- tracesSampleRate: readSentryTracesSampleRate(env),
454
- sendDefaultPii: false,
455
- beforeSend(event) {
456
- return scrubEventRequestHeaders(event);
457
- },
458
- beforeSendTransaction(event) {
459
- return scrubEventRequestHeaders(event);
460
- }
461
- };
462
- }
463
-
464
- // src/observability/instrument-sentry.ts
465
- var options = buildSentryInitOptions();
466
- if (options) {
467
- Sentry.init(options);
468
- }
469
-
470
- // src/main.ts
471
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
472
-
473
- // src/backend/backend-client.ts
474
- var BACKEND_HTTP_HEALTH_PATH = "/api/v1/health-checks/http";
475
- var BACKEND_CREATE_MERCHANT_ACCOUNT_PATH = "/api/v1/mcp/provision";
476
- function backendCreateProductPath(merchantId) {
477
- return `/api/v1/mcp/merchants/${merchantId}/products`;
478
- }
479
- function backendActivationStatusPath(merchantId) {
480
- return `/api/v1/mcp/merchants/${merchantId}/activation-status`;
481
- }
482
- function backendGetCheckoutCustomizationPath(merchantId) {
483
- return `/api/v1/mcp/merchants/${merchantId}/checkout/customization`;
484
- }
485
- function backendUploadProductImagePath(merchantId, productId) {
486
- return `/api/v1/mcp/merchants/${merchantId}/products/${productId}/image`;
487
- }
488
- function backendUpdateCheckoutCustomizationPath(merchantId) {
489
- return `/api/v1/mcp/merchants/${merchantId}/checkout/customization`;
490
- }
491
- var DEFAULT_TIMEOUT_MS = 1e4;
492
- function normalizeBaseUrl(baseUrl) {
493
- return baseUrl.replace(/\/+$/, "");
494
- }
495
- function joinUrl(baseUrl, path2) {
496
- const p = path2.startsWith("/") ? path2 : `/${path2}`;
497
- return `${normalizeBaseUrl(baseUrl)}${p}`;
498
- }
499
- var BackendClient = class {
500
- baseUrl;
501
- timeoutMs;
502
- fetchImpl;
503
- authorizationHeader;
504
- constructor(config) {
505
- this.baseUrl = normalizeBaseUrl(config.baseUrl);
506
- this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
507
- this.fetchImpl = config.fetchImpl ?? globalThis.fetch;
508
- this.authorizationHeader = config.authorizationHeader;
509
- }
510
- async get(path2) {
511
- const url = joinUrl(this.baseUrl, path2);
512
- let response;
513
- try {
514
- response = await this.fetchImpl(url, {
515
- method: "GET",
516
- headers: { Accept: "application/json", ...this.buildAuthHeader() },
517
- signal: AbortSignal.timeout(this.timeoutMs)
518
- });
519
- } catch (error) {
520
- const isAbort = error instanceof Error && error.name === "TimeoutError";
521
- return {
522
- ok: false,
523
- kind: "network",
524
- message: isAbort ? `Backend request timed out after ${this.timeoutMs}ms. Check BACKEND_BASE_URL, network reachability, and firewall rules.` : `Could not reach backend at ${this.baseUrl}. Verify BACKEND_BASE_URL and that the backend process is running. (${error instanceof Error ? error.message : String(error)})`
525
- };
526
- }
527
- const bodyText = await response.text();
528
- if (!response.ok) {
529
- return {
530
- ok: false,
531
- kind: "http_error",
532
- status: response.status,
533
- message: `Backend returned HTTP ${response.status}. ${bodyText ? `Body: ${bodyText.slice(0, 500)}` : "Empty response body."} If this persists, confirm the route exists and ingress targets the API service.`
534
- };
535
- }
536
- return { ok: true, status: response.status, bodyText };
537
- }
538
- post(path2, jsonBody) {
539
- return this.sendJson("POST", path2, jsonBody);
540
- }
541
- patch(path2, jsonBody) {
542
- return this.sendJson("PATCH", path2, jsonBody);
543
- }
544
- postNoContent(path2, jsonBody) {
545
- return this.sendJson("POST", path2, jsonBody);
546
- }
547
- async sendJson(method, path2, jsonBody) {
548
- const url = joinUrl(this.baseUrl, path2);
549
- let response;
550
- try {
551
- response = await this.fetchImpl(url, {
552
- method,
553
- headers: {
554
- Accept: "application/json",
555
- "Content-Type": "application/json",
556
- ...this.buildAuthHeader()
557
- },
558
- body: JSON.stringify(jsonBody),
559
- signal: AbortSignal.timeout(this.timeoutMs)
560
- });
561
- } catch (error) {
562
- const isAbort = error instanceof Error && error.name === "TimeoutError";
563
- return {
564
- ok: false,
565
- kind: "network",
566
- message: isAbort ? `Backend request timed out after ${this.timeoutMs}ms. Check BACKEND_BASE_URL, network reachability, and firewall rules.` : `Could not reach backend at ${this.baseUrl}. Verify BACKEND_BASE_URL and that the backend process is running. (${error instanceof Error ? error.message : String(error)})`
567
- };
568
- }
569
- const bodyText = await response.text();
570
- if (!response.ok) {
571
- return {
572
- ok: false,
573
- kind: "http_error",
574
- status: response.status,
575
- message: `Backend returned HTTP ${response.status}. ${bodyText ? `Body: ${bodyText.slice(0, 500)}` : "Empty response body."} If this persists, confirm the route exists and ingress targets the API service.`
576
- };
577
- }
578
- return { ok: true, status: response.status, bodyText };
579
- }
580
- buildAuthHeader() {
581
- return this.authorizationHeader ? { Authorization: this.authorizationHeader } : {};
582
- }
583
- };
584
- function createBackendClientFromEnv(explicitBaseUrl) {
585
- const raw = explicitBaseUrl?.trim() || process.env.BACKEND_BASE_URL?.trim();
586
- if (!raw) {
587
- console.error(
588
- "BACKEND_BASE_URL is not set; MCP server runs in stub-only mode for backend-backed probes (set it to enable HTTP checks from tools)."
589
- );
590
- return void 0;
591
- }
592
- const timeoutRaw = process.env.BACKEND_HTTP_TIMEOUT_MS?.trim();
593
- const timeoutMs = timeoutRaw ? Number(timeoutRaw) : DEFAULT_TIMEOUT_MS;
594
- const parsedTimeout = Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : DEFAULT_TIMEOUT_MS;
595
- const serviceToken = process.env.BACKEND_SERVICE_TOKEN?.trim();
596
- const authorizationHeader = serviceToken ? `Bearer ${serviceToken}` : void 0;
597
- return new BackendClient({ baseUrl: raw, timeoutMs: parsedTimeout, authorizationHeader });
598
- }
599
-
600
- // src/load-package-meta.ts
601
- import { readFileSync } from "node:fs";
602
- import { dirname, join } from "node:path";
603
- import { fileURLToPath } from "node:url";
604
- var __dirname = dirname(fileURLToPath(import.meta.url));
605
- function readMcpServerVersion() {
606
- const path2 = join(__dirname, "..", "package.json");
607
- const raw = readFileSync(path2, "utf8");
608
- const pkg = JSON.parse(raw);
609
- return pkg.version ?? "0.0.0";
610
- }
611
-
612
- // src/observability/create-instrumented-mcp-server.ts
613
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
614
- var MCP_SERVER_NAME = "pai-managed-mcp-server";
615
- function createInstrumentedMcpServer(options2) {
616
- const server = new McpServer({
617
- name: MCP_SERVER_NAME,
618
- version: options2.version
619
- });
620
- return Sentry.wrapMcpServerWithSentry(server, {
621
- recordInputs: false,
622
- recordOutputs: false
623
- });
624
- }
625
-
626
- // src/tools/register-tools.ts
627
- var import_mcp_contracts8 = __toESM(require_dist(), 1);
628
-
629
- // src/tools/merchant/activation-status.tool.ts
630
- var import_mcp_contracts = __toESM(require_dist(), 1);
631
-
632
- // src/shared/zod-format.ts
633
- function formatZodIssuesForTool(issues) {
634
- return issues.map((issue) => {
635
- const path2 = issue.path.length > 0 ? issue.path.join(".") : "arguments";
636
- return `[${path2}] ${issue.message}`;
637
- }).join("\n");
638
- }
639
-
640
- // src/tools/merchant/activation-status.tool.ts
641
- function stubStatusForAccount(merchantId) {
642
- return merchantId.toLowerCase().includes("pending") ? "pending" : "not_provisioned";
643
- }
644
- function buildActivationStatusStub(input) {
645
- return import_mcp_contracts.activationStatusOutputSchema.parse({
646
- status: stubStatusForAccount(input.merchantId)
647
- });
648
- }
649
- function runActivationStatusHandler(raw) {
650
- const parsed = import_mcp_contracts.activationStatusInputSchema.safeParse(raw);
651
- if (!parsed.success) {
652
- return {
653
- ok: false,
654
- errorText: `Invalid arguments for get_merchant_activation_status.
655
- ${formatZodIssuesForTool(parsed.error.issues)}`
656
- };
657
- }
658
- return { ok: true, data: buildActivationStatusStub(parsed.data) };
659
- }
660
- async function runActivationStatusHandlerWithBackend(backendClient, input) {
661
- const result = await backendClient.get(backendActivationStatusPath(input.merchantId));
662
- if (!result.ok) {
663
- return {
664
- ok: false,
665
- errorText: `Backend activation status request failed.
666
- ${result.message}
667
- Recovery: verify BACKEND_BASE_URL and that GET ${backendActivationStatusPath(input.merchantId)} is reachable from the MCP container or host.`
668
- };
669
- }
670
- let parsedJson;
671
- try {
672
- parsedJson = JSON.parse(result.bodyText);
673
- } catch {
674
- return {
675
- ok: false,
676
- errorText: "Backend returned non-JSON for get_merchant_activation_status. Recovery: confirm the API matches this MCP server version."
677
- };
678
- }
679
- const out = import_mcp_contracts.activationStatusOutputSchema.safeParse(parsedJson);
680
- if (!out.success) {
681
- return {
682
- ok: false,
683
- errorText: "Backend response did not match the expected activation status output schema. Recovery: inspect backend deployment and shared @pai-managed/mcp-contracts version."
684
- };
685
- }
686
- return { ok: true, data: out.data };
687
- }
688
-
689
- // src/tools/product/create-product.tool.ts
690
- var import_mcp_contracts2 = __toESM(require_dist(), 1);
691
- async function runCreateProductHandler(backendClient, input) {
692
- const { merchantId, ...body } = input;
693
- const result = await backendClient.post(backendCreateProductPath(merchantId), body);
694
- if (!result.ok) {
695
- return {
696
- ok: false,
697
- errorText: `Backend create product request failed.
698
- ${result.message}
699
- Recovery: verify BACKEND_BASE_URL and that POST /api/v1/mcp/merchants/:merchantId/products is reachable.`
700
- };
701
- }
702
- let parsedJson;
703
- try {
704
- parsedJson = JSON.parse(result.bodyText);
705
- } catch {
706
- return {
707
- ok: false,
708
- errorText: "Backend returned non-JSON for create product. Recovery: confirm the API matches this MCP server version."
709
- };
710
- }
711
- const out = import_mcp_contracts2.createMcpProductOutputSchema.safeParse(parsedJson);
712
- if (!out.success) {
713
- return {
714
- ok: false,
715
- errorText: "Backend response did not match expected create product output schema. Recovery: inspect backend deployment and shared @pai-managed/mcp-contracts version."
716
- };
717
- }
718
- return { ok: true, data: out.data };
719
- }
720
- function runCreateProductHandlerValidation(raw) {
721
- const parsed = import_mcp_contracts2.createMcpProductInputSchema.safeParse(raw);
722
- if (!parsed.success) {
723
- return {
724
- ok: false,
725
- errorText: `Invalid arguments for create_product.
726
- ${formatZodIssuesForTool(parsed.error.issues)}`
727
- };
728
- }
729
- return { ok: true, data: parsed.data };
730
- }
731
-
732
- // src/tools/product/upload-product-image.tool.ts
733
- var import_mcp_contracts3 = __toESM(require_dist(), 1);
734
- function runUploadProductImageHandlerValidation(raw) {
735
- const parsed = import_mcp_contracts3.uploadMcpProductImageInputSchema.safeParse(raw);
736
- if (!parsed.success) {
737
- return {
738
- ok: false,
739
- errorText: `Invalid arguments for upload_product_image.
740
- ${formatZodIssuesForTool(parsed.error.issues)}`
741
- };
742
- }
743
- return { ok: true, data: parsed.data };
744
- }
745
- function mapUploadHttpError(status, message) {
746
- if (status === 404) {
747
- return `Product not found for this merchant.
748
- ${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.`;
750
- }
751
- if (status === 422) {
752
- return `Cannot upload an image for this product.
753
- ${message}
754
- Recovery: archived products cannot receive images. Create a new product or restore the product in the dashboard.`;
755
- }
756
- if (status === 400) {
757
- return `Image upload rejected by the backend.
758
- ${message}
759
- Recovery: use PNG, JPEG, or WebP under 350 KB and ensure fileContentBase64 matches the declared mimeType.`;
760
- }
761
- return `Backend upload product image request failed.
762
- ${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.`;
764
- }
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
- );
771
- if (!result.ok) {
772
- return {
773
- ok: false,
774
- errorText: mapUploadHttpError(result.status, result.message)
775
- };
776
- }
777
- const out = import_mcp_contracts3.uploadMcpProductImageOutputSchema.safeParse({ success: true });
778
- if (!out.success) {
779
- return {
780
- ok: false,
781
- errorText: "Internal error building upload product image output. Recovery: contact your platform administrator."
782
- };
783
- }
784
- return { ok: true, data: out.data };
785
- }
786
-
787
- // src/tools/docs/knowledge/products-plans.ts
788
- var DOC_SECTIONS = [
789
- {
790
- title: "Navigation \u2014 Products & Plans",
791
- path: "/docs/products-plans#navigation",
792
- content: `To access Products & Plans, find **Products** in the left sidebar under the "Product" group (Package icon). Click **Products** to navigate to /products.`
793
- },
794
- {
795
- title: "Create a Product",
796
- path: "/docs/products-plans#create-a-product",
797
- 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).
799
- 3. Use a returned planId to build a checkout URL: {checkout-base-url}/payment/{planId}`
800
- },
801
- {
802
- title: "Upload a Product Image (MCP)",
803
- path: "/docs/products-plans#upload-product-image-mcp",
804
- 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.`
808
- },
809
- {
810
- title: "Plan Fields",
811
- path: "/docs/products-plans#plan-fields",
812
- content: `Each plan has the following fields:
813
- - **Name** \u2014 display name for the plan (max 30 characters)
814
- - **Amount** \u2014 price in USD as a decimal dollar amount (e.g. 9.99 = $9.99)
815
- - **Type** \u2014 one of: \`one-time\`, \`recurring\`, \`free-access\`
816
- - **Billing Period** \u2014 one of: \`day\`, \`week\`, \`month\`, \`year\`
817
- - **Period Length** \u2014 number of billing periods per cycle
818
- - **Free Trial** \u2014 number of free trial days (optional)`
819
- },
820
- {
821
- title: "Edit a Product",
822
- path: "/docs/products-plans#edit-a-product",
823
- content: `1. Go to **Products** in the left sidebar.
824
- 2. Click on the product row you want to edit.
825
- 3. Click **Edit** to modify name, description, status, or plans.`
826
- },
827
- {
828
- title: "Checkout URL",
829
- path: "/docs/products-plans#checkout-url",
830
- content: `Each plan has a unique checkout URL. The format is:
831
- {checkout-base-url}/payment/{planId}
832
-
833
- Embed it as: \`<a href="{checkout-base-url}/payment/{planId}">Pay Now!</a>\`
834
-
835
- Customers land on the payment page when they click the link. The \`planId\` is returned when you create a product via the \`create_product\` MCP tool.`
836
- }
837
- ];
838
-
839
- // src/tools/docs/knowledge/checkout-customization.ts
840
- var CHECKOUT_CUSTOMIZATION_DOC_SECTIONS = [
841
- {
842
- title: "Checkout Customization \u2014 Overview",
843
- path: "/docs/checkout-customization#overview",
844
- content: `Checkout customization controls the branding of a merchant's hosted checkout (the customer-facing payment page at /payment/{planId}). Configurable fields:
845
- - **themeMode** \u2014 \`light\` or \`dark\`
846
- - **backgroundColor**, **buttonColor**, **buttonTextColor**, **fontColor** \u2014 6-digit hex like \`#1A2B3C\`
847
- - **fontFamily** \u2014 \`Inter\` or \`Roboto\`
848
- - **inputStyle** \u2014 \`rounded\` or \`square\`
849
- - **logo** \u2014 set via the dashboard (not yet settable over MCP)
850
-
851
- Read with the \`get_checkout_customization\` MCP tool; change with \`update_checkout_customization\`.`
852
- },
853
- {
854
- title: "Checkout Customization \u2014 Updating branding via MCP",
855
- path: "/docs/checkout-customization#update",
856
- content: `The \`update_checkout_customization\` tool MERGES the fields you send: only the fields in the call are changed, and any field you omit keeps its current value. So you can send just \`{ "buttonColor": "#0A84FF" }\` to recolor the button without touching the rest of the branding.
857
-
858
- To remove or reset a field back to default, use the dashboard \u2014 clearing a field is not supported over MCP. Colors must be 6-digit hex (include the leading \`#\`). The tool returns the full customization after the merge, including the current \`logoUrl\` if a logo was set via the dashboard.`
859
- },
860
- {
861
- title: "Checkout Customization \u2014 Logo",
862
- path: "/docs/checkout-customization#logo",
863
- content: `The checkout logo is currently set through the PAI Managed dashboard (Checkout settings). It is returned as a read-only \`logoUrl\` by \`get_checkout_customization\`. Setting the logo directly over MCP is planned but not yet available.`
864
- }
865
- ];
866
-
867
- // src/tools/docs/search-payments-ai-docs.tool.ts
868
- var ALL_DOC_SECTIONS = [...DOC_SECTIONS, ...CHECKOUT_CUSTOMIZATION_DOC_SECTIONS];
869
- function runSearchPaymentsAiDocsHandler(query) {
870
- const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 1);
871
- if (terms.length === 0) {
872
- return { results: ALL_DOC_SECTIONS };
873
- }
874
- const scored = ALL_DOC_SECTIONS.map((section) => {
875
- const haystack = `${section.title} ${section.content}`.toLowerCase();
876
- const hits = terms.filter((t) => haystack.includes(t)).length;
877
- return { section, hits };
878
- });
879
- const matches = scored.filter((s) => s.hits > 0).sort((a, b) => b.hits - a.hits);
880
- return {
881
- results: matches.length > 0 ? matches.map((s) => s.section) : ALL_DOC_SECTIONS
882
- };
883
- }
884
-
885
- // src/tools/health/payments-ai-health.tool.ts
886
- var import_mcp_contracts4 = __toESM(require_dist(), 1);
887
- async function buildPaymentsAiHealthOutput(backendClient) {
888
- const base = {
889
- serverName: "pai-managed-mcp-server",
890
- version: readMcpServerVersion(),
891
- stub: true,
892
- backend: { baseUrlConfigured: false }
893
- };
894
- if (!backendClient) {
895
- return import_mcp_contracts4.paymentsAiHealthOutputSchema.parse(base);
896
- }
897
- const result = await backendClient.get(BACKEND_HTTP_HEALTH_PATH);
898
- if (!result.ok) {
899
- return import_mcp_contracts4.paymentsAiHealthOutputSchema.parse({
900
- ...base,
901
- backend: {
902
- baseUrlConfigured: true,
903
- probe: {
904
- path: BACKEND_HTTP_HEALTH_PATH,
905
- ok: false,
906
- httpStatus: result.status,
907
- message: result.message
908
- }
909
- }
910
- });
911
- }
912
- let message = "Backend responded successfully.";
913
- try {
914
- const json = JSON.parse(result.bodyText);
915
- if (typeof json.message === "string" && json.message.length > 0) {
916
- message = json.message;
917
- }
918
- } catch {
919
- message = result.bodyText.slice(0, 500) || message;
920
- }
921
- return import_mcp_contracts4.paymentsAiHealthOutputSchema.parse({
922
- ...base,
923
- backend: {
924
- baseUrlConfigured: true,
925
- probe: {
926
- path: BACKEND_HTTP_HEALTH_PATH,
927
- ok: true,
928
- httpStatus: result.status,
929
- message
930
- }
931
- }
932
- });
933
- }
934
-
935
- // src/tools/merchant/create-merchant-account.tool.ts
936
- var import_mcp_contracts5 = __toESM(require_dist(), 1);
937
- var STUB_MERCHANT = {
938
- merchantId: "01930000-0000-7000-8000-000000000001"
939
- };
940
- function buildCreateMerchantAccountStub(_input) {
941
- return import_mcp_contracts5.createMerchantAccountOutputSchema.parse(STUB_MERCHANT);
942
- }
943
- function runCreateMerchantAccountHandler(raw) {
944
- const parsed = import_mcp_contracts5.createMerchantAccountInputSchema.safeParse(raw);
945
- if (!parsed.success) {
946
- return {
947
- ok: false,
948
- errorText: `Invalid arguments for create_merchant_account.
949
- ${formatZodIssuesForTool(parsed.error.issues)}`
950
- };
951
- }
952
- return { ok: true, data: buildCreateMerchantAccountStub(parsed.data) };
953
- }
954
- async function runCreateMerchantAccountHandlerWithBackend(backendClient, input, userId) {
955
- const result = await backendClient.post(BACKEND_CREATE_MERCHANT_ACCOUNT_PATH, {
956
- name: input.name,
957
- email: input.email,
958
- userId
959
- });
960
- if (!result.ok) {
961
- return {
962
- ok: false,
963
- errorText: `Backend create merchant account request failed.
964
- ${result.message}
965
- Recovery: verify BACKEND_BASE_URL and that POST ${BACKEND_CREATE_MERCHANT_ACCOUNT_PATH} is reachable from the MCP container or host.`
966
- };
967
- }
968
- let parsedJson;
969
- try {
970
- parsedJson = JSON.parse(result.bodyText);
971
- } catch {
972
- return {
973
- ok: false,
974
- errorText: "Backend returned non-JSON for create_merchant_account. Recovery: confirm the API matches this MCP server version."
975
- };
976
- }
977
- const out = import_mcp_contracts5.createMerchantAccountOutputSchema.safeParse(parsedJson);
978
- if (!out.success) {
979
- return {
980
- ok: false,
981
- errorText: "Backend response did not match the expected merchant account output schema. Recovery: inspect backend deployment and shared @pai-managed/mcp-contracts version."
982
- };
983
- }
984
- return { ok: true, data: out.data };
985
- }
986
-
987
- // src/tools/checkout/get-checkout-customization.tool.ts
988
- var import_mcp_contracts6 = __toESM(require_dist(), 1);
989
- function runGetCheckoutCustomizationValidation(raw) {
990
- const parsed = import_mcp_contracts6.getCheckoutCustomizationInputSchema.safeParse(raw);
991
- if (!parsed.success) {
992
- return {
993
- ok: false,
994
- errorText: `Invalid arguments for get_checkout_customization.
995
- ${formatZodIssuesForTool(parsed.error.issues)}`
996
- };
997
- }
998
- return { ok: true, data: parsed.data };
999
- }
1000
- async function runGetCheckoutCustomizationHandler(backendClient, input) {
1001
- const result = await backendClient.get(backendGetCheckoutCustomizationPath(input.merchantId));
1002
- if (!result.ok) {
1003
- return {
1004
- ok: false,
1005
- errorText: `Backend get checkout customization request failed.
1006
- ${result.message}
1007
- Recovery: verify BACKEND_BASE_URL and that GET /api/v1/mcp/merchants/:merchantId/checkout/customization is reachable.`
1008
- };
1009
- }
1010
- let parsedJson;
1011
- try {
1012
- parsedJson = JSON.parse(result.bodyText);
1013
- } catch {
1014
- return {
1015
- ok: false,
1016
- errorText: "Backend returned non-JSON for get checkout customization. Recovery: confirm the API matches this MCP server version."
1017
- };
1018
- }
1019
- const customization = parsedJson?.customization;
1020
- const out = import_mcp_contracts6.checkoutCustomizationOutputSchema.safeParse(customization);
1021
- if (!out.success) {
1022
- return {
1023
- ok: false,
1024
- errorText: "Backend response did not match expected checkout customization output schema. Recovery: inspect backend deployment and shared @pai-managed/mcp-contracts version."
1025
- };
1026
- }
1027
- return { ok: true, data: out.data };
1028
- }
1029
-
1030
- // src/tools/checkout/update-checkout-customization.tool.ts
1031
- var import_mcp_contracts7 = __toESM(require_dist(), 1);
1032
- function runUpdateCheckoutCustomizationValidation(raw) {
1033
- const parsed = import_mcp_contracts7.updateCheckoutCustomizationInputSchema.safeParse(raw);
1034
- if (!parsed.success) {
1035
- return {
1036
- ok: false,
1037
- errorText: `Invalid arguments for update_checkout_customization.
1038
- ${formatZodIssuesForTool(parsed.error.issues)}`
1039
- };
1040
- }
1041
- return { ok: true, data: parsed.data };
1042
- }
1043
- async function runUpdateCheckoutCustomizationHandler(backendClient, input) {
1044
- const { merchantId, customization } = input;
1045
- const result = await backendClient.patch(backendUpdateCheckoutCustomizationPath(merchantId), {
1046
- customization
1047
- });
1048
- if (!result.ok) {
1049
- return {
1050
- ok: false,
1051
- errorText: `Backend update checkout customization request failed.
1052
- ${result.message}
1053
- Recovery: verify BACKEND_BASE_URL and that PATCH /api/v1/mcp/merchants/:merchantId/checkout/customization is reachable.`
1054
- };
1055
- }
1056
- let parsedJson;
1057
- try {
1058
- parsedJson = JSON.parse(result.bodyText);
1059
- } catch {
1060
- return {
1061
- ok: false,
1062
- errorText: "Backend returned non-JSON for update checkout customization. Recovery: confirm the API matches this MCP server version."
1063
- };
1064
- }
1065
- const updated = parsedJson?.customization;
1066
- const out = import_mcp_contracts7.checkoutCustomizationOutputSchema.safeParse(updated);
1067
- if (!out.success) {
1068
- return {
1069
- ok: false,
1070
- errorText: "Backend response did not match expected checkout customization output schema. Recovery: inspect backend deployment and shared @pai-managed/mcp-contracts version."
1071
- };
1072
- }
1073
- return { ok: true, data: out.data };
1074
- }
1075
-
1076
- // src/tools/register-tools.ts
1077
- function toolError(text) {
1078
- return {
1079
- isError: true,
1080
- content: [{ type: "text", text }]
1081
- };
1082
- }
1083
- function isMerchantAllowed(merchantId, payload) {
1084
- return !payload || payload.merchantIds.includes(merchantId);
1085
- }
1086
- function isScopeAllowed(required, payload) {
1087
- if (!payload) return true;
1088
- if (payload.scopes === null) return true;
1089
- return payload.scopes.includes(required);
1090
- }
1091
- function registerTools(server, options2) {
1092
- const backendClient = options2?.backendClient ?? createBackendClientFromEnv();
1093
- const mcpTokenPayload = options2?.mcpTokenPayload;
1094
- server.registerTool(
1095
- import_mcp_contracts8.MCP_TOOL_NAMES.PAYMENTS_AI_HEALTH,
1096
- {
1097
- description: "Check whether the PAI Managed MCP server is healthy. Returns server version and connectivity status.",
1098
- outputSchema: import_mcp_contracts8.paymentsAiHealthOutputSchema
1099
- },
1100
- async () => {
1101
- if (!isScopeAllowed("health:read", mcpTokenPayload)) {
1102
- return toolError("Insufficient scope.");
1103
- }
1104
- const structuredContent = await buildPaymentsAiHealthOutput(backendClient);
1105
- return {
1106
- content: [{ type: "text", text: JSON.stringify(structuredContent) }],
1107
- structuredContent
1108
- };
1109
- }
1110
- );
1111
- server.registerTool(
1112
- import_mcp_contracts8.MCP_TOOL_NAMES.CREATE_MERCHANT_ACCOUNT,
1113
- {
1114
- 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
1117
- },
1118
- async (args) => {
1119
- if (!isScopeAllowed("merchant:create", mcpTokenPayload)) {
1120
- return toolError("Insufficient scope.");
1121
- }
1122
- const result = backendClient ? await runCreateMerchantAccountHandlerWithBackend(
1123
- backendClient,
1124
- args,
1125
- mcpTokenPayload?.userId
1126
- ) : runCreateMerchantAccountHandler(args);
1127
- if (!result.ok) {
1128
- return toolError(result.errorText);
1129
- }
1130
- return {
1131
- content: [{ type: "text", text: JSON.stringify(result.data) }],
1132
- structuredContent: result.data
1133
- };
1134
- }
1135
- );
1136
- server.registerTool(
1137
- import_mcp_contracts8.MCP_TOOL_NAMES.GET_MERCHANT_ACTIVATION_STATUS,
1138
- {
1139
- 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
1142
- },
1143
- async (args) => {
1144
- if (!isScopeAllowed("merchant:read", mcpTokenPayload)) {
1145
- return toolError("Insufficient scope.");
1146
- }
1147
- if (!isMerchantAllowed(args.merchantId, mcpTokenPayload)) {
1148
- return toolError("Merchant not authorized for this MCP key.");
1149
- }
1150
- const result = backendClient ? await runActivationStatusHandlerWithBackend(backendClient, args) : runActivationStatusHandler(args);
1151
- if (!result.ok) {
1152
- return toolError(result.errorText);
1153
- }
1154
- return {
1155
- content: [{ type: "text", text: JSON.stringify(result.data) }],
1156
- structuredContent: result.data
1157
- };
1158
- }
1159
- );
1160
- server.registerTool(
1161
- import_mcp_contracts8.MCP_TOOL_NAMES.SEARCH_PAYMENTS_AI_DOCS,
1162
- {
1163
- 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
1166
- },
1167
- async (args) => {
1168
- if (!isScopeAllowed("docs:read", mcpTokenPayload)) {
1169
- return toolError("Insufficient scope.");
1170
- }
1171
- const structuredContent = runSearchPaymentsAiDocsHandler(args.query);
1172
- return {
1173
- content: [{ type: "text", text: JSON.stringify(structuredContent) }],
1174
- structuredContent
1175
- };
1176
- }
1177
- );
1178
- server.registerTool(
1179
- import_mcp_contracts8.MCP_TOOL_NAMES.CREATE_PRODUCT,
1180
- {
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
1184
- },
1185
- async (args) => {
1186
- if (!isScopeAllowed("product:create", mcpTokenPayload)) {
1187
- return toolError("Insufficient scope.");
1188
- }
1189
- const validation = runCreateProductHandlerValidation(args);
1190
- if (!validation.ok) {
1191
- return toolError(validation.errorText);
1192
- }
1193
- if (!isMerchantAllowed(validation.data.merchantId, mcpTokenPayload)) {
1194
- return toolError("Merchant not authorized for this MCP key.");
1195
- }
1196
- if (!backendClient) {
1197
- return toolError(
1198
- "The PAI Managed backend is not reachable. Contact your platform administrator to configure backend connectivity."
1199
- );
1200
- }
1201
- const result = await runCreateProductHandler(backendClient, validation.data);
1202
- if (!result.ok) {
1203
- return toolError(result.errorText);
1204
- }
1205
- return {
1206
- content: [{ type: "text", text: JSON.stringify(result.data) }],
1207
- structuredContent: result.data
1208
- };
1209
- }
1210
- );
1211
- server.registerTool(
1212
- import_mcp_contracts8.MCP_TOOL_NAMES.UPLOAD_PRODUCT_IMAGE,
1213
- {
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
1217
- },
1218
- async (args) => {
1219
- if (!isScopeAllowed("product:create", mcpTokenPayload)) {
1220
- return toolError("Insufficient scope.");
1221
- }
1222
- const validation = runUploadProductImageHandlerValidation(args);
1223
- if (!validation.ok) {
1224
- return toolError(validation.errorText);
1225
- }
1226
- if (!isMerchantAllowed(validation.data.merchantId, mcpTokenPayload)) {
1227
- return toolError("Merchant not authorized for this MCP key.");
1228
- }
1229
- if (!backendClient) {
1230
- return toolError(
1231
- "The PAI Managed backend is not reachable. Contact your platform administrator to configure backend connectivity."
1232
- );
1233
- }
1234
- const result = await runUploadProductImageHandler(backendClient, validation.data);
1235
- if (!result.ok) {
1236
- return toolError(result.errorText);
1237
- }
1238
- return {
1239
- content: [{ type: "text", text: JSON.stringify(result.data) }],
1240
- structuredContent: result.data
1241
- };
1242
- }
1243
- );
1244
- server.registerTool(
1245
- import_mcp_contracts8.MCP_TOOL_NAMES.GET_CHECKOUT_CUSTOMIZATION,
1246
- {
1247
- 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
1250
- },
1251
- async (args) => {
1252
- if (!isScopeAllowed("checkout:read", mcpTokenPayload)) {
1253
- return toolError("Insufficient scope.");
1254
- }
1255
- const validation = runGetCheckoutCustomizationValidation(args);
1256
- if (!validation.ok) {
1257
- return toolError(validation.errorText);
1258
- }
1259
- if (!isMerchantAllowed(validation.data.merchantId, mcpTokenPayload)) {
1260
- return toolError("Merchant not authorized for this MCP key.");
1261
- }
1262
- if (!backendClient) {
1263
- return toolError(
1264
- "The PAI Managed backend is not reachable. Contact your platform administrator to configure backend connectivity."
1265
- );
1266
- }
1267
- const result = await runGetCheckoutCustomizationHandler(backendClient, validation.data);
1268
- if (!result.ok) {
1269
- return toolError(result.errorText);
1270
- }
1271
- return {
1272
- content: [{ type: "text", text: JSON.stringify(result.data) }],
1273
- structuredContent: result.data
1274
- };
1275
- }
1276
- );
1277
- server.registerTool(
1278
- import_mcp_contracts8.MCP_TOOL_NAMES.UPDATE_CHECKOUT_CUSTOMIZATION,
1279
- {
1280
- 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
1283
- },
1284
- async (args) => {
1285
- if (!isScopeAllowed("checkout:write", mcpTokenPayload)) {
1286
- return toolError("Insufficient scope.");
1287
- }
1288
- const validation = runUpdateCheckoutCustomizationValidation(args);
1289
- if (!validation.ok) {
1290
- return toolError(validation.errorText);
1291
- }
1292
- if (!isMerchantAllowed(validation.data.merchantId, mcpTokenPayload)) {
1293
- return toolError("Merchant not authorized for this MCP key.");
1294
- }
1295
- if (!backendClient) {
1296
- return toolError(
1297
- "The PAI Managed backend is not reachable. Contact your platform administrator to configure backend connectivity."
1298
- );
1299
- }
1300
- const result = await runUpdateCheckoutCustomizationHandler(backendClient, validation.data);
1301
- if (!result.ok) {
1302
- return toolError(result.errorText);
1303
- }
1304
- return {
1305
- content: [{ type: "text", text: JSON.stringify(result.data) }],
1306
- structuredContent: result.data
1307
- };
1308
- }
1309
- );
1310
- }
1311
-
1312
- // src/main.ts
1313
- async function main() {
1314
- const version = readMcpServerVersion();
1315
- const server = createInstrumentedMcpServer({ version });
1316
- const backendClient = createBackendClientFromEnv();
1317
- registerTools(server, { backendClient });
1318
- const transport = new StdioServerTransport();
1319
- await server.connect(transport);
1320
- console.error(`pai-managed MCP server ready (v${version}).`);
1321
- console.error(
1322
- "Transport: stdio (JSON-RPC on process stdin/stdout). No host or TCP port \u2014 the MCP client must spawn this process."
1323
- );
1324
- }
1325
- main().catch(async (error) => {
1326
- Sentry.captureException(error);
1327
- console.error("Fatal error in MCP server:", error);
1328
- await Sentry.flush(2e3);
1329
- process.exit(1);
1330
- });