@payware/mcp-server 1.2.0 → 1.3.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 (58) hide show
  1. package/README.md +5 -0
  2. package/package.json +69 -66
  3. package/src/auth/sandbox-setup.js +186 -186
  4. package/src/isv/index.js +29 -1
  5. package/src/isv/invitations/index.js +471 -0
  6. package/src/isv/oauth2/obtain-token.js +19 -8
  7. package/src/isv/oauth2/token-helper.js +5 -2
  8. package/src/isv/poi/cancel-price.js +2 -10
  9. package/src/isv/poi/create.js +19 -0
  10. package/src/isv/poi/get-poi.js +2 -10
  11. package/src/isv/poi/get-qrcode.js +2 -10
  12. package/src/isv/poi/get-status.js +2 -10
  13. package/src/isv/poi/index.js +5 -0
  14. package/src/isv/poi/list-pois.js +2 -10
  15. package/src/isv/poi/set-price.js +217 -225
  16. package/src/isv/products/create-product.js +313 -313
  17. package/src/isv/products/update-product.js +264 -264
  18. package/src/isv/reference/index.js +249 -0
  19. package/src/isv/shops/index.js +22 -0
  20. package/src/isv/transactions/cancel-transaction.js +41 -10
  21. package/src/isv/transactions/create-transaction.js +20 -4
  22. package/src/isv/transactions/get-status.js +18 -11
  23. package/src/isv/transactions/process-transaction.js +160 -42
  24. package/src/isv/transactions/simulate-callback.js +128 -109
  25. package/src/isv/transactions/transaction-history.js +86 -90
  26. package/src/merchant/data/generate-report.js +19 -1
  27. package/src/merchant/index.js +21 -1
  28. package/src/merchant/logs/index.js +366 -0
  29. package/src/merchant/products/create-product.js +287 -287
  30. package/src/merchant/products/update-product.js +281 -281
  31. package/src/merchant/shops/index.js +18 -0
  32. package/src/merchant/transactions/cancel-transaction.js +10 -13
  33. package/src/merchant/transactions/create-transaction.js +88 -8
  34. package/src/merchant/transactions/get-status.js +40 -11
  35. package/src/merchant/transactions/process-transaction.js +2 -10
  36. package/src/merchant/transactions/simulate-callback.js +68 -9
  37. package/src/merchant/transactions/transaction-history.js +275 -257
  38. package/src/payment-institution/transactions/create-pi-transaction.js +2 -10
  39. package/src/payment-institution/transactions/finalize-transaction.js +333 -340
  40. package/src/payment-institution/transactions/get-transaction-history.js +2 -10
  41. package/src/payment-institution/transactions/get-transaction-status.js +38 -11
  42. package/src/payment-institution/transactions/process-pi-transaction.js +2 -10
  43. package/src/payment-institution/transactions/simulate-pi-callback.js +49 -9
  44. package/src/payment-institution/transactions/soundbite-transaction.js +2 -10
  45. package/src/shared/api-errors.js +188 -0
  46. package/src/shared/auth-headers.js +62 -0
  47. package/src/shared/data/data-api.js +11 -10
  48. package/src/shared/deep-links/api.js +5 -4
  49. package/src/shared/logs/logs-api.js +242 -0
  50. package/src/shared/poi/poi-create-api.js +419 -0
  51. package/src/shared/products/products-api.js +17 -16
  52. package/src/shared/shops/shops-api.js +155 -0
  53. package/src/tools/advanced-code-generator.js +2255 -2246
  54. package/src/utils/code-examples/common/auth-examples.js +1484 -1481
  55. package/src/utils/code-examples/common/helpers.js +7 -4
  56. package/src/utils/code-examples/isv/oauth2.js +458 -600
  57. package/src/utils/code-examples/isv/transactions.js +815 -812
  58. package/MCP-SETUP.md +0 -190
@@ -1,22 +1,35 @@
1
1
  import axios from 'axios';
2
2
  import { createJWTForPartner } from '../../core/auth/jwt-factory.js';
3
3
  import { getSandboxUrl, getProductionUrl, getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
4
+ import { describeApiError } from '../../shared/api-errors.js';
4
5
 
5
6
  /**
6
- * Get transaction history from payware API as an ISV
7
+ * Get one completed transaction as an ISV acting on behalf of a merchant.
8
+ *
9
+ * **This used to be a list-and-filter tool, and there is nothing on the server to list.**
10
+ * It built `GET /transactions-history?limit=50&offset=0&status=...&from=...&to=...` and read
11
+ * `result.transactions` / `result.totalCount` off the response. `TransactionHistoryController` maps
12
+ * exactly one route - `GET /api/transactions-history/{transactionId}` - so the bare path matched no
13
+ * handler and the call answered 404 every time, whatever the filters said. The filters, the
14
+ * pagination and the result shape were all describing an endpoint that has never existed.
15
+ *
16
+ * It now does what the merchant tool does and what the API offers: fetch one transaction by id.
7
17
  */
8
18
  export async function getTransactionHistory({
9
19
  merchantPartnerId,
10
20
  oauth2Token,
11
- partnerId,
12
21
  transactionId,
13
- limit = 50,
14
- offset = 0,
15
- status,
16
- from,
17
- to,
18
22
  useSandbox = true
19
23
  }) {
24
+ if (!transactionId) {
25
+ throw new Error(
26
+ 'transactionId is required. The payware API has no endpoint that lists or filters transaction ' +
27
+ 'history - GET /transactions-history/{transactionId} fetches one transaction by id, and that ' +
28
+ 'is the whole surface. Keep your own record of the ids you created, or read them from the ' +
29
+ 'callbacks you received.'
30
+ );
31
+ }
32
+
20
33
  if (!merchantPartnerId) {
21
34
  throw new Error('Merchant Partner ID is required for ISV operations');
22
35
  }
@@ -28,6 +41,7 @@ export async function getTransactionHistory({
28
41
  const isvPartnerId = getPartnerIdSafe();
29
42
  const privateKey = getPrivateKeySafe(useSandbox);
30
43
 
44
+ // GET, so no body and no content hash.
31
45
  const jwtData = await createJWTForPartner({
32
46
  partnerId: isvPartnerId,
33
47
  privateKey,
@@ -36,19 +50,9 @@ export async function getTransactionHistory({
36
50
  oauth2Token
37
51
  });
38
52
 
39
- const params = new URLSearchParams();
40
- if (partnerId) params.append('partnerId', partnerId);
41
- if (transactionId) params.append('transactionId', transactionId);
42
- if (limit) params.append('limit', limit.toString());
43
- if (offset) params.append('offset', offset.toString());
44
- if (status) params.append('status', status);
45
- if (from) params.append('from', from);
46
- if (to) params.append('to', to);
47
-
48
53
  try {
49
54
  const baseUrl = useSandbox ? getSandboxUrl() : getProductionUrl();
50
- const url = `${baseUrl}/transactions-history?${params.toString()}`;
51
- const response = await axios.get(url, {
55
+ const response = await axios.get(`${baseUrl}/transactions-history/${transactionId}`, {
52
56
  headers: {
53
57
  'Authorization': `Bearer ${jwtData.token}`,
54
58
  'Content-Type': 'application/json',
@@ -58,62 +62,50 @@ export async function getTransactionHistory({
58
62
 
59
63
  return response.data;
60
64
  } catch (error) {
61
- if (error.response) {
62
- throw new Error(`payware API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
63
- }
64
- throw error;
65
+ throw describeApiError(error, 'call the payware API');
65
66
  }
66
67
  }
67
68
 
68
69
  export const getTransactionHistoryTool = {
69
70
  name: 'payware_operations_get_transaction_history',
70
- description: `Get transaction history as an ISV on behalf of a merchant.
71
+ description: `Get one completed or expired transaction as an ISV on behalf of a merchant.
72
+
73
+ **Endpoint:** GET /transactions-history/{transactionId}
74
+
75
+ Returns a transaction in a final state: CONFIRMED, DECLINED, FAILED, EXPIRED or CANCELLED. For a
76
+ transaction still ACTIVE, use \`payware_operations_get_transaction_status\` instead - the two
77
+ endpoints cover disjoint sets of transactions and neither falls back to the other.
78
+
79
+ ⚠️ **There is no way to list or search transaction history.** This endpoint takes an id and returns
80
+ that one transaction. There is no pagination, no date range and no status filter anywhere in the
81
+ payware API - so keep your own record of the transaction ids you created, or read them from the
82
+ callbacks you received. (This tool previously advertised \`limit\`, \`offset\`, \`status\`, \`from\` and
83
+ \`to\` parameters; they described an endpoint that does not exist and every call 404'd.)
84
+
85
+ 📦 **This is the only source of the delivery address.** For a SHIPPABLE transaction,
86
+ \`deliveryAddress\` is returned here and nowhere else - it was removed from the finalized callback on
87
+ 2026-08-07, because a callback goes to whatever certificate the merchant's endpoint presents and a
88
+ shopper's home address does not belong on that channel. The callback tells you the shipment is
89
+ ready; this call gives you the address.
71
90
 
72
- Retrieves both active and completed transactions with optional filtering.
73
- Requires ISV authentication with merchant partner ID and OAuth2 token.`,
91
+ ⚠️ **The MERCHANT's plan governs, and this endpoint is now gated.** It was ungated until 2026-08-21
92
+ and now requires Standard or Premium, so acting for a **Basic** merchant answers **403**.`,
74
93
 
75
94
  inputSchema: {
76
95
  type: 'object',
77
- required: ['merchantPartnerId', 'oauth2Token'],
96
+ required: ['transactionId', 'merchantPartnerId', 'oauth2Token'],
78
97
  properties: {
79
- merchantPartnerId: {
80
- type: 'string',
81
- description: 'Partner ID of the target merchant'
82
- },
83
- oauth2Token: {
84
- type: 'string',
85
- description: 'OAuth2 access token obtained from the merchant'
86
- },
87
- partnerId: {
88
- type: 'string',
89
- description: 'Filter by partner ID (optional)'
90
- },
91
98
  transactionId: {
92
99
  type: 'string',
93
- description: 'Filter by specific transaction ID (optional)'
94
- },
95
- limit: {
96
- type: 'number',
97
- default: 50,
98
- description: 'Maximum number of results to return'
99
- },
100
- offset: {
101
- type: 'number',
102
- default: 0,
103
- description: 'Number of results to skip for pagination'
100
+ description: 'The transaction to fetch, e.g. "pw7e4rCToG". Required - there is no list or search endpoint.'
104
101
  },
105
- status: {
106
- type: 'string',
107
- enum: ['ACTIVE', 'CONFIRMED', 'DECLINED', 'FAILED', 'EXPIRED', 'CANCELLED'],
108
- description: 'Filter by transaction status'
109
- },
110
- from: {
102
+ merchantPartnerId: {
111
103
  type: 'string',
112
- description: 'Start date for filtering (YYYY-MM-DD format)'
104
+ description: 'Partner ID of the target merchant (8 alphanumeric characters)'
113
105
  },
114
- to: {
106
+ oauth2Token: {
115
107
  type: 'string',
116
- description: 'End date for filtering (YYYY-MM-DD format)'
108
+ description: 'OAuth2 access token obtained from the merchant'
117
109
  },
118
110
  useSandbox: {
119
111
  type: 'boolean',
@@ -125,46 +117,46 @@ Requires ISV authentication with merchant partner ID and OAuth2 token.`,
125
117
 
126
118
  async handler(params) {
127
119
  try {
128
- const result = await getTransactionHistory(params);
129
-
130
- const transactions = result.transactions || [];
131
- const totalCount = result.totalCount || transactions.length;
132
-
133
- const formatTransaction = (tx) => {
134
- const statusEmojis = {
135
- 'ACTIVE': '⏳',
136
- 'CONFIRMED': '✅',
137
- 'DECLINED': '❌',
138
- 'FAILED': '⚠️',
139
- 'EXPIRED': '⏰',
140
- 'CANCELLED': '🚫'
141
- };
142
- const emoji = statusEmojis[tx.status] || '❓';
143
- const paymentInfo = tx.paymentMethod ? ` [${tx.paymentMethod}]` : '';
144
- return ` ${emoji} **${tx.id}** - ${tx.trData?.amount || '0.00'} ${tx.trData?.currency || 'EUR'}${paymentInfo} - ${tx.status} - ${tx.createdAt || 'N/A'}`;
120
+ const tx = await getTransactionHistory(params);
121
+
122
+ const statusEmojis = {
123
+ CONFIRMED: '✅',
124
+ DECLINED: '❌',
125
+ FAILED: '⚠️',
126
+ EXPIRED: '⏰',
127
+ CANCELLED: '🚫'
145
128
  };
129
+ const emoji = statusEmojis[tx.status] || '❓';
130
+
131
+ const delivery = tx.deliveryAddress ? `
132
+
133
+ 📦 **Delivery Address** (SHIPPABLE)
134
+ - Name: ${tx.deliveryAddress.fullName || 'N/A'}
135
+ - Address: ${tx.deliveryAddress.streetAddressLine1 || 'N/A'}${tx.deliveryAddress.streetAddressLine2 ? `, ${tx.deliveryAddress.streetAddressLine2}` : ''}
136
+ - City: ${tx.deliveryAddress.city || 'N/A'}, ${tx.deliveryAddress.zipCode || 'N/A'}
137
+ - Region: ${tx.deliveryAddress.region || 'N/A'}
138
+ - Country: ${tx.deliveryAddress.country || 'N/A'}
139
+ - Phone: ${tx.deliveryAddress.phoneNumber || 'N/A'}
140
+ - Email: ${tx.deliveryAddress.email || 'N/A'}` : '';
146
141
 
147
142
  return {
148
143
  content: [{
149
144
  type: 'text',
150
- text: `📈 **Transaction History Retrieved (ISV -> Merchant: ${params.merchantPartnerId})**
145
+ text: `${emoji} **Transaction ${tx.transactionId || params.transactionId}** - ${tx.status}
151
146
 
152
- **ISV Integration:**
153
- - ISV Partner: ${getPartnerIdSafe()}
154
- - Target Merchant: ${params.merchantPartnerId}
155
- - OAuth2 Token: ${params.oauth2Token.substring(0, 8)}...
147
+ **ISV -> Merchant:** ${getPartnerIdSafe()} -> ${params.merchantPartnerId}
156
148
 
157
- **Results:** ${transactions.length} of ${totalCount} transactions
158
-
159
- **Transactions:**
160
- ${transactions.length > 0 ? transactions.map(formatTransaction).join('\n') : ' No transactions found'}
161
-
162
- **Applied Filters:**
163
- ${params.status ? `- Status: ${params.status}\n` : ''}${params.from ? `- From: ${params.from}\n` : ''}${params.to ? `- To: ${params.to}\n` : ''}${params.limit ? `- Limit: ${params.limit}\n` : ''}${params.offset ? `- Offset: ${params.offset}\n` : ''}
149
+ - Amount: ${tx.amount ?? 'N/A'} ${tx.currency ?? ''}
150
+ - Fee: ${tx.fee ?? '(not reported - absence means unknown, not zero)'}
151
+ - Type: ${tx.transactionType ?? 'N/A'}
152
+ - Payment method: ${tx.paymentMethod ?? 'N/A'}
153
+ - Status message: ${tx.statusMessage ?? 'N/A'}
154
+ - Created: ${tx.created ?? 'N/A'}
155
+ - Finalized: ${tx.finalized ?? '(absent - EXPIRED transactions carry no finalization moment)'}${delivery}
164
156
 
165
157
  **Raw Response:**
166
158
  \`\`\`json
167
- ${JSON.stringify(result, null, 2)}
159
+ ${JSON.stringify(tx, null, 2)}
168
160
  \`\`\``
169
161
  }]
170
162
  };
@@ -179,9 +171,13 @@ ${JSON.stringify(result, null, 2)}
179
171
  **ISV Authentication:**
180
172
  - Merchant Partner ID: ${params.merchantPartnerId}
181
173
  - OAuth2 Token: ${params.oauth2Token ? 'Provided' : 'Missing'}
182
- - ISV Partner ID: ${getPartnerIdSafe()}`
174
+ - ISV Partner ID: ${getPartnerIdSafe()}
175
+
176
+ **Common causes:**
177
+ - 404: the transaction is still ACTIVE (use \`payware_operations_get_transaction_status\`), or the id is wrong.
178
+ - 403: the acting merchant is on the Basic plan, which does not include transaction history.`
183
179
  }]
184
180
  };
185
181
  }
186
182
  }
187
- };
183
+ };
@@ -6,7 +6,25 @@ import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
6
6
  */
7
7
  export const generateReportTool = {
8
8
  name: "payware_data_generate_report",
9
- description: "Generate an asynchronous data report for merchant data analysis and reporting. Production only - not supported in sandbox.",
9
+ description: `Generate an asynchronous data report for merchant data analysis and reporting. Production only - not supported in sandbox.
10
+
11
+ ⚠️ **Running a report is entitlement-checked as of 2026-08-22.** Naming a report answers **403
12
+ \`ERR_REPORT_NOT_AVAILABLE\`** when that report is not included in the caller's plan or partner type.
13
+ Until that date only the *menu* was filtered - the door was not - so a caller that hard-coded a
14
+ report id it had never been offered could still run it, and may now start failing.
15
+
16
+ **Always resolve the id from \`payware_data_list_reports\` rather than hard-coding one.** That call
17
+ and this check share a single rule, so the menu and the door cannot drift apart: if a report is in
18
+ that list, this call will accept it.
19
+
20
+ Distinguish the two failures:
21
+ - **403 \`ERR_REPORT_NOT_AVAILABLE\`** - the report exists, this caller may not run it. Not retryable;
22
+ it is a plan boundary, not a transient error.
23
+ - **404 \`ERR_REPORT_UNIT_ID_NOT_FOUND\`** - no report is registered under that id at all. Usually a
24
+ typo or a stale id.
25
+
26
+ For an ISV acting on behalf of a merchant, the plan that applies is the **merchant's**, and reports
27
+ are outside the Basic plan entirely.`,
10
28
  inputSchema: {
11
29
  type: "object",
12
30
  properties: {
@@ -20,6 +20,12 @@ import { merchantProductTools } from './products/index.js';
20
20
  // Deep links tools
21
21
  import { merchantDeepLinksTools } from './deep-links/index.js';
22
22
 
23
+ // Logs tools (Premium feature)
24
+ import { merchantLogsTools } from './logs/index.js';
25
+
26
+ // Shop tools - needed before any call that takes a shop / shopCode
27
+ import { merchantShopTools } from './shops/index.js';
28
+
23
29
  // Shared utility tools
24
30
  import { getServerInfoTool } from '../utils/server-info.js';
25
31
 
@@ -48,7 +54,13 @@ export const merchantTools = [
48
54
  ...merchantProductTools,
49
55
 
50
56
  // Deep links management
51
- ...merchantDeepLinksTools
57
+ ...merchantDeepLinksTools,
58
+
59
+ // Logs management (Premium feature)
60
+ ...merchantLogsTools,
61
+
62
+ // Shop lookup
63
+ ...merchantShopTools
52
64
  ];
53
65
 
54
66
  /**
@@ -83,6 +95,14 @@ export function getMerchantTools() {
83
95
  else if (tool.name.startsWith('payware_deep_links_')) {
84
96
  newName = tool.name; // Keep deep_links grouping
85
97
  }
98
+ // Logs tools keep their logs grouping
99
+ else if (tool.name.startsWith('payware_logs_')) {
100
+ newName = tool.name; // Keep logs grouping
101
+ }
102
+ // Shop tools keep their shops grouping
103
+ else if (tool.name.startsWith('payware_shops_')) {
104
+ newName = tool.name; // Keep shops grouping
105
+ }
86
106
 
87
107
  return {
88
108
  ...tool,
@@ -0,0 +1,366 @@
1
+ /**
2
+ * Merchant Logs Tools - Premium Feature
3
+ * Tools for querying and downloading application logs.
4
+ * Requires Premium (PRM) plan subscription.
5
+ */
6
+
7
+ import {
8
+ queryLogs,
9
+ getLogStatus,
10
+ getLogDownloadUrl,
11
+ downloadLogs,
12
+ LOG_LEVELS
13
+ } from '../../shared/logs/logs-api.js';
14
+ import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
15
+
16
+ /**
17
+ * Query logs tool for merchants
18
+ */
19
+ export const queryLogsTool = {
20
+ name: "payware_logs_query",
21
+ description: "Query application logs with filters. Premium plan required. Returns paginated JSON log entries for debugging and monitoring your integration.",
22
+ inputSchema: {
23
+ type: "object",
24
+ properties: {
25
+ from: {
26
+ type: "string",
27
+ description: "Start timestamp in ISO 8601 format (e.g., '2024-01-15T00:00:00Z'). Defaults to 24 hours ago."
28
+ },
29
+ to: {
30
+ type: "string",
31
+ description: "End timestamp in ISO 8601 format (e.g., '2024-01-15T23:59:59Z'). Defaults to now."
32
+ },
33
+ level: {
34
+ type: "string",
35
+ enum: ["ERROR", "WARN", "INFO", "DEBUG"],
36
+ description: "Filter by log level. Use ERROR for errors only, WARN for warnings and above, etc."
37
+ },
38
+ logger: {
39
+ type: "string",
40
+ description: "Filter by logger name (partial match). E.g., 'TransactionController' to see transaction-related logs."
41
+ },
42
+ search: {
43
+ type: "string",
44
+ description: "Search text in log messages. Case-insensitive partial match."
45
+ },
46
+ limit: {
47
+ type: "number",
48
+ description: "Maximum records to return (default 1000, max 10000).",
49
+ default: 1000
50
+ },
51
+ cursor: {
52
+ type: "string",
53
+ description: "Pagination cursor from previous response for fetching next page."
54
+ },
55
+ partnerId: {
56
+ type: "string",
57
+ description: "Partner ID from payware dashboard. Uses PAYWARE_PARTNER_ID env var as default.",
58
+ default: getPartnerIdSafe()
59
+ },
60
+ privateKey: {
61
+ type: "string",
62
+ description: "RSA private key for authentication. Uses environment-specific private key as default."
63
+ }
64
+ },
65
+ required: []
66
+ },
67
+
68
+ async handler(args) {
69
+ const {
70
+ from,
71
+ to,
72
+ level,
73
+ logger,
74
+ search,
75
+ limit = 1000,
76
+ cursor,
77
+ partnerId = getPartnerIdSafe(),
78
+ privateKey = getPrivateKeySafe()
79
+ } = args;
80
+
81
+ if (!partnerId || !privateKey) {
82
+ throw new Error("Partner ID and private key are required. Set PAYWARE_PARTNER_ID and environment-specific private key variables or provide them as parameters.");
83
+ }
84
+
85
+ try {
86
+ const result = await queryLogs({
87
+ from,
88
+ to,
89
+ level,
90
+ logger,
91
+ search,
92
+ limit,
93
+ cursor,
94
+ partnerType: 'merchant',
95
+ partnerId,
96
+ privateKey
97
+ });
98
+
99
+ const data = result.data;
100
+ const entries = data.entries || [];
101
+ const hasMore = data.hasMore || false;
102
+ const nextCursor = data.nextCursor;
103
+
104
+ // Format log entries for display
105
+ let logsPreview = '';
106
+ if (entries.length > 0) {
107
+ const displayEntries = entries.slice(0, 10); // Show first 10
108
+ logsPreview = displayEntries.map(log => {
109
+ const timestamp = log['@timestamp'] || log.timestamp || 'N/A';
110
+ const logLevel = log.level || 'N/A';
111
+ const loggerName = log.logger_name || log.loggerName || 'N/A';
112
+ const message = log.message || 'N/A';
113
+ const shortMessage = message.length > 100 ? message.substring(0, 100) + '...' : message;
114
+ return `[${timestamp}] ${logLevel} ${loggerName}\n ${shortMessage}`;
115
+ }).join('\n\n');
116
+
117
+ if (entries.length > 10) {
118
+ logsPreview += `\n\n... and ${entries.length - 10} more entries`;
119
+ }
120
+ } else {
121
+ logsPreview = 'No log entries found matching the criteria.';
122
+ }
123
+
124
+ // Build filter summary
125
+ const filters = [];
126
+ if (from) filters.push(`from: ${from}`);
127
+ if (to) filters.push(`to: ${to}`);
128
+ if (level) filters.push(`level: ${level}`);
129
+ if (logger) filters.push(`logger: ${logger}`);
130
+ if (search) filters.push(`search: "${search}"`);
131
+ const filterSummary = filters.length > 0 ? filters.join(', ') : 'none (last 24 hours)';
132
+
133
+ return {
134
+ content: [{
135
+ type: "text",
136
+ text: `📋 **Log Query Results**
137
+
138
+ **Query Summary:**
139
+ - Total Entries: ${data.totalCount || entries.length}
140
+ - Returned: ${data.returnedCount || entries.length}
141
+ - Has More: ${hasMore ? 'Yes' : 'No'}
142
+ - Filters: ${filterSummary}
143
+
144
+ **Log Entries Preview:**
145
+ \`\`\`
146
+ ${logsPreview}
147
+ \`\`\`
148
+
149
+ ${hasMore ? `**Pagination:**
150
+ Use cursor \`${nextCursor}\` to fetch the next page of results.` : ''}
151
+
152
+ **Log Levels:**
153
+ - 🔴 **ERROR**: Application errors and exceptions
154
+ - 🟠 **WARN**: Warnings that may need attention
155
+ - 🟢 **INFO**: Informational messages
156
+ - 🔵 **DEBUG**: Detailed debugging information
157
+
158
+ **Tips:**
159
+ - Use \`level: "ERROR"\` to quickly find errors
160
+ - Use \`search\` to find specific transaction IDs or error messages
161
+ - Use \`logger\` filter to focus on specific components
162
+
163
+ ⚠️ **Note:** This is a Premium (PRM) plan feature.`
164
+ }]
165
+ };
166
+ } catch (error) {
167
+ throw new Error(`Failed to query logs: ${error.message}`);
168
+ }
169
+ }
170
+ };
171
+
172
+ /**
173
+ * Get log status tool for merchants
174
+ */
175
+ export const getLogStatusTool = {
176
+ name: "payware_logs_status",
177
+ description: "Get log availability and storage information. Premium plan required. Shows if logs are available, total size, and file count.",
178
+ inputSchema: {
179
+ type: "object",
180
+ properties: {
181
+ partnerId: {
182
+ type: "string",
183
+ description: "Partner ID from payware dashboard. Uses PAYWARE_PARTNER_ID env var as default.",
184
+ default: getPartnerIdSafe()
185
+ },
186
+ privateKey: {
187
+ type: "string",
188
+ description: "RSA private key for authentication. Uses environment-specific private key as default."
189
+ }
190
+ },
191
+ required: []
192
+ },
193
+
194
+ async handler(args) {
195
+ const {
196
+ partnerId = getPartnerIdSafe(),
197
+ privateKey = getPrivateKeySafe()
198
+ } = args;
199
+
200
+ if (!partnerId || !privateKey) {
201
+ throw new Error("Partner ID and private key are required. Set PAYWARE_PARTNER_ID and environment-specific private key variables or provide them as parameters.");
202
+ }
203
+
204
+ try {
205
+ const result = await getLogStatus({
206
+ partnerType: 'merchant',
207
+ partnerId,
208
+ privateKey
209
+ });
210
+
211
+ const data = result.data;
212
+
213
+ // Format file size
214
+ const formatSize = (bytes) => {
215
+ if (!bytes) return '0 B';
216
+ const units = ['B', 'KB', 'MB', 'GB'];
217
+ let unitIndex = 0;
218
+ let size = bytes;
219
+ while (size >= 1024 && unitIndex < units.length - 1) {
220
+ size /= 1024;
221
+ unitIndex++;
222
+ }
223
+ return `${size.toFixed(1)} ${units[unitIndex]}`;
224
+ };
225
+
226
+ return {
227
+ content: [{
228
+ type: "text",
229
+ text: `📊 **Log Status**
230
+
231
+ **Availability:**
232
+ - Partner ID: ${data.partnerId || partnerId}
233
+ - Logs Available: ${data.logsAvailable ? '✅ Yes' : '❌ No'}
234
+
235
+ **Storage Information:**
236
+ - Total Size: ${formatSize(data.totalSizeBytes)}
237
+ - File Count: ${data.fileCount || 0} files
238
+
239
+ **What You Can Do:**
240
+ ${data.logsAvailable ? `
241
+ 1. **Query logs** using \`payware_logs_query\` tool
242
+ - Filter by level, logger, search text, date range
243
+ - Paginate through results with cursor
244
+
245
+ 2. **Download logs** using \`payware_logs_download_url\` tool
246
+ - Get compressed archive of logs for offline analysis
247
+ ` : `
248
+ ⚠️ No logs are currently available. Logs are generated when API calls are made.
249
+ `}
250
+
251
+ ⚠️ **Note:** This is a Premium (PRM) plan feature.`
252
+ }]
253
+ };
254
+ } catch (error) {
255
+ throw new Error(`Failed to get log status: ${error.message}`);
256
+ }
257
+ }
258
+ };
259
+
260
+ /**
261
+ * Get log download URL tool for merchants
262
+ */
263
+ export const getLogDownloadUrlTool = {
264
+ name: "payware_logs_download_url",
265
+ description: "Get download URL for logs archive. Premium plan required. Returns URL for downloading logs as gzipped JSON file.",
266
+ inputSchema: {
267
+ type: "object",
268
+ properties: {
269
+ from: {
270
+ type: "string",
271
+ description: "Start timestamp in ISO 8601 format (e.g., '2024-01-15T00:00:00Z'). Defaults to 24 hours ago."
272
+ },
273
+ to: {
274
+ type: "string",
275
+ description: "End timestamp in ISO 8601 format (e.g., '2024-01-15T23:59:59Z'). Defaults to now."
276
+ },
277
+ partnerId: {
278
+ type: "string",
279
+ description: "Partner ID from payware dashboard. Uses PAYWARE_PARTNER_ID env var as default.",
280
+ default: getPartnerIdSafe()
281
+ },
282
+ privateKey: {
283
+ type: "string",
284
+ description: "RSA private key for authentication. Uses environment-specific private key as default."
285
+ }
286
+ },
287
+ required: []
288
+ },
289
+
290
+ async handler(args) {
291
+ const {
292
+ from,
293
+ to,
294
+ partnerId = getPartnerIdSafe(),
295
+ privateKey = getPrivateKeySafe()
296
+ } = args;
297
+
298
+ if (!partnerId || !privateKey) {
299
+ throw new Error("Partner ID and private key are required. Set PAYWARE_PARTNER_ID and environment-specific private key variables or provide them as parameters.");
300
+ }
301
+
302
+ try {
303
+ const result = await getLogDownloadUrl({
304
+ from,
305
+ to,
306
+ partnerType: 'merchant',
307
+ partnerId,
308
+ privateKey
309
+ });
310
+
311
+ const data = result.data;
312
+
313
+ return {
314
+ content: [{
315
+ type: "text",
316
+ text: `📥 **Log Download Information**
317
+
318
+ **Download URL:**
319
+ \`${data.downloadUrl}\`
320
+
321
+ **Parameters:**
322
+ - From: ${data.parameters.from}
323
+ - To: ${data.parameters.to}
324
+
325
+ **Important:**
326
+ ${data.note}
327
+
328
+ **File Format:**
329
+ - Content-Type: application/gzip
330
+ - Format: Gzipped newline-delimited JSON (NDJSON)
331
+ - Each line is a complete JSON log entry
332
+
333
+ **Usage Example (cURL):**
334
+ \`\`\`bash
335
+ curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \\
336
+ -H "Api-Version: 1" \\
337
+ "${data.downloadUrl}" \\
338
+ -o logs.json.gz
339
+ \`\`\`
340
+
341
+ **Processing the Download:**
342
+ \`\`\`bash
343
+ # Decompress and view
344
+ gunzip -c logs.json.gz | head -10
345
+
346
+ # Parse with jq
347
+ gunzip -c logs.json.gz | jq '.level'
348
+ \`\`\`
349
+
350
+ ⚠️ **Note:** This is a Premium (PRM) plan feature.`
351
+ }]
352
+ };
353
+ } catch (error) {
354
+ throw new Error(`Failed to get download URL: ${error.message}`);
355
+ }
356
+ }
357
+ };
358
+
359
+ /**
360
+ * All merchant logs tools
361
+ */
362
+ export const merchantLogsTools = [
363
+ queryLogsTool,
364
+ getLogStatusTool,
365
+ getLogDownloadUrlTool
366
+ ];