@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.
- package/.env.example +4 -0
- package/README.md +18 -0
- package/package.json +7 -9
- package/src/auth/rsa-keys.js +12 -6
- package/src/auth/sandbox-setup.js +2 -0
- package/src/config/toolsets.js +104 -0
- package/src/core/auth/jwt-factory.js +4 -1
- package/src/core/auth/jwt-token.js +4 -0
- package/src/index.js +10 -6
- package/src/isv/data/index.js +15 -14
- package/src/isv/deep-links/delete-product-link.js +3 -2
- package/src/isv/deep-links/delete-transaction-link.js +3 -2
- package/src/isv/deep-links/get-product-link.js +8 -6
- package/src/isv/deep-links/get-transaction-link.js +8 -6
- package/src/isv/index.js +6 -0
- package/src/isv/invitations/index.js +5 -0
- package/src/isv/oauth2/get-token-info.js +3 -0
- package/src/isv/oauth2/obtain-token.js +3 -0
- package/src/isv/oauth2/token-helper.js +2 -0
- package/src/isv/poi/cancel-price.js +2 -0
- package/src/isv/poi/get-poi.js +2 -0
- package/src/isv/poi/get-qrcode.js +40 -9
- package/src/isv/poi/get-status.js +2 -0
- package/src/isv/poi/list-pois.js +2 -0
- package/src/isv/poi/set-price.js +2 -0
- package/src/isv/products/audios.js +8 -2
- package/src/isv/products/create-product.js +2 -0
- package/src/isv/products/delete-product.js +2 -0
- package/src/isv/products/get-product-image.js +2 -0
- package/src/isv/products/get-product.js +2 -0
- package/src/isv/products/list-products.js +2 -0
- package/src/isv/products/schedules.js +6 -0
- package/src/isv/products/update-product.js +2 -0
- package/src/isv/reference/index.js +2 -0
- package/src/isv/transactions/cancel-transaction.js +2 -0
- package/src/isv/transactions/create-transaction.js +14 -2
- package/src/isv/transactions/get-status.js +3 -1
- package/src/isv/transactions/process-transaction.js +14 -2
- package/src/isv/transactions/simulate-callback.js +2 -0
- package/src/isv/transactions/transaction-history.js +2 -0
- package/src/merchant/data/export-report.js +2 -0
- package/src/merchant/data/generate-report.js +2 -0
- package/src/merchant/data/get-export-list.js +2 -0
- package/src/merchant/data/get-report-requests.js +2 -0
- package/src/merchant/data/get-report-status.js +2 -0
- package/src/merchant/data/index.js +15 -10
- package/src/merchant/data/list-reports.js +2 -0
- package/src/merchant/deep-links/delete-product-link.js +3 -2
- package/src/merchant/deep-links/delete-transaction-link.js +3 -2
- package/src/merchant/deep-links/get-product-link.js +8 -6
- package/src/merchant/deep-links/get-transaction-link.js +8 -6
- package/src/merchant/index.js +7 -1
- package/src/merchant/logs/index.js +4 -0
- package/src/merchant/products/audios.js +15 -14
- package/src/merchant/products/create-product.js +3 -2
- package/src/merchant/products/delete-product.js +3 -2
- package/src/merchant/products/get-product-image.js +3 -2
- package/src/merchant/products/get-product.js +3 -2
- package/src/merchant/products/list-products.js +3 -2
- package/src/merchant/products/schedules.js +11 -10
- package/src/merchant/products/update-product.js +3 -2
- package/src/merchant/transactions/cancel-transaction.js +4 -3
- package/src/merchant/transactions/create-transaction.js +14 -2
- package/src/merchant/transactions/get-status.js +4 -3
- package/src/merchant/transactions/process-transaction.js +17 -6
- package/src/merchant/transactions/simulate-callback.js +3 -0
- package/src/merchant/transactions/transaction-history.js +5 -4
- package/src/payment-institution/data/index.js +15 -14
- package/src/payment-institution/deep-links/delete-transaction-link.js +3 -2
- package/src/payment-institution/deep-links/get-transaction-link.js +8 -6
- package/src/payment-institution/transactions/create-pi-transaction.js +15 -4
- package/src/payment-institution/transactions/finalize-transaction.js +7 -4
- package/src/payment-institution/transactions/get-transaction-history.js +7 -4
- package/src/payment-institution/transactions/get-transaction-status.js +15 -12
- package/src/payment-institution/transactions/process-pi-transaction.js +19 -8
- package/src/payment-institution/transactions/simulate-pi-callback.js +3 -0
- package/src/payment-institution/transactions/soundbite-transaction.js +5 -4
- package/src/proxy-server.js +25 -0
- package/src/shared/placements/api.js +51 -0
- package/src/shared/placements/tools.js +519 -0
- package/src/shared/poi/poi-create-api.js +3 -0
- package/src/shared/products/products-api.js +36 -25
- package/src/shared/shops/shops-api.js +2 -0
- package/src/shared/tool-annotations.js +53 -0
- package/src/tools/advanced-code-generator.js +3 -0
- package/src/utils/format-request.js +2 -0
- package/src/utils/json-formatter.js +2 -0
- package/src/utils/server-info.js +2 -0
- 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
|
+
"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": "^
|
|
24
|
-
"axios": "^1.
|
|
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
|
|
28
|
-
"
|
|
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"
|
package/src/auth/rsa-keys.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const publicKeyPem =
|
|
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
|
-
|
|
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
|
-
//
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
});
|
package/src/isv/data/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 \`
|
|
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.
|