@payware/mcp-server 1.0.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 (106) hide show
  1. package/.env.example +92 -0
  2. package/MCP-SETUP.md +190 -0
  3. package/README.md +483 -0
  4. package/check-keys.js +52 -0
  5. package/keys/README.md +112 -0
  6. package/mcp-config.json +9 -0
  7. package/package.json +66 -0
  8. package/payware-mcp-config.json +12 -0
  9. package/src/auth/rsa-keys.js +82 -0
  10. package/src/auth/sandbox-setup.js +187 -0
  11. package/src/config/env.js +229 -0
  12. package/src/config/partner-types.js +143 -0
  13. package/src/core/auth/jwt-factory.js +182 -0
  14. package/src/core/auth/jwt-token.js +735 -0
  15. package/src/core/utils/json-serializer.js +100 -0
  16. package/src/core/utils/key-utils.js +133 -0
  17. package/src/index.js +237 -0
  18. package/src/isv/data/index.js +291 -0
  19. package/src/isv/deep-links/delete-product-link.js +154 -0
  20. package/src/isv/deep-links/delete-transaction-link.js +154 -0
  21. package/src/isv/deep-links/get-product-link.js +167 -0
  22. package/src/isv/deep-links/get-transaction-link.js +167 -0
  23. package/src/isv/deep-links/index.js +23 -0
  24. package/src/isv/index.js +103 -0
  25. package/src/isv/oauth2/create-token-simple.js +46 -0
  26. package/src/isv/oauth2/get-token-info.js +157 -0
  27. package/src/isv/oauth2/obtain-token.js +189 -0
  28. package/src/isv/oauth2/token-helper.js +151 -0
  29. package/src/isv/products/audios.js +470 -0
  30. package/src/isv/products/create-product.js +314 -0
  31. package/src/isv/products/delete-product.js +119 -0
  32. package/src/isv/products/get-product-image.js +247 -0
  33. package/src/isv/products/get-product.js +168 -0
  34. package/src/isv/products/index.js +65 -0
  35. package/src/isv/products/list-products.js +136 -0
  36. package/src/isv/products/schedules.js +522 -0
  37. package/src/isv/products/update-product.js +265 -0
  38. package/src/isv/transactions/cancel-transaction.js +129 -0
  39. package/src/isv/transactions/create-transaction.js +361 -0
  40. package/src/isv/transactions/get-status.js +217 -0
  41. package/src/isv/transactions/process-transaction.js +145 -0
  42. package/src/isv/transactions/simulate-callback.js +146 -0
  43. package/src/isv/transactions/transaction-history.js +186 -0
  44. package/src/isv/utils/auth.js +159 -0
  45. package/src/mcp-proxy.js +150 -0
  46. package/src/merchant/data/export-report.js +108 -0
  47. package/src/merchant/data/generate-report.js +151 -0
  48. package/src/merchant/data/get-report-status.js +120 -0
  49. package/src/merchant/data/index.js +351 -0
  50. package/src/merchant/deep-links/delete-product-link.js +121 -0
  51. package/src/merchant/deep-links/delete-transaction-link.js +121 -0
  52. package/src/merchant/deep-links/get-product-link.js +134 -0
  53. package/src/merchant/deep-links/get-transaction-link.js +134 -0
  54. package/src/merchant/deep-links/index.js +23 -0
  55. package/src/merchant/index.js +92 -0
  56. package/src/merchant/products/audios.js +606 -0
  57. package/src/merchant/products/create-product.js +288 -0
  58. package/src/merchant/products/delete-product.js +111 -0
  59. package/src/merchant/products/get-product-image.js +252 -0
  60. package/src/merchant/products/get-product.js +144 -0
  61. package/src/merchant/products/index.js +65 -0
  62. package/src/merchant/products/list-products.js +112 -0
  63. package/src/merchant/products/schedules.js +646 -0
  64. package/src/merchant/products/update-product.js +282 -0
  65. package/src/merchant/transactions/cancel-transaction.js +241 -0
  66. package/src/merchant/transactions/create-transaction.js +482 -0
  67. package/src/merchant/transactions/get-status.js +196 -0
  68. package/src/merchant/transactions/process-transaction.js +349 -0
  69. package/src/merchant/transactions/simulate-callback.js +246 -0
  70. package/src/merchant/transactions/transaction-history.js +258 -0
  71. package/src/payment-institution/data/index.js +280 -0
  72. package/src/payment-institution/deep-links/delete-transaction-link.js +128 -0
  73. package/src/payment-institution/deep-links/get-transaction-link.js +141 -0
  74. package/src/payment-institution/deep-links/index.js +17 -0
  75. package/src/payment-institution/index.js +92 -0
  76. package/src/payment-institution/transactions/create-pi-transaction.js +484 -0
  77. package/src/payment-institution/transactions/finalize-transaction.js +340 -0
  78. package/src/payment-institution/transactions/get-transaction-history.js +269 -0
  79. package/src/payment-institution/transactions/get-transaction-status.js +234 -0
  80. package/src/payment-institution/transactions/process-pi-transaction.js +421 -0
  81. package/src/payment-institution/transactions/simulate-pi-callback.js +326 -0
  82. package/src/payment-institution/transactions/soundbite-transaction.js +269 -0
  83. package/src/proxy-server.js +205 -0
  84. package/src/shared/data/data-api.js +372 -0
  85. package/src/shared/deep-links/api.js +214 -0
  86. package/src/shared/index.js +44 -0
  87. package/src/shared/products/products-api.js +872 -0
  88. package/src/tools/advanced-code-generator.js +2247 -0
  89. package/src/utils/code-examples/common/auth-examples.js +1482 -0
  90. package/src/utils/code-examples/common/helpers.js +416 -0
  91. package/src/utils/code-examples/common/utils-examples.js +591 -0
  92. package/src/utils/code-examples/frameworks/examples.js +859 -0
  93. package/src/utils/code-examples/index.js +441 -0
  94. package/src/utils/code-examples/isv/oauth2.js +600 -0
  95. package/src/utils/code-examples/isv/products.js +666 -0
  96. package/src/utils/code-examples/isv/transactions.js +813 -0
  97. package/src/utils/code-examples/merchant/products.js +497 -0
  98. package/src/utils/code-examples/merchant/transactions.js +623 -0
  99. package/src/utils/code-examples/payment-institution/transactions.js +388 -0
  100. package/src/utils/code-examples/shared/data.js +607 -0
  101. package/src/utils/code-examples/shared/deep-links.js +662 -0
  102. package/src/utils/code-examples/shared/soundbites.js +808 -0
  103. package/src/utils/code-examples.js +646 -0
  104. package/src/utils/format-request.js +363 -0
  105. package/src/utils/json-formatter.js +249 -0
  106. package/src/utils/server-info.js +132 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * JSON Consistency Utilities for payware API
3
+ *
4
+ * CORE REQUIREMENT: The exact same compact JSON string must be used for
5
+ * both JWT contentMd5 calculation and HTTP request body to prevent MD5 mismatches.
6
+ *
7
+ * SOLUTION: We use deterministic serialization (sorted keys) to guarantee
8
+ * consistent JSON output regardless of object property insertion order.
9
+ *
10
+ * This prevents subtle bugs where the same object could produce different
11
+ * JSON strings across different JavaScript environments or property orderings.
12
+ */
13
+
14
+ /**
15
+ * Create consistent compact JSON string for payware API requests
16
+ *
17
+ * CORE REQUIREMENT: payware API requires that the exact same compact JSON string
18
+ * is used for both JWT contentMd5 calculation and the HTTP request body.
19
+ *
20
+ * SOLUTION: This function uses deterministic serialization with sorted keys
21
+ * to guarantee the same output every time, preventing MD5 mismatch errors.
22
+ *
23
+ * @param {Object} obj - Object to serialize
24
+ * @returns {string} Compact JSON string with sorted keys (no whitespace)
25
+ */
26
+ export function createDeterministicJSON(obj) {
27
+ if (obj === null || obj === undefined) {
28
+ return JSON.stringify(obj);
29
+ }
30
+
31
+ if (typeof obj !== 'object' || Array.isArray(obj)) {
32
+ return JSON.stringify(obj);
33
+ }
34
+
35
+ // Sort object keys recursively to ensure deterministic output
36
+ const sortedObj = sortObjectKeys(obj);
37
+
38
+ // Use JSON.stringify with no formatting (minimized)
39
+ return JSON.stringify(sortedObj);
40
+ }
41
+
42
+ /**
43
+ * Recursively sort object keys to ensure deterministic ordering
44
+ * @param {any} obj - Object to sort
45
+ * @returns {any} Object with sorted keys
46
+ */
47
+ function sortObjectKeys(obj) {
48
+ if (obj === null || obj === undefined) {
49
+ return obj;
50
+ }
51
+
52
+ if (Array.isArray(obj)) {
53
+ return obj.map(item => sortObjectKeys(item));
54
+ }
55
+
56
+ if (typeof obj !== 'object') {
57
+ return obj;
58
+ }
59
+
60
+ const sorted = {};
61
+ const keys = Object.keys(obj).sort();
62
+
63
+ for (const key of keys) {
64
+ sorted[key] = sortObjectKeys(obj[key]);
65
+ }
66
+
67
+ return sorted;
68
+ }
69
+
70
+ /**
71
+ * Create compact JSON for payware API requests (alias for consistency)
72
+ *
73
+ * payware REQUIRES:
74
+ * 1. JSON must be compact (no extra whitespace)
75
+ * 2. The EXACT same string must be used for JWT contentMd5 and HTTP body
76
+ *
77
+ * This function ensures both requirements by using deterministic serialization.
78
+ *
79
+ * @param {Object} requestBody - Request body object
80
+ * @returns {string} Compact JSON string with sorted keys
81
+ */
82
+ export function createMinimizedJSON(requestBody) {
83
+ return createDeterministicJSON(requestBody);
84
+ }
85
+
86
+ /**
87
+ * Validate that two objects produce the same JSON string
88
+ *
89
+ * This is crucial for payware API because the JWT contentMd5 and HTTP body
90
+ * must use the exact same string. This function helps test that requirement.
91
+ *
92
+ * @param {Object} obj1 - First object
93
+ * @param {Object} obj2 - Second object
94
+ * @returns {boolean} True if both objects produce identical JSON strings
95
+ */
96
+ export function validateJSONConsistency(obj1, obj2) {
97
+ const json1 = createDeterministicJSON(obj1);
98
+ const json2 = createDeterministicJSON(obj2);
99
+ return json1 === json2;
100
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * RSA Key Utility Functions
3
+ * Handles flexible key formats for payware MCP server
4
+ */
5
+
6
+ /**
7
+ * Normalize RSA private key to proper PEM format
8
+ * Accepts keys with or without headers/footers
9
+ * Preserves the original key format (PKCS#8 vs RSA) when headers are present
10
+ * @param {string} privateKey - RSA private key in various formats
11
+ * @returns {string} Properly formatted PEM private key
12
+ */
13
+ export function normalizePrivateKey(privateKey) {
14
+ if (!privateKey) {
15
+ throw new Error('Private key is required');
16
+ }
17
+
18
+ // Remove any extra whitespace and line breaks
19
+ const cleanKey = privateKey.trim().replace(/\r/g, '');
20
+
21
+ // Check if key already has proper PEM format - preserve the original format
22
+ if (cleanKey.includes('-----BEGIN RSA PRIVATE KEY-----') || cleanKey.includes('-----BEGIN PRIVATE KEY-----')) {
23
+ // Key already has headers, return as-is (with proper line breaks)
24
+ return cleanKey.replace(/\r?\n/g, '\n');
25
+ }
26
+
27
+ // Remove any existing partial headers/footers
28
+ let keyContent = cleanKey
29
+ .replace(/-----BEGIN[^-]*-----/g, '')
30
+ .replace(/-----END[^-]*-----/g, '')
31
+ .replace(/\s+/g, '') // Remove all whitespace
32
+ .trim();
33
+
34
+ if (!keyContent) {
35
+ throw new Error('Private key content is empty after normalization');
36
+ }
37
+
38
+ // Add line breaks every 64 characters for proper PEM format
39
+ const formattedContent = keyContent.match(/.{1,64}/g)?.join('\n') || keyContent;
40
+
41
+ // Default to PKCS#8 format (more modern and widely supported)
42
+ // Use PRIVATE KEY instead of RSA PRIVATE KEY for better compatibility
43
+ return `-----BEGIN PRIVATE KEY-----
44
+ ${formattedContent}
45
+ -----END PRIVATE KEY-----`;
46
+ }
47
+
48
+ /**
49
+ * Normalize RSA public key to proper PEM format
50
+ * Accepts keys with or without headers/footers
51
+ * @param {string} publicKey - RSA public key in various formats
52
+ * @returns {string} Properly formatted PEM public key
53
+ */
54
+ export function normalizePublicKey(publicKey) {
55
+ if (!publicKey) {
56
+ throw new Error('Public key is required');
57
+ }
58
+
59
+ // Remove any extra whitespace and line breaks
60
+ const cleanKey = publicKey.trim().replace(/\r/g, '');
61
+
62
+ // Check if key already has proper PEM format
63
+ if (cleanKey.includes('-----BEGIN PUBLIC KEY-----') || cleanKey.includes('-----BEGIN RSA PUBLIC KEY-----')) {
64
+ return cleanKey;
65
+ }
66
+
67
+ // Remove any existing partial headers/footers
68
+ let keyContent = cleanKey
69
+ .replace(/-----BEGIN[^-]*-----/g, '')
70
+ .replace(/-----END[^-]*-----/g, '')
71
+ .replace(/\s+/g, '') // Remove all whitespace
72
+ .trim();
73
+
74
+ if (!keyContent) {
75
+ throw new Error('Public key content is empty after normalization');
76
+ }
77
+
78
+ // Add line breaks every 64 characters for proper PEM format
79
+ const formattedContent = keyContent.match(/.{1,64}/g)?.join('\n') || keyContent;
80
+
81
+ // Return with proper PUBLIC KEY headers
82
+ return `-----BEGIN PUBLIC KEY-----
83
+ ${formattedContent}
84
+ -----END PUBLIC KEY-----`;
85
+ }
86
+
87
+ /**
88
+ * Validate if a string looks like base64 encoded key content
89
+ * @param {string} content - Content to validate
90
+ * @returns {boolean} True if looks like valid base64 key content
91
+ */
92
+ export function isValidKeyContent(content) {
93
+ if (!content || content.length < 100) return false; // RSA keys should be much longer
94
+
95
+ // Check if it contains only valid base64 characters
96
+ const base64Regex = /^[A-Za-z0-9+/=\s]*$/;
97
+ return base64Regex.test(content);
98
+ }
99
+
100
+ /**
101
+ * Get key format information
102
+ * @param {string} key - Key to analyze
103
+ * @returns {Object} Information about the key format
104
+ */
105
+ export function getKeyInfo(key) {
106
+ const cleanKey = key.trim();
107
+
108
+ const info = {
109
+ hasHeaders: false,
110
+ isPrivate: false,
111
+ isPublic: false,
112
+ format: 'unknown',
113
+ length: key.length
114
+ };
115
+
116
+ // Check for headers
117
+ if (cleanKey.includes('-----BEGIN') && cleanKey.includes('-----END')) {
118
+ info.hasHeaders = true;
119
+ }
120
+
121
+ // Check key type
122
+ if (cleanKey.includes('PRIVATE KEY')) {
123
+ info.isPrivate = true;
124
+ info.format = cleanKey.includes('RSA PRIVATE KEY') ? 'rsa-private' : 'pkcs8-private';
125
+ } else if (cleanKey.includes('PUBLIC KEY')) {
126
+ info.isPublic = true;
127
+ info.format = cleanKey.includes('RSA PUBLIC KEY') ? 'rsa-public' : 'pkcs8-public';
128
+ } else if (isValidKeyContent(cleanKey)) {
129
+ info.format = 'base64-content';
130
+ }
131
+
132
+ return info;
133
+ }
package/src/index.js ADDED
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * payware MCP Server with Partner Type Support
5
+ * Dynamically loads tools based on partner type (merchant, isv, payment_institution)
6
+ * Provides authentication and transaction tools for payware API integration
7
+ */
8
+
9
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
10
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
11
+ import {
12
+ CallToolRequestSchema,
13
+ ListToolsRequestSchema,
14
+ ErrorCode,
15
+ McpError
16
+ } from '@modelcontextprotocol/sdk/types.js';
17
+
18
+ // Import partner type and configuration
19
+ import { getPartnerTypeSafe, getPartnerType } from './config/env.js';
20
+ import { getPartnerConfig, getPartnerDisplayInfo } from './config/partner-types.js';
21
+
22
+ // Import tool registries
23
+ import { getSharedTools } from './shared/index.js';
24
+ import { getMerchantTools } from './merchant/index.js';
25
+ import { getISVTools } from './isv/index.js';
26
+ import { getPaymentInstitutionTools } from './payment-institution/index.js';
27
+
28
+ class PaywareMCPServer {
29
+ constructor() {
30
+ this.partnerType = getPartnerTypeSafe();
31
+ this.partnerConfig = getPartnerConfig(this.partnerType);
32
+ this.partnerInfo = getPartnerDisplayInfo(this.partnerType);
33
+
34
+ this.server = new Server(
35
+ {
36
+ name: `payware-mcp-server-${this.partnerType}`,
37
+ version: '1.0.0',
38
+ },
39
+ {
40
+ capabilities: {
41
+ tools: {},
42
+ },
43
+ }
44
+ );
45
+
46
+ this.tools = this.loadPartnerTools();
47
+ this.setupToolHandlers();
48
+ this.setupErrorHandling();
49
+ }
50
+
51
+ /**
52
+ * Load tools based on partner type
53
+ * @returns {Array} Combined array of shared and partner-specific tools
54
+ */
55
+ loadPartnerTools() {
56
+ let allTools = [];
57
+
58
+ switch (this.partnerType) {
59
+ case 'merchant':
60
+ allTools = getMerchantTools();
61
+ break;
62
+ case 'isv':
63
+ allTools = getISVTools();
64
+ break;
65
+ case 'payment_institution':
66
+ allTools = getPaymentInstitutionTools();
67
+ break;
68
+ default:
69
+ // Send warning to stderr to avoid MCP protocol corruption
70
+ process.stderr.write(`Warning: Unknown partner type: ${this.partnerType}, defaulting to merchant tools\n`);
71
+ allTools = getMerchantTools();
72
+ }
73
+
74
+ // Only log in development mode to avoid interfering with MCP transport
75
+ // Debugging disabled to prevent MCP JSON-RPC protocol corruption
76
+ // These logs interfere with the MCP client communication
77
+ // console.log messages on stdout break the JSON-RPC protocol
78
+ return allTools;
79
+ }
80
+
81
+ setupToolHandlers() {
82
+ // MCP protocol requires clean JSON-RPC communication over stdio
83
+ // Any console.log to stdout will corrupt the protocol and cause
84
+ // "Unexpected end of JSON input" and "not valid JSON" errors
85
+ // All debugging output has been permanently disabled
86
+
87
+ // List tools handler
88
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
89
+ return {
90
+ tools: this.tools.map(tool => ({
91
+ name: tool.name,
92
+ description: `[${this.partnerInfo.name}] ${tool.description}`,
93
+ inputSchema: tool.inputSchema
94
+ }))
95
+ };
96
+ });
97
+
98
+ // Call tool handler
99
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
100
+ const { name, arguments: args } = request.params;
101
+ const timestamp = new Date().toISOString();
102
+
103
+ // Find the tool by name
104
+ const tool = this.tools.find(t => t.name === name);
105
+
106
+ if (!tool) {
107
+ throw new McpError(
108
+ ErrorCode.MethodNotFound,
109
+ `Unknown tool: ${name}`
110
+ );
111
+ }
112
+
113
+ try {
114
+ // Call the tool's handler with execution metadata
115
+ const startTime = Date.now();
116
+ const result = await tool.handler(args || {});
117
+ const duration = Date.now() - startTime;
118
+
119
+ // Add execution metadata to response
120
+ if (result && result.content && Array.isArray(result.content)) {
121
+ result.content.forEach(content => {
122
+ if (content.type === 'text') {
123
+ content.text += `\n\n---\n**Execution Info:**\n- Partner Type: ${this.partnerInfo.name}\n- Tool: ${name}\n- Executed: ${timestamp}\n- Duration: ${duration}ms\n- Process ID: ${process.pid}\n- Status: ✅ Success`;
124
+ }
125
+ });
126
+ }
127
+
128
+ return result;
129
+ } catch (error) {
130
+ throw new McpError(
131
+ ErrorCode.InternalError,
132
+ `Error executing tool ${name}: ${error.message}`
133
+ );
134
+ }
135
+ });
136
+ }
137
+
138
+ setupErrorHandling() {
139
+ this.server.onerror = (error) => {
140
+ // MCP server error - will be handled by client
141
+ // Don't output anything to stdout to avoid protocol corruption
142
+ };
143
+
144
+ process.on('SIGINT', async () => {
145
+ // Graceful shutdown without any console output
146
+ try {
147
+ await this.server.close();
148
+ } catch (error) {
149
+ // Ignore shutdown errors to prevent console output
150
+ }
151
+ process.exit(0);
152
+ });
153
+
154
+ process.on('SIGTERM', async () => {
155
+ // Handle SIGTERM gracefully without console output
156
+ try {
157
+ await this.server.close();
158
+ } catch (error) {
159
+ // Ignore shutdown errors to prevent console output
160
+ }
161
+ process.exit(0);
162
+ });
163
+
164
+ // Prevent any unhandled promise rejections from outputting to console
165
+ process.on('unhandledRejection', (reason, promise) => {
166
+ // Send to stderr to avoid MCP protocol corruption
167
+ process.stderr.write(`Unhandled promise rejection: ${reason}\n`);
168
+ });
169
+ }
170
+
171
+ async run() {
172
+ const transport = new StdioServerTransport();
173
+ await this.server.connect(transport);
174
+ // MCP server ready - no console output needed
175
+ }
176
+ }
177
+
178
+ // COMPREHENSIVE stdout protection for MCP protocol
179
+ // Hijack ALL console methods that could output to stdout
180
+ const originalConsole = {
181
+ log: console.log,
182
+ info: console.info,
183
+ warn: console.warn,
184
+ error: console.error,
185
+ debug: console.debug,
186
+ trace: console.trace
187
+ };
188
+
189
+ // Redirect all console output to stderr to prevent MCP protocol corruption
190
+ console.log = (...args) => process.stderr.write(`[LOG] ${args.join(' ')}\n`);
191
+ console.info = (...args) => process.stderr.write(`[INFO] ${args.join(' ')}\n`);
192
+ console.warn = (...args) => process.stderr.write(`[WARN] ${args.join(' ')}\n`);
193
+ console.error = (...args) => process.stderr.write(`[ERROR] ${args.join(' ')}\n`);
194
+ console.debug = (...args) => process.stderr.write(`[DEBUG] ${args.join(' ')}\n`);
195
+ console.trace = (...args) => process.stderr.write(`[TRACE] ${args.join(' ')}\n`);
196
+
197
+ // Hijack process.stdout.write to catch any direct writes
198
+ const originalStdoutWrite = process.stdout.write;
199
+ process.stdout.write = function(chunk, encoding, callback) {
200
+ // Only allow JSON-RPC messages through (they should start with { or be buffers)
201
+ const str = chunk.toString();
202
+
203
+ // Allow JSON-RPC messages (start with { or [, or are empty/whitespace)
204
+ if (str.startsWith('{') || str.startsWith('[') || str.trim() === '') {
205
+ return originalStdoutWrite.call(this, chunk, encoding, callback);
206
+ }
207
+
208
+ // Block anything that looks like text messages (especially shutdown messages)
209
+ if (str.toLowerCase().includes('shutting') ||
210
+ str.toLowerCase().includes('closing') ||
211
+ str.toLowerCase().includes('stopping') ||
212
+ str.toLowerCase().includes('exit')) {
213
+ process.stderr.write(`[MCP STDOUT BLOCKED] ${str}`);
214
+ if (callback) callback();
215
+ return true;
216
+ }
217
+
218
+ // Block any other non-JSON content
219
+ process.stderr.write(`[STDOUT BLOCKED] ${str}`);
220
+ if (callback) callback();
221
+ return true;
222
+ };
223
+
224
+ // Also override process exit handlers to be completely silent
225
+ const originalExit = process.exit;
226
+ process.exit = function(code) {
227
+ // Exit silently without any output
228
+ originalExit.call(this, code);
229
+ };
230
+
231
+ // Start the server
232
+ const server = new PaywareMCPServer();
233
+ server.run().catch(error => {
234
+ // Send error to stderr (not stdout) to avoid MCP protocol corruption
235
+ process.stderr.write(`Failed to start payware MCP Server: ${error.message}\n`);
236
+ process.exit(1);
237
+ });