@payware/mcp-server 1.3.3 → 1.5.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 (89) hide show
  1. package/.env.example +4 -0
  2. package/README.md +18 -0
  3. package/package.json +7 -9
  4. package/src/auth/rsa-keys.js +12 -6
  5. package/src/auth/sandbox-setup.js +2 -0
  6. package/src/config/toolsets.js +104 -0
  7. package/src/core/auth/jwt-factory.js +4 -1
  8. package/src/core/auth/jwt-token.js +4 -0
  9. package/src/index.js +10 -6
  10. package/src/isv/data/index.js +15 -14
  11. package/src/isv/deep-links/delete-product-link.js +3 -2
  12. package/src/isv/deep-links/delete-transaction-link.js +3 -2
  13. package/src/isv/deep-links/get-product-link.js +8 -6
  14. package/src/isv/deep-links/get-transaction-link.js +8 -6
  15. package/src/isv/index.js +6 -0
  16. package/src/isv/invitations/index.js +5 -0
  17. package/src/isv/oauth2/get-token-info.js +3 -0
  18. package/src/isv/oauth2/obtain-token.js +3 -0
  19. package/src/isv/oauth2/token-helper.js +2 -0
  20. package/src/isv/poi/cancel-price.js +2 -0
  21. package/src/isv/poi/get-poi.js +2 -0
  22. package/src/isv/poi/get-qrcode.js +40 -9
  23. package/src/isv/poi/get-status.js +2 -0
  24. package/src/isv/poi/list-pois.js +2 -0
  25. package/src/isv/poi/set-price.js +2 -0
  26. package/src/isv/products/audios.js +8 -2
  27. package/src/isv/products/create-product.js +2 -0
  28. package/src/isv/products/delete-product.js +2 -0
  29. package/src/isv/products/get-product-image.js +2 -0
  30. package/src/isv/products/get-product.js +2 -0
  31. package/src/isv/products/list-products.js +2 -0
  32. package/src/isv/products/schedules.js +6 -0
  33. package/src/isv/products/update-product.js +2 -0
  34. package/src/isv/reference/index.js +2 -0
  35. package/src/isv/transactions/cancel-transaction.js +2 -0
  36. package/src/isv/transactions/create-transaction.js +14 -2
  37. package/src/isv/transactions/get-status.js +3 -1
  38. package/src/isv/transactions/process-transaction.js +14 -2
  39. package/src/isv/transactions/simulate-callback.js +2 -0
  40. package/src/isv/transactions/transaction-history.js +2 -0
  41. package/src/merchant/data/export-report.js +2 -0
  42. package/src/merchant/data/generate-report.js +2 -0
  43. package/src/merchant/data/get-export-list.js +2 -0
  44. package/src/merchant/data/get-report-requests.js +2 -0
  45. package/src/merchant/data/get-report-status.js +2 -0
  46. package/src/merchant/data/index.js +15 -10
  47. package/src/merchant/data/list-reports.js +2 -0
  48. package/src/merchant/deep-links/delete-product-link.js +3 -2
  49. package/src/merchant/deep-links/delete-transaction-link.js +3 -2
  50. package/src/merchant/deep-links/get-product-link.js +8 -6
  51. package/src/merchant/deep-links/get-transaction-link.js +8 -6
  52. package/src/merchant/index.js +7 -1
  53. package/src/merchant/logs/index.js +4 -0
  54. package/src/merchant/products/audios.js +15 -14
  55. package/src/merchant/products/create-product.js +3 -2
  56. package/src/merchant/products/delete-product.js +3 -2
  57. package/src/merchant/products/get-product-image.js +3 -2
  58. package/src/merchant/products/get-product.js +3 -2
  59. package/src/merchant/products/list-products.js +3 -2
  60. package/src/merchant/products/schedules.js +11 -10
  61. package/src/merchant/products/update-product.js +3 -2
  62. package/src/merchant/transactions/cancel-transaction.js +4 -3
  63. package/src/merchant/transactions/create-transaction.js +14 -2
  64. package/src/merchant/transactions/get-status.js +4 -3
  65. package/src/merchant/transactions/process-transaction.js +17 -6
  66. package/src/merchant/transactions/simulate-callback.js +3 -0
  67. package/src/merchant/transactions/transaction-history.js +5 -4
  68. package/src/payment-institution/data/index.js +15 -14
  69. package/src/payment-institution/deep-links/delete-transaction-link.js +3 -2
  70. package/src/payment-institution/deep-links/get-transaction-link.js +8 -6
  71. package/src/payment-institution/transactions/create-pi-transaction.js +15 -4
  72. package/src/payment-institution/transactions/finalize-transaction.js +7 -4
  73. package/src/payment-institution/transactions/get-transaction-history.js +7 -4
  74. package/src/payment-institution/transactions/get-transaction-status.js +15 -12
  75. package/src/payment-institution/transactions/process-pi-transaction.js +19 -8
  76. package/src/payment-institution/transactions/simulate-pi-callback.js +3 -0
  77. package/src/payment-institution/transactions/soundbite-transaction.js +5 -4
  78. package/src/proxy-server.js +25 -0
  79. package/src/shared/placements/api.js +51 -0
  80. package/src/shared/placements/tools.js +519 -0
  81. package/src/shared/poi/poi-create-api.js +3 -0
  82. package/src/shared/products/products-api.js +36 -25
  83. package/src/shared/shops/shops-api.js +2 -0
  84. package/src/shared/tool-annotations.js +53 -0
  85. package/src/tools/advanced-code-generator.js +3 -0
  86. package/src/utils/format-request.js +2 -0
  87. package/src/utils/json-formatter.js +2 -0
  88. package/src/utils/server-info.js +2 -0
  89. package/src/utils/code-examples.js +0 -646
package/.env.example CHANGED
@@ -26,6 +26,10 @@ PAYWARE_SANDBOX_URL=https://sandbox.payware.eu/api
26
26
  # Production base URL (Optional - defaults to https://api.payware.eu/api)
27
27
  PAYWARE_PRODUCTION_URL=https://api.payware.eu/api
28
28
 
29
+ # Toolsets to load (Optional - unset loads every tool), comma-separated:
30
+ # auth, transactions, products, placements, deep_links, data, logs, poi, shops, onboarding, utils
31
+ # PAYWARE_MCP_TOOLSETS=products,placements
32
+
29
33
  # =============================================================================
30
34
  # ISV-SPECIFIC CONFIGURATION (Only when PAYWARE_PARTNER_TYPE=isv)
31
35
  # =============================================================================
package/README.md CHANGED
@@ -40,6 +40,14 @@ Releases are published to npm as `@payware/mcp-server` and to the official [MCP
40
40
  - **Get QR Code**: Generate QR code for POI payments
41
41
  - **List POIs**: List all POIs for a merchant
42
42
 
43
+ ### 📣 Campaign, Placement and Airing Tools
44
+ For merchants, and for ISVs acting for them. Changes need the merchant's PRM plan; reading works on every plan.
45
+ - **Placements**: Distribute a product through posters, TV spots, packaging, links or tags, each with its own `pl` ID, so every payment it starts is attributed to it. Includes the QR code, barcode and short link of each placement
46
+ - **Campaigns**: Group placements, across products, and read their combined results
47
+ - **Airing Rules**: Schedule when a placement is on air, with recurrences, time windows and time zones, and replace a whole schedule in one call
48
+ - **Airing Calendar**: Every airing across all products in a time range
49
+ - **Results**: Offers shown, payments started and confirmed, and confirmed amounts per placement or campaign
50
+
43
51
  ### 📋 Logs Tools (Premium)
44
52
  - **Query Logs**: Search and filter application logs by level, logger, message content, and date range
45
53
  - **Log Status**: Check log availability and storage information
@@ -92,6 +100,16 @@ PAYWARE_SANDBOX_URL=https://sandbox.payware.eu/api
92
100
 
93
101
  ⚠️ **Security Note**: Never commit the `.env` file to version control. It contains sensitive credentials.
94
102
 
103
+ #### Choosing toolsets
104
+
105
+ Every tool definition goes into the assistant's context on every turn, and a merchant has over 70 of them. To offer only the groups you use, set `PAYWARE_MCP_TOOLSETS` to a comma-separated list:
106
+
107
+ ```env
108
+ PAYWARE_MCP_TOOLSETS=products,placements
109
+ ```
110
+
111
+ Toolsets: `auth`, `transactions`, `products`, `placements`, `deep_links`, `data`, `logs`, `poi`, `shops`, `onboarding`, `utils`. Unset, or `all`, loads every tool. An unknown name is ignored with a warning in the server log. A tool outside the chosen toolsets is neither listed nor callable; this narrows what the assistant is offered and does not change what your partner account may do. An ISV that obtains OAuth2 tokens through this server includes `auth`.
112
+
95
113
  ### 3. Start the MCP Server
96
114
 
97
115
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payware/mcp-server",
3
- "version": "1.3.3",
3
+ "version": "1.5.0",
4
4
  "mcpName": "io.github.payware/mcp-server",
5
5
  "description": "Official MCP server for payware payment API integration",
6
6
  "main": "src/index.js",
@@ -15,19 +15,17 @@
15
15
  "proxy:dev": "node --watch src/proxy-server.js",
16
16
  "bridge": "node src/mcp-proxy.js",
17
17
  "inspector": "npx @modelcontextprotocol/inspector node src/mcp-proxy.js",
18
- "test": "node --test tests/api-errors.test.js tests/json-serializer.test.js tests/request-contract.test.js tests/tool-contract.test.js",
19
- "test:watch": "node --test --watch tests/api-errors.test.js tests/json-serializer.test.js tests/request-contract.test.js tests/tool-contract.test.js",
18
+ "test": "node --test tests/api-errors.test.js tests/json-serializer.test.js tests/proxy-server.test.js tests/request-contract.test.js tests/stdio-server.test.js tests/tool-contract.test.js tests/tool-routes.test.js tests/toolsets.test.js",
19
+ "test:watch": "node --test --watch tests/api-errors.test.js tests/json-serializer.test.js tests/proxy-server.test.js tests/request-contract.test.js tests/stdio-server.test.js tests/tool-contract.test.js tests/tool-routes.test.js tests/toolsets.test.js",
20
20
  "check": "node --check src/index.js && npm test"
21
21
  },
22
22
  "dependencies": {
23
- "@modelcontextprotocol/sdk": "^0.5.0",
24
- "axios": "^1.6.0",
23
+ "@modelcontextprotocol/sdk": "^1.32.1",
24
+ "axios": "^1.20.0",
25
25
  "cors": "^2.8.5",
26
26
  "dotenv": "^17.2.2",
27
- "express": "^5.1.0",
28
- "http-proxy-middleware": "^3.0.5",
29
- "jsonwebtoken": "^9.0.0",
30
- "node-forge": "^1.3.1"
27
+ "express": "^5.2.1",
28
+ "jsonwebtoken": "^9.0.3"
31
29
  },
32
30
  "engines": {
33
31
  "node": ">=18.0.0"
@@ -1,4 +1,5 @@
1
- import forge from 'node-forge';
1
+ import { generateKeyPairSync } from 'crypto';
2
+ import { LOCAL } from '../shared/tool-annotations.js';
2
3
 
3
4
  /**
4
5
  * Generate RSA key pair for payware API authentication
@@ -6,11 +7,15 @@ import forge from 'node-forge';
6
7
  * @returns {Object} Object containing publicKey and privateKey in PEM format
7
8
  */
8
9
  export function generateRSAKeyPair(keySize = 2048) {
9
- const keyPair = forge.pki.rsa.generateKeyPair({ bits: keySize });
10
-
11
- const privateKeyPem = forge.pki.privateKeyToPem(keyPair.privateKey);
12
- const publicKeyPem = forge.pki.publicKeyToPem(keyPair.publicKey);
13
-
10
+ // node:crypto rather than node-forge, which was a dependency for this call alone. The encodings are
11
+ // the ones forge produced - PKCS#1 private ("BEGIN RSA PRIVATE KEY"), SPKI public - so the output
12
+ // reads the same; only forge's CRLF line endings become LF.
13
+ const { privateKey: privateKeyPem, publicKey: publicKeyPem } = generateKeyPairSync('rsa', {
14
+ modulusLength: keySize,
15
+ privateKeyEncoding: { type: 'pkcs1', format: 'pem' },
16
+ publicKeyEncoding: { type: 'spki', format: 'pem' }
17
+ });
18
+
14
19
  return {
15
20
  privateKey: privateKeyPem,
16
21
  publicKey: publicKeyPem,
@@ -24,6 +29,7 @@ export function generateRSAKeyPair(keySize = 2048) {
24
29
  */
25
30
  export const generateRSAKeysTool = {
26
31
  name: "payware_authentication_generate_rsa_keys",
32
+ annotations: LOCAL,
27
33
  description: "Generate RSA key pair for payware API authentication",
28
34
  inputSchema: {
29
35
  type: "object",
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { getPartnerIdSafe, getPrivateKeySafe } from '../config/env.js';
6
+ import { LOCAL } from '../shared/tool-annotations.js';
6
7
 
7
8
  const SANDBOX_CONFIG = {
8
9
  baseUrl: 'https://sandbox.payware.eu/api',
@@ -90,6 +91,7 @@ export function generateSandboxConfig(partnerId, privateKey = null) {
90
91
  */
91
92
  export const setupSandboxAuthTool = {
92
93
  name: "payware_authentication_setup_sandbox_auth",
94
+ annotations: LOCAL,
93
95
  description: "Setup sandbox authentication configuration for payware API",
94
96
  inputSchema: {
95
97
  type: "object",
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Toolsets: named groups of tools, so a client can load only what it needs.
3
+ *
4
+ * Every tool definition is sent in tools/list and from there into every prompt the client builds, so
5
+ * a merchant with 72 tools pays for all of them on every turn whether it uses them or not. Setting
6
+ * PAYWARE_MCP_TOOLSETS=products,placements loads those two groups and nothing else. Unset, every tool
7
+ * loads, as it always has.
8
+ *
9
+ * This narrows what the assistant is offered; it is not access control. What a call may do is still
10
+ * decided by payware for the configured partner.
11
+ *
12
+ * Toolsets are assigned by tool name, not by prefix alone: the placement and airing tools sit under
13
+ * payware_products_* because the API nests them under products, yet they are a separate concern.
14
+ * tests/tool-contract.test.js fails on a tool that belongs to no toolset, so a new tool family has to
15
+ * be placed here.
16
+ */
17
+
18
+ /** Toolset name -> what it holds. The order is the order they are listed in messages. */
19
+ export const TOOLSETS = {
20
+ auth: 'Keys, JWTs and, for ISVs, OAuth2 tokens',
21
+ transactions: 'Create, process, finalize, cancel and look up transactions, and simulate callbacks',
22
+ products: 'Products, price schedules, audio and product images',
23
+ placements: 'Campaigns, placements, airing rules and the airing calendar',
24
+ deep_links: 'Shareable links for transactions and products',
25
+ data: 'Reports and exports',
26
+ logs: 'API logs (Premium)',
27
+ poi: 'Points of interaction (ISV)',
28
+ shops: 'Shop codes',
29
+ onboarding: 'ISV merchant invitations and the reference data their forms need',
30
+ utils: 'Code samples, documentation, request formatting and server information'
31
+ };
32
+
33
+ const BY_PREFIX = [
34
+ ['payware_authentication_', 'auth'],
35
+ ['payware_authorization_', 'auth'],
36
+ ['payware_operations_', 'transactions'],
37
+ ['payware_products_', 'products'],
38
+ ['payware_deep_links_', 'deep_links'],
39
+ ['payware_data_', 'data'],
40
+ ['payware_logs_', 'logs'],
41
+ ['payware_poi_', 'poi'],
42
+ ['payware_shops_', 'shops'],
43
+ ['payware_isv_', 'onboarding'],
44
+ ['payware_reference_', 'onboarding'],
45
+ ['payware_utils_', 'utils'],
46
+ ['payware_generate_', 'utils']
47
+ ];
48
+
49
+ /**
50
+ * The toolset a tool belongs to, or null for a name no rule covers.
51
+ * @param {string} name tool name
52
+ * @returns {string|null}
53
+ */
54
+ export function toolsetOf(name) {
55
+ if (name.startsWith('payware_campaigns_') || /^payware_products_.*(placement|airing)/.test(name)) {
56
+ return 'placements';
57
+ }
58
+ const match = BY_PREFIX.find(([prefix]) => name.startsWith(prefix));
59
+ return match ? match[1] : null;
60
+ }
61
+
62
+ /**
63
+ * Narrow a role's tools to the toolsets named in `setting` (PAYWARE_MCP_TOOLSETS).
64
+ *
65
+ * Unset, blank or containing `all`: every tool. Names are case-insensitive and may use a hyphen for
66
+ * the underscore. An unknown name is ignored with a warning, and when no known name is left every
67
+ * tool loads, because a typo that silently served no tools would look like a broken server. A known
68
+ * toolset with no tools for this role is a warning too, not an error.
69
+ *
70
+ * @param {Array} tools the role's tools
71
+ * @param {string|undefined} setting the PAYWARE_MCP_TOOLSETS value
72
+ * @returns {{tools: Array, selected: string[]|null, warnings: string[]}} selected is null when all load
73
+ */
74
+ export function filterToolsets(tools, setting) {
75
+ const names = String(setting ?? '')
76
+ .split(',')
77
+ .map(s => s.trim().toLowerCase().replace(/-/g, '_'))
78
+ .filter(Boolean);
79
+
80
+ if (names.length === 0 || names.includes('all')) {
81
+ return { tools, selected: null, warnings: [] };
82
+ }
83
+
84
+ const known = Object.keys(TOOLSETS);
85
+ const warnings = [];
86
+ const unknown = names.filter(n => !known.includes(n));
87
+ if (unknown.length > 0) {
88
+ warnings.push(`PAYWARE_MCP_TOOLSETS: unknown toolset ${unknown.join(', ')} ignored. Known: ${known.join(', ')}, or all.`);
89
+ }
90
+
91
+ const selected = known.filter(n => names.includes(n));
92
+ if (selected.length === 0) {
93
+ warnings.push('PAYWARE_MCP_TOOLSETS names no known toolset, so every tool is loaded.');
94
+ return { tools, selected: null, warnings };
95
+ }
96
+
97
+ const kept = tools.filter(t => selected.includes(toolsetOf(t.name)));
98
+ for (const set of selected) {
99
+ if (!kept.some(t => toolsetOf(t.name) === set)) {
100
+ warnings.push(`PAYWARE_MCP_TOOLSETS: toolset ${set} has no tools for this partner type.`);
101
+ }
102
+ }
103
+ return { tools: kept, selected, warnings };
104
+ }
@@ -119,7 +119,10 @@ async function createJWTTokenWithCustomPayload(payload, privateKey, requestBody)
119
119
  // Add contentSha256 to header if request body exists (SHA-256 is preferred over MD5)
120
120
  if (requestBody) {
121
121
  const { createMinimizedJSON } = await import('../utils/json-serializer.js');
122
- const bodyString = createMinimizedJSON(requestBody);
122
+ // A string IS the body, byte for byte - the caller serialized it once and sends exactly that.
123
+ // Serializing it again hashed a JSON string literal of the body, so every ISV write that passed
124
+ // its body as a string was ERR_INVALID_CONTENT_HASH. createJWTToken has always made this split.
125
+ const bodyString = typeof requestBody === 'string' ? requestBody : createMinimizedJSON(requestBody);
123
126
  header.contentSha256 = crypto.default.createHash('sha256').update(bodyString, 'utf8').digest('base64');
124
127
  }
125
128
 
@@ -3,6 +3,7 @@ import crypto from 'crypto';
3
3
  import { normalizePrivateKey, getKeyInfo } from '../utils/key-utils.js';
4
4
  import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
5
5
  import { createDeterministicJSON } from '../utils/json-serializer.js';
6
+ import { LOCAL } from '../../shared/tool-annotations.js';
6
7
 
7
8
  /**
8
9
  * Generate SHA-256 hash for JWT contentSha256 header (PREFERRED)
@@ -132,6 +133,7 @@ const IAT_MAX_FUTURE_SKEW_SECONDS = 60;
132
133
 
133
134
  export const validateJWTTokenTool = {
134
135
  name: "payware_authentication_validate_jwt",
136
+ annotations: LOCAL,
135
137
  description: "Validate and debug a payware JWT: decode header and payload, check the claims against the API's rules (algorithm, audience including the ISV on-behalf form, 5-minute freshness), and verify the contentSha256 hash against the request body when one is given",
136
138
  inputSchema: {
137
139
  type: "object",
@@ -374,6 +376,7 @@ ${issues.some(i => i.includes('hash mismatch')) ? '- Use deterministic JSON seri
374
376
  */
375
377
  export const testJWTTokenTool = {
376
378
  name: "payware_authentication_test_jwt",
379
+ annotations: LOCAL,
377
380
  description: "Test JWT token creation process with detailed step-by-step breakdown, showing deterministic JSON serialization, SHA-256 hash calculation, and final JWT structure",
378
381
  inputSchema: {
379
382
  type: "object",
@@ -604,6 +607,7 @@ ${deterministicJson}
604
607
  */
605
608
  export const createJWTTokenTool = {
606
609
  name: "payware_authentication_create_jwt_token",
610
+ annotations: LOCAL,
607
611
  description: "Create JWT token for payware API authentication according to official documentation",
608
612
  inputSchema: {
609
613
  type: "object",
package/src/index.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  // Import partner type and configuration
19
19
  import { getPartnerTypeSafe, getPartnerType } from './config/env.js';
20
20
  import { getPartnerConfig, getPartnerDisplayInfo } from './config/partner-types.js';
21
+ import { filterToolsets } from './config/toolsets.js';
21
22
 
22
23
  // Import tool registries
23
24
  import { getSharedTools } from './shared/index.js';
@@ -71,11 +72,13 @@ class PaywareMCPServer {
71
72
  allTools = getMerchantTools();
72
73
  }
73
74
 
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;
75
+ // PAYWARE_MCP_TOOLSETS narrows the list; a filtered-out tool is not listed and cannot be called.
76
+ // Warnings go to stderr, which MCP clients keep in their server log, never to stdout.
77
+ const { tools, warnings } = filterToolsets(allTools, process.env.PAYWARE_MCP_TOOLSETS);
78
+ for (const warning of warnings) {
79
+ process.stderr.write(`Warning: ${warning}\n`);
80
+ }
81
+ return tools;
79
82
  }
80
83
 
81
84
  setupToolHandlers() {
@@ -90,7 +93,8 @@ class PaywareMCPServer {
90
93
  tools: this.tools.map(tool => ({
91
94
  name: tool.name,
92
95
  description: `[${this.partnerInfo.name}] ${tool.description}`,
93
- inputSchema: tool.inputSchema
96
+ inputSchema: tool.inputSchema,
97
+ annotations: tool.annotations
94
98
  }))
95
99
  };
96
100
  });
@@ -19,10 +19,12 @@ import {
19
19
  LOCALES
20
20
  } from '../../shared/data/data-api.js';
21
21
  import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
22
+ import { CHANGES, CREATES, READS } from '../../shared/tool-annotations.js';
22
23
 
23
24
  // Create ISV data tool factory
24
- const createISVDataTool = (toolName, apiFunction, description, additionalParams = {}) => ({
25
+ const createISVDataTool = (toolName, apiFunction, description, additionalParams = {}, annotations) => ({
25
26
  name: `payware_data_${toolName}`,
27
+ annotations,
26
28
  description: `${description} (ISV acting on behalf of merchant)`,
27
29
  inputSchema: {
28
30
  type: "object",
@@ -43,8 +45,7 @@ const createISVDataTool = (toolName, apiFunction, description, additionalParams
43
45
  },
44
46
  privateKey: {
45
47
  type: "string",
46
- description: "ISV RSA private key for authentication. Uses environment-specific private key as default.",
47
- default: getPrivateKeySafe()
48
+ description: "ISV RSA private key for authentication. Uses environment-specific private key as default."
48
49
  },
49
50
  ...additionalParams
50
51
  },
@@ -104,6 +105,7 @@ ${JSON.stringify(result.data, null, 2)}
104
105
  */
105
106
  export const generateReportTool = {
106
107
  name: "payware_data_generate_report",
108
+ annotations: CREATES,
107
109
  description: "Generate an asynchronous data report for ISV data analysis and reporting on behalf of merchants. Production only - not supported in sandbox.",
108
110
  inputSchema: {
109
111
  type: "object",
@@ -156,8 +158,7 @@ export const generateReportTool = {
156
158
  },
157
159
  privateKey: {
158
160
  type: "string",
159
- description: "ISV RSA private key for authentication. Uses environment-specific private key as default.",
160
- default: getPrivateKeySafe()
161
+ description: "ISV RSA private key for authentication. Uses environment-specific private key as default."
161
162
  }
162
163
  },
163
164
  required: ["reportUnitId", "merchantPartnerId", "oauth2Token"]
@@ -232,7 +233,7 @@ This feature requires proper OAuth2 authorization from the merchant and is only
232
233
  };
233
234
 
234
235
  // Create all other ISV data tools
235
- export const listReportsTool = createISVDataTool('list_reports', listReports, 'List All Available Reports', {});
236
+ export const listReportsTool = createISVDataTool('list_reports', listReports, 'List All Available Reports', {}, READS);
236
237
 
237
238
  export const getReportRequestsTool = createISVDataTool('get_report_requests', getReportRequests, 'Get Report Request History', {
238
239
  reportId: {
@@ -240,21 +241,21 @@ export const getReportRequestsTool = createISVDataTool('get_report_requests', ge
240
241
  description: "The report unit ID (path) to get execution history for (e.g., '/processedTransactions')",
241
242
  enum: Object.values(REPORT_TYPES)
242
243
  }
243
- });
244
+ }, READS);
244
245
 
245
246
  export const getExportListTool = createISVDataTool('get_export_list', getExportList, 'Get Export List for Request', {
246
247
  requestId: {
247
248
  type: "string",
248
249
  description: "The request ID (UUID) to get exports for"
249
250
  }
250
- });
251
+ }, READS);
251
252
 
252
253
  export const getReportStatusTool = createISVDataTool('get_report_status', getReportStatus, 'Get Report Status', {
253
254
  requestId: {
254
255
  type: "string",
255
256
  description: "The request ID returned from the report generation request (UUID format)"
256
257
  }
257
- });
258
+ }, READS);
258
259
 
259
260
  export const exportReportTool = createISVDataTool('export_report', exportReport, 'Export Report', {
260
261
  requestId: {
@@ -267,35 +268,35 @@ export const exportReportTool = createISVDataTool('export_report', exportReport,
267
268
  enum: Object.values(EXPORT_FORMATS),
268
269
  default: EXPORT_FORMATS.PDF
269
270
  }
270
- });
271
+ }, CREATES);
271
272
 
272
273
  export const getExportStatusTool = createISVDataTool('get_export_status', getExportStatus, 'Get Export Status', {
273
274
  exportId: {
274
275
  type: "string",
275
276
  description: "The export ID returned from the export request (UUID format)"
276
277
  }
277
- });
278
+ }, READS);
278
279
 
279
280
  export const downloadExportTool = createISVDataTool('download_export', downloadExport, 'Download Export', {
280
281
  exportId: {
281
282
  type: "string",
282
283
  description: "The export ID from the export request (UUID format)"
283
284
  }
284
- });
285
+ }, READS);
285
286
 
286
287
  export const cancelReportTool = createISVDataTool('cancel_report', cancelReport, 'Cancel Report', {
287
288
  requestId: {
288
289
  type: "string",
289
290
  description: "The request ID of the report to cancel (UUID format)"
290
291
  }
291
- });
292
+ }, CHANGES);
292
293
 
293
294
  export const cancelExportTool = createISVDataTool('cancel_export', cancelExport, 'Cancel Export', {
294
295
  exportId: {
295
296
  type: "string",
296
297
  description: "The export ID of the export to cancel (UUID format)"
297
298
  }
298
- });
299
+ }, CHANGES);
299
300
 
300
301
  /**
301
302
  * All ISV data tools
@@ -1,11 +1,13 @@
1
1
  import { deleteProductDeepLink } from '../../shared/deep-links/api.js';
2
2
  import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
3
+ import { CHANGES } from '../../shared/tool-annotations.js';
3
4
 
4
5
  /**
5
6
  * Delete deep link for a product (ISV on behalf of merchant)
6
7
  */
7
8
  export const deleteProductLinkTool = {
8
9
  name: "payware_deep_links_delete_product_link",
10
+ annotations: CHANGES,
9
11
  description: `Delete an active deep link for a product on behalf of a merchant. Once deleted, the deep link URL will no longer redirect to the payware mobile app.
10
12
 
11
13
  **ISV Authentication:**
@@ -45,8 +47,7 @@ export const deleteProductLinkTool = {
45
47
  },
46
48
  privateKey: {
47
49
  type: "string",
48
- description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default.",
49
- default: getPrivateKeySafe()
50
+ description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default."
50
51
  }
51
52
  },
52
53
  required: ["productId", "merchantPartnerId", "oauth2Token"]
@@ -1,11 +1,13 @@
1
1
  import { deleteTransactionDeepLink } from '../../shared/deep-links/api.js';
2
2
  import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
3
+ import { CHANGES } from '../../shared/tool-annotations.js';
3
4
 
4
5
  /**
5
6
  * Delete deep link for a transaction (ISV on behalf of merchant)
6
7
  */
7
8
  export const deleteTransactionLinkTool = {
8
9
  name: "payware_deep_links_delete_transaction_link",
10
+ annotations: CHANGES,
9
11
  description: `Delete an active deep link for a transaction on behalf of a merchant. Once deleted, the deep link URL will no longer redirect to the payware mobile app.
10
12
 
11
13
  **ISV Authentication:**
@@ -45,8 +47,7 @@ export const deleteTransactionLinkTool = {
45
47
  },
46
48
  privateKey: {
47
49
  type: "string",
48
- description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default.",
49
- default: getPrivateKeySafe()
50
+ description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default."
50
51
  }
51
52
  },
52
53
  required: ["transactionId", "merchantPartnerId", "oauth2Token"]
@@ -1,12 +1,14 @@
1
1
  import { getProductDeepLink } from '../../shared/deep-links/api.js';
2
2
  import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
3
+ import { ADDS } from '../../shared/tool-annotations.js';
3
4
 
4
5
  /**
5
6
  * Get deep link for a product (ISV on behalf of merchant)
6
7
  */
7
8
  export const getProductLinkTool = {
8
9
  name: "payware_deep_links_get_product_link",
9
- description: `Get an active deep link for a product on behalf of a merchant. Returns a clickable URL that redirects to the payware mobile app.
10
+ annotations: ADDS,
11
+ description: `Get an active deep link for a product on behalf of a merchant. Returns a clickable URL that redirects to the payware mobile app. For a link whose payments are attributed to one placement of the product (a poster, an ad, a mailing), use payware_products_get_placement_link instead.
10
12
 
11
13
  **ISV Authentication:**
12
14
  - Requires ISV JWT token with merchant partner ID in 'aud' claim
@@ -14,7 +16,7 @@ export const getProductLinkTool = {
14
16
 
15
17
  **Deep Link Format:**
16
18
  - Web URL: https://go.payware.eu/{productId}
17
- - Mobile Redirect: payware://{productId}
19
+ - Mobile Redirect: payware:/{productId} (one slash: the identifier is case-sensitive, so it must not sit in the URI host, which redirect services lowercase)
18
20
 
19
21
  **Use Cases:**
20
22
  - Mobile-only e-commerce product references
@@ -23,6 +25,7 @@ export const getProductLinkTool = {
23
25
 
24
26
  **Important Notes:**
25
27
  - Deep links are only supported in production environment
28
+ - Apps that open payware links must accept \`payware:\` followed by one, two or three slashes (QR codes and NFC tags carry \`payware://{id}\`): read the identifier after the scheme and any slashes, up to \`/\`, \`?\` or \`#\`, and keep its case
26
29
  - Link expiration is synchronized with the referenced product
27
30
  - Traditional deep links that route to app content when app is installed`,
28
31
 
@@ -49,8 +52,7 @@ export const getProductLinkTool = {
49
52
  },
50
53
  privateKey: {
51
54
  type: "string",
52
- description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default.",
53
- default: getPrivateKeySafe()
55
+ description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default."
54
56
  }
55
57
  },
56
58
  required: ["productId", "merchantPartnerId", "oauth2Token"]
@@ -107,7 +109,7 @@ export const getProductLinkTool = {
107
109
  - Product ID: ${productId}
108
110
  - Merchant: ${merchantPartnerId}
109
111
  - Web URL: ${linkUrl}
110
- - Mobile Redirect: payware://${productId}
112
+ - Mobile Redirect: payware:/${productId}
111
113
 
112
114
  **ISV Authentication:**
113
115
  - ISV Partner ID: ${isvPartnerId}
@@ -131,7 +133,7 @@ ${JSON.stringify(result.data, null, 2)}
131
133
 
132
134
  **Usage:**
133
135
  1. Share the web URL: \`${linkUrl}\`
134
- 2. On mobile devices, it will redirect to: \`payware://${productId}\`
136
+ 2. On mobile devices, it will redirect to: \`payware:/${productId}\`
135
137
  3. Link expires when the product expires
136
138
  4. Recreate if product is updated`
137
139
  }]
@@ -1,11 +1,13 @@
1
1
  import { getTransactionDeepLink } from '../../shared/deep-links/api.js';
2
2
  import { getPartnerIdSafe, getPrivateKeySafe } from '../../config/env.js';
3
+ import { ADDS } from '../../shared/tool-annotations.js';
3
4
 
4
5
  /**
5
6
  * Get deep link for a transaction (ISV on behalf of merchant)
6
7
  */
7
8
  export const getTransactionLinkTool = {
8
9
  name: "payware_deep_links_get_transaction_link",
10
+ annotations: ADDS,
9
11
  description: `Get an active deep link for a transaction on behalf of a merchant. Returns a clickable URL that redirects to the payware mobile app.
10
12
 
11
13
  **ISV Authentication:**
@@ -14,7 +16,7 @@ export const getTransactionLinkTool = {
14
16
 
15
17
  **Deep Link Format:**
16
18
  - Web URL: https://go.payware.eu/{transactionId}
17
- - Mobile Redirect: payware://{transactionId}
19
+ - Mobile Redirect: payware:/{transactionId} (one slash: the identifier is case-sensitive, so it must not sit in the URI host, which redirect services lowercase)
18
20
 
19
21
  **Use Cases:**
20
22
  - P2P payments via messaging services
@@ -23,6 +25,7 @@ export const getTransactionLinkTool = {
23
25
 
24
26
  **Important Notes:**
25
27
  - Deep links are only supported in production environment
28
+ - Apps that open payware links must accept \`payware:\` followed by one, two or three slashes (QR codes and NFC tags carry \`payware://{id}\`): read the identifier after the scheme and any slashes, up to \`/\`, \`?\` or \`#\`, and keep its case
26
29
  - Link expiration is synchronized with the referenced transaction
27
30
  - Traditional deep links that route to app content when app is installed`,
28
31
 
@@ -49,8 +52,7 @@ export const getTransactionLinkTool = {
49
52
  },
50
53
  privateKey: {
51
54
  type: "string",
52
- description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default.",
53
- default: getPrivateKeySafe()
55
+ description: "ISV RSA private key for JWT token creation. Uses environment-specific private key as default."
54
56
  }
55
57
  },
56
58
  required: ["transactionId", "merchantPartnerId", "oauth2Token"]
@@ -107,7 +109,7 @@ export const getTransactionLinkTool = {
107
109
  - Transaction ID: ${transactionId}
108
110
  - Merchant: ${merchantPartnerId}
109
111
  - Web URL: ${linkUrl}
110
- - Mobile Redirect: payware://${transactionId}
112
+ - Mobile Redirect: payware:/${transactionId}
111
113
 
112
114
  **ISV Authentication:**
113
115
  - ISV Partner ID: ${isvPartnerId}
@@ -131,7 +133,7 @@ ${JSON.stringify(result.data, null, 2)}
131
133
 
132
134
  **Usage:**
133
135
  1. Share the web URL: \`${linkUrl}\`
134
- 2. On mobile devices, it will redirect to: \`payware://${transactionId}\`
136
+ 2. On mobile devices, it will redirect to: \`payware:/${transactionId}\`
135
137
  3. Link expires when the transaction expires
136
138
  4. Recreate if transaction is extended`
137
139
  }]
@@ -155,7 +157,7 @@ ${JSON.stringify(result.data, null, 2)}
155
157
  5. **Invalid transaction ID**: Ensure transaction ID format is correct
156
158
 
157
159
  **Troubleshooting:**
158
- 1. Verify transaction exists using \`payware_transactions_get_transaction_status\`
160
+ 1. Verify transaction exists using \`payware_operations_get_transaction_status\`
159
161
  2. Check OAuth2 token is valid and not expired
160
162
  3. Ensure ISV has permission to access merchant data
161
163
  4. Check if running in production environment
package/src/isv/index.js CHANGED
@@ -31,6 +31,9 @@ import { isvPOITools } from './poi/index.js';
31
31
  // Shop tools
32
32
  import { isvShopTools } from './shops/index.js';
33
33
 
34
+ // Campaigns, placements and airings - one definition shared by merchants and ISVs
35
+ import { placementTools } from '../shared/placements/tools.js';
36
+
34
37
  // Merchant onboarding: invitations, and the reference data their forms need
35
38
  import { isvInvitationTools } from './invitations/index.js';
36
39
  import { isvReferenceTools } from './reference/index.js';
@@ -73,6 +76,9 @@ export const isvTools = [
73
76
  // Shop lookup - needed before any call that takes a shopCode
74
77
  ...isvShopTools,
75
78
 
79
+ // Campaigns, placements and airings, on behalf of the merchant
80
+ ...placementTools('isv'),
81
+
76
82
  // Merchant onboarding. These come BEFORE a merchant exists, so they authenticate as the ISV
77
83
  // itself rather than on behalf of anyone - they are how the merchantPartnerId + oauth2Token pair
78
84
  // that every other tool here requires comes into existence in the first place.