@airwallex/developer-mcp 0.3.0-beta.10

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Airwallex
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # @airwallex/developer-mcp
2
+
3
+ The Airwallex Developer Model Context Protocol (MCP) server empowers AI coding agents with the tools they need to assist developers integrating with [Airwallex APIs](https://www.airwallex.com/docs/api#/Introduction). It enables agents to search Airwallex documentation and interact with the sandbox environment for testing and simulation.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ <details>
10
+ <summary><strong>Cursor</strong></summary>
11
+
12
+ [![Install Airwallex Developer MCP Server on Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=airwallex-dev&config=eyJlbnYiOnsiQUlSV0FMTEVYX1NBTkRCT1hfQ0xJRU5UX0lEIjoiIiwiQUlSV0FMTEVYX1NBTkRCT1hfQVBJX0tFWSI6IiJ9LCJjb21tYW5kIjoibnB4IC15IEBhaXJ3YWxsZXgvZGV2ZWxvcGVyLW1jcEBsYXRlc3QifQ%3D%3D)
13
+
14
+ ```json
15
+ {
16
+ "mcpServers": {
17
+ "airwallex-dev": {
18
+ "command": "npx",
19
+ "args": ["-y", "@airwallex/developer-mcp@latest"],
20
+ "env": {
21
+ "AIRWALLEX_SANDBOX_CLIENT_ID": "",
22
+ "AIRWALLEX_SANDBOX_API_KEY": ""
23
+ }
24
+ }
25
+ }
26
+ }
27
+ ```
28
+
29
+ </details>
30
+
31
+ <details>
32
+ <summary><strong>Claude Code</strong></summary>
33
+
34
+ ```zsh
35
+ claude mcp add-json airwallex-dev '{ "type": "stdio", "command":"npx", "args": ["-y", "@airwallex/developer-mcp@latest"], "env": { "AIRWALLEX_SANDBOX_CLIENT_ID": "", "AIRWALLEX_SANDBOX_API_KEY": "" } }'
36
+ ```
37
+
38
+ </details>
39
+
40
+ <details>
41
+ <summary><strong>Gemini CLI</strong></summary>
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "airwallex-dev": {
47
+ "command": "npx",
48
+ "args": ["-y", "@airwallex/developer-mcp@latest"],
49
+ "env": {
50
+ "AIRWALLEX_SANDBOX_CLIENT_ID": "",
51
+ "AIRWALLEX_SANDBOX_API_KEY": ""
52
+ }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ </details>
59
+
60
+ <details>
61
+ <summary><strong>OpenAI Codex</strong></summary>
62
+
63
+ ```toml
64
+ [mcp_servers.airwallex-dev]
65
+ command = "npx"
66
+ args = ["-y", "@airwallex/developer-mcp@latest"]
67
+
68
+ [mcp_servers.airwallex-dev.env]
69
+ AIRWALLEX_SANDBOX_CLIENT_ID = ""
70
+ AIRWALLEX_SANDBOX_API_KEY = ""
71
+ ```
72
+
73
+ </details>
74
+
75
+ ---
76
+
77
+ ## Example prompts
78
+
79
+ _Integrate Airwallex's Drop-in element with this website_
80
+
81
+ _Using Airwallex, can you find me test card numbers for the 3DS authentication failure case?_
82
+
83
+ _I tried to make a payout with Airwallex and got this error: amount_below_transfer_method_limit. What does that mean?_
84
+
85
+ ### With sandbox tools
86
+
87
+ _Can you create a new billing plan on Airwallex called Startup for $29.99/month and create a hosted URL to subscribe to it?_
88
+
89
+ ---
90
+
91
+ ## Environment variables
92
+
93
+ To access sandbox tools, ensure that your sandbox API credentials are set using the following environment variables. If you don't already have a sandbox account, create one using [this link](https://demo.airwallex.com/signup/sandbox).
94
+
95
+ | Name | Description | Default | Required |
96
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
97
+ | `AIRWALLEX_SANDBOX_API_KEY` | The API key, either an admin key or a scoped key with limited permissions, generated in the sandbox environment. Note that the sandbox tools work as expected only if the API key has the required permissions to access the underlying APIs. Simulation APIs require an admin API key. | N/A | No |
98
+ | `AIRWALLEX_SANDBOX_CLIENT_ID` | The client ID associated with the API key generated in the sandbox environment. | N/A | No |
99
+ | `DISABLE_TELEMETRY` | Set this variable to `true` to opt out of telemetry. | `0` | No |
100
+
101
+ ---
102
+
103
+ ## Tools
104
+
105
+ ### Developer tools
106
+
107
+
108
+ | Name | Description |
109
+ |-------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
110
+ | `search_public_docs` | Search the Airwallex documentation. Covers the following sources: [Product docs](https://www.airwallex.com/docs/), [API reference](https://www.airwallex.com/docs/api), [Airwallex.js](https://www.airwallex.com/docs/js/), Payments [iOS](https://github.com/airwallex/airwallex-payment-ios) and [Android](https://github.com/airwallex/airwallex-payment-android) SDKs |
111
+
112
+ ### Sandbox tools
113
+
114
+ All data retrieved and actions performed with the tools below are in the authenticated Sandbox account.
115
+
116
+ | Name | Description |
117
+ |-------------------------|-------------------------------------------------------------------------------------------------------------------------------------|
118
+ | `list_global_accounts` | List all available global accounts |
119
+ | `get_balances` | List balances in all currencies |
120
+ | `simulate_create_deposit` | Simulate a deposit to an available global account |
121
+ | `list_billing_products` | List available billing products |
122
+ | `list_billing_prices` | List available billing prices |
123
+ | `create_billing_product` | Create a new billing product |
124
+ | `create_billing_price` | Create a new billing price |
125
+ | `create_billing_checkout` | Create a new hosted billing checkout page |
126
+ | `get_fx_quote` | Get a quote for a FX conversion |
127
+ | `list_transfers` | List transfers initiated |
128
+ | `create_transfer` | Initiate a new transfer to a recipient |
129
+ | `list_beneficiaries` | List available transfer recipients |
130
+ | `simulate_transfer_result` | Simulate the status of a transfer |
131
+ | `create_payment_link` | Create a new payment link |
132
+ | `list_payment_links` | List created payment links |
133
+
134
+
135
+
136
+ ---
137
+
138
+ ## Disclaimer
139
+
140
+ This MCP server can help minimize hallucinations in Airwallex-related integration code produced by your coding agent but does not eliminate hallucinations entirely. Please always review, validate, and thoroughly test any AI-generated code before deploying it to production.
141
+
142
+ ## Feedback
143
+
144
+ This MCP server is developed and maintained by the Airwallex Developer Experience team. We welcome your feedback — please contact us at [`developer.support@airwallex.com`](mailto:developer.support@airwallex.com). Remember to mention the AI model your coding agent was connected to.
145
+
146
+ ## Telemetry
147
+
148
+ Telemetry is enabled by default. To disable telemetry, set the `DISABLE_TELEMETRY` environment variable to `true`. Only anonymous data about MCP tool usage is collected.
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BASE_URLS = void 0;
4
+ exports.BASE_URLS = {
5
+ api: "https://api-demo.airwallex.com",
6
+ docsSearch: "https://demo.airwallex.com/docs/ai",
7
+ telemetry: "https://o11y-demo.airwallex.com",
8
+ };
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TOOL_DESCRIPTIONS = void 0;
4
+ exports.TOOL_DESCRIPTIONS = {
5
+ CREATE_BILLING_CHECKOUT: `Create a billing checkout session for a single price in the authenticated Sandbox account.
6
+
7
+ This tool creates a billing checkout session and returns a hosted checkout URL that can be shared.
8
+
9
+ EXAMPLES:
10
+ • One-time payment: mode=PAYMENT, price_id=pri_xxx
11
+ • Monthly subscription: mode=SUBSCRIPTION, price_id=pri_xxx`,
12
+ CREATE_BILLING_PRICE: `Create a new price for a billing product in the authenticated Sandbox account.
13
+
14
+ EXAMPLES:
15
+ • Ecommerce product: type=ONE_OFF, pricing_model=PER_UNIT
16
+ • Monthly subscription: type=RECURRING, pricing_model=FLAT, recurring_period=1, recurring_period_unit=MONTH
17
+ • Bi-weekly: type=RECURRING, recurring_period=2, recurring_period_unit=WEEK
18
+ • Yearly: type=RECURRING, recurring_period=1, recurring_period_unit=YEAR`,
19
+ CREATE_BILLING_PRODUCT: `Create a new billing product in the authenticated Sandbox account.`,
20
+ CREATE_PAYMENT_LINK: `Create a payment link in the authenticated Sandbox account and optionally send it via email to a shopper.`,
21
+ CREATE_TRANSFER: `Create a new transfer to an existing beneficiary in the authenticated Sandbox account.
22
+
23
+ Either transfer_amount OR source_amount must be provided, but not both.`,
24
+ GET_ACCOUNT_BALANCES: `Retrieve balances for all currencies in the authenticated Sandbox account.
25
+
26
+ PRESENTATION REQUIREMENT: When presenting the results to the user, you MUST format the balance data as a clear table showing each currency with its corresponding balance amounts. Create a table with columns for Currency, Available, Pending, Reserved, Total, and Prepayment amounts.
27
+
28
+ RESPONSE: Amounts returned are NOT in minor units.`,
29
+ GET_FX_QUOTE: `Get a foreign exchange quote for a currency conversion in the authenticated Sandbox account.`,
30
+ LIST_BENEFICIARIES: `List all beneficiaries in the authenticated Sandbox account.`,
31
+ LIST_BILLING_PRICES: `List billing prices in the authenticated Sandbox account.`,
32
+ LIST_BILLING_PRODUCTS: `List all billing products in the authenticated Sandbox account.`,
33
+ LIST_GLOBAL_ACCOUNTS: `List all global accounts in the authenticated Sandbox account.`,
34
+ LIST_PAYMENT_LINKS: `List all payment links in the authenticated Sandbox account.
35
+
36
+ This tool retrieves a list of all payment links created by the merchant.`,
37
+ LIST_TRANSFERS: `List all transfers in the authenticated Sandbox account.`,
38
+ READ_BEST_PRACTICES: `CODE GENERATION ONLY: This tool MUST be called before any other Airwallex tools, but ONLY when the user intent is to generate code. For documentation searches, general questions, or non-code-related tasks, skip this tool and proceed directly with the appropriate tools for the task at hand.
39
+
40
+ SINGLE USE PER CONVERSATION: You MUST call this tool only ONCE per conversation. The best practices content is static and doesn't need to be re-read multiple times.
41
+
42
+ This tool reads the Airwallex integration best practices documentation that contains essential guidelines for API usage, payment handling, SDK implementation, and common pitfalls to avoid.`,
43
+ RETRIEVE_DOCS: `Retrieve relevant sections from the official Airwallex documentation matching the user's question.
44
+
45
+ USAGE STRATEGY:
46
+ DO: Prefer to start with a broad search without specifying any source and then narrow down the search with a specific source when needed
47
+ DO: For relevant API endpoints or SDK methods in the sections returned, invoke this tool again with the specific source to get more detailed information
48
+ DO: For API endpoints referenced, always remember to explicitly check how to perform API authentication
49
+ DO: For SDK methods referenced, always remember to explicitly check how to initialize the SDK
50
+
51
+ PRIVACY & SECURITY REQUIREMENTS:
52
+ • DON'T: Include PII (Personally Identifiable Information) in questions
53
+ • DON'T: Include account IDs, user IDs, email addresses, or customer data
54
+ • DON'T: Include API keys, tokens, or credentials
55
+ • DON'T: Include transaction IDs, payment references, or financial data
56
+ • DO: Use generic examples like 'how to process payments' instead of specific account details
57
+ • DO: Focus on technical concepts, API endpoints, and implementation patterns
58
+ • DO: API version information is permitted as an exception (e.g., '2023-09-30')`,
59
+ SIMULATE_DEPOSIT: `Simulate a deposit to a global account in the authenticated Sandbox account.
60
+
61
+ This tool simulates deposits into your global account for testing payment flows and account balance management.`,
62
+ SIMULATE_TRANSFER_UPDATE: `Simulate a transfer status update in the authenticated Sandbox account.
63
+
64
+ TRANSFER STATUS LIFECYCLE:
65
+ To simulate a successful transfer completion, progress through these states in order:
66
+ 1. SENT (transfer sent to beneficiary's bank)
67
+ 3. PAID (transfer successfully completed - final status)`,
68
+ VALIDATE_REQUEST_SCHEMA: `Validate generated code that makes API requests to the Airwallex API.
69
+ Validates generated code that involves making API requests to Airwallex API for correctness and quality.
70
+ **REQUIRED WORKFLOW**: When you generate any code that involves Airwallex APIs for the user, you MUST:
71
+ (1) Generate the code,
72
+ (2) Immediately call this validation tool,
73
+ (3) Review validation results,
74
+ (4) Fix any issues found,
75
+ (5) Validate again if changes were made,
76
+ (6) Only present code to user after passing validation.
77
+ Never skip validation - it ensures that users are protected from broken or unsafe code.
78
+ * This tool does NOT execute the API request or return live data. It ONLY checks if the request made (as a result of running the generated code) is valid according to the schema.
79
+ * If the request requires input, use sensible example values that match the data type used in the code.
80
+ * If the code uses variables, use placeholder values that match the expected format.
81
+ * If the request body is dynamically generated, perform a validation for each type of variation
82
+ * DO NOT use real or sensitive data.
83
+ * if AIRWALLEX_API_VERSION is not set, use "latest" as the default version.
84
+
85
+ This tool validates the request body, headers, query parameters, HTTP method, and route against the Airwallex API schema for the specified version. It helps ensure that requests are correctly formed before sending them to the Airwallex API.`,
86
+ };
package/dist/index.js ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
41
+ const fs = __importStar(require("fs"));
42
+ const path_1 = __importDefault(require("path"));
43
+ const server_1 = require("./server");
44
+ const version_1 = require("./utils/version");
45
+ (async () => {
46
+ const appVersion = (0, version_1.getVersion)(__dirname);
47
+ const bestPracticesPath = path_1.default.join(__dirname, "integration-best-practices.md");
48
+ const integrationBestPracticesContent = fs.readFileSync(bestPracticesPath, "utf-8");
49
+ const server = (0, server_1.buildServer)({
50
+ appVersion,
51
+ integrationBestPracticesContent: integrationBestPracticesContent,
52
+ });
53
+ const transport = new stdio_js_1.StdioServerTransport();
54
+ await server.connect(transport);
55
+ })();
@@ -0,0 +1,15 @@
1
+ # Airwallex Integration Best Practices
2
+
3
+ ## General
4
+
5
+ - Check how to authenticate with the API and obtain a bearer token for API authorization using the `search_public_docs` MCP tool with a suitable `source` parameter. DO NOT hallucinate or assume.
6
+ - Check how to initialise the Airwallex.js SDK using the `search_public_docs` MCP tool with a suitable `source` parameter. DO NOT hallucinate or assume.
7
+ - Unless requested explicitly by the user, always use the most recent version of the API
8
+
9
+ ## Payments
10
+
11
+ - In the Airwallex APIs & SDKs, request fields related to money are NOT represented in minor units by default unless explicitly specified
12
+ - For the Javascript browser library, if the user prefers a direct integration with a CDN, use `https://static.airwallex.com/components/sdk/v1/index.js` and NOT `https://checkout.airwallex.com/assets/elements.bundle.min.js` unless explicitly requested by the user. The latter is a legacy library that is considered deprecated. When the user explicitly asks for it, advise them to consider migrating.
13
+ - For the Javascript browser library, if the user prefers to use a package from the `npm` registry with `npm`, `yarn` or the `pnpm` CLI tools, use `@airwallex/components-sdk` instead of `airwallex-payment-elements` unless explicitly requested by the user. The latter is a legacy package that is considered deprecated. When the user explicitly asks for it, advise them to consider migrating.
14
+ - For a robust and complete payments integration, always consider supporting setting up webhooks.
15
+ - If the payment integration involves redirect-style payment methods, remember to configure a return URL in your payment intent and set up a URL handler on the server for the same. In the URL handler, retrieve the payment intent and check its status. If the status is non-terminal, repeat until a terminal status is observed and decide what to do next based on that. For Drop-in element integrations, you can consider doing this by default since multiple payment methods can be accepted through that integration.
package/dist/server.js ADDED
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildServer = void 0;
4
+ const node_sdk_1 = require("@airwallex/node-sdk");
5
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
6
+ const config_1 = require("./constants/config");
7
+ const AirTrackerClient_1 = require("./services/AirTrackerClient");
8
+ const createBillingPrice_1 = require("./tools/createBillingPrice");
9
+ const createPaymentLink_1 = require("./tools/createPaymentLink");
10
+ const createTransfer_1 = require("./tools/createTransfer");
11
+ const getFxQuote_1 = require("./tools/getFxQuote");
12
+ const index_1 = require("./tools/index");
13
+ const listBeneficiaries_1 = require("./tools/listBeneficiaries");
14
+ const listPaymentLinks_1 = require("./tools/listPaymentLinks");
15
+ const listTransfers_1 = require("./tools/listTransfers");
16
+ const simulateTransferResult_1 = require("./tools/simulateTransferResult");
17
+ const withTelemetry_1 = require("./utils/withTelemetry");
18
+ const awxEnv = "demo";
19
+ const disableTelemetry = process.env.DISABLE_TELEMETRY === "true" ||
20
+ process.env.DISABLE_TELEMETRY === "1";
21
+ const buildServer = ({ appVersion, integrationBestPracticesContent, }) => {
22
+ const server = new mcp_js_1.McpServer({
23
+ name: "Airwallex",
24
+ version: appVersion,
25
+ });
26
+ const airTrackerClient = new AirTrackerClient_1.AirTrackerClient(config_1.BASE_URLS.telemetry, awxEnv, appVersion);
27
+ server.registerTool(index_1.retrieveDocsToolConfig.name, index_1.retrieveDocsToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.retrieveDocsToolConfig.name, async (args) => (0, index_1.executeRetrieveDocs)(args, appVersion), { disableTelemetry, telemetryClient: airTrackerClient }));
28
+ server.registerTool(index_1.readBestPracticesToolConfig.name, index_1.readBestPracticesToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.readBestPracticesToolConfig.name, async () => (0, index_1.executeReadBestPractices)(integrationBestPracticesContent), { disableTelemetry, telemetryClient: airTrackerClient }));
29
+ if (process.env.AIRWALLEX_SANDBOX_CLIENT_ID &&
30
+ process.env.AIRWALLEX_SANDBOX_API_KEY) {
31
+ const airwallex = new node_sdk_1.Airwallex({
32
+ apiKey: process.env.AIRWALLEX_SANDBOX_API_KEY,
33
+ clientId: process.env.AIRWALLEX_SANDBOX_CLIENT_ID,
34
+ env: "demo",
35
+ });
36
+ server.registerTool(index_1.listGlobalAccountsToolConfig.name, index_1.listGlobalAccountsToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.listGlobalAccountsToolConfig.name, async () => (0, index_1.executeListGlobalAccounts)(airwallex), { disableTelemetry, telemetryClient: airTrackerClient }));
37
+ server.registerTool(index_1.getAccountBalancesToolConfig.name, index_1.getAccountBalancesToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.getAccountBalancesToolConfig.name, async () => (0, index_1.executeGetAccountBalances)(airwallex), { disableTelemetry, telemetryClient: airTrackerClient }));
38
+ server.registerTool(index_1.simulateDepositToolConfig.name, index_1.simulateDepositToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.simulateDepositToolConfig.name, async (args) => (0, index_1.executeSimulateDeposit)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
39
+ server.registerTool(index_1.listBillingProductsToolConfig.name, index_1.listBillingProductsToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.listBillingProductsToolConfig.name, async () => (0, index_1.executeListBillingProducts)(airwallex), { disableTelemetry, telemetryClient: airTrackerClient }));
40
+ server.registerTool(index_1.listBillingPricesToolConfig.name, index_1.listBillingPricesToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.listBillingPricesToolConfig.name, async (args) => (0, index_1.executeListBillingPrices)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
41
+ server.registerTool(index_1.createBillingProductToolConfig.name, index_1.createBillingProductToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.createBillingProductToolConfig.name, async (args) => (0, index_1.executeCreateBillingProduct)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
42
+ server.registerTool(createBillingPrice_1.createBillingPriceToolConfig.name, createBillingPrice_1.createBillingPriceToolConfig, (0, withTelemetry_1.withToolTelemetry)(createBillingPrice_1.createBillingPriceToolConfig.name, async (args) => (0, createBillingPrice_1.executeCreateBillingPrice)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
43
+ server.registerTool(index_1.createBillingCheckoutToolConfig.name, index_1.createBillingCheckoutToolConfig, (0, withTelemetry_1.withToolTelemetry)(index_1.createBillingCheckoutToolConfig.name, async (args) => (0, index_1.executeCreateBillingCheckout)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
44
+ server.registerTool(getFxQuote_1.getFxQuoteToolConfig.name, getFxQuote_1.getFxQuoteToolConfig, (0, withTelemetry_1.withToolTelemetry)(getFxQuote_1.getFxQuoteToolConfig.name, async (args) => (0, getFxQuote_1.executeGetFxQuote)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
45
+ server.registerTool(listTransfers_1.listTransfersToolConfig.name, listTransfers_1.listTransfersToolConfig, (0, withTelemetry_1.withToolTelemetry)(listTransfers_1.listTransfersToolConfig.name, async () => (0, listTransfers_1.executeListTransfers)(airwallex), { disableTelemetry, telemetryClient: airTrackerClient }));
46
+ server.registerTool(createTransfer_1.createTransferToolConfig.name, createTransfer_1.createTransferToolConfig, (0, withTelemetry_1.withToolTelemetry)(createTransfer_1.createTransferToolConfig.name, async (args) => (0, createTransfer_1.executeCreateTransfer)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
47
+ server.registerTool(listBeneficiaries_1.listBeneficiariesToolConfig.name, listBeneficiaries_1.listBeneficiariesToolConfig, (0, withTelemetry_1.withToolTelemetry)(listBeneficiaries_1.listBeneficiariesToolConfig.name, async () => (0, listBeneficiaries_1.executeListBeneficiaries)(airwallex), { disableTelemetry, telemetryClient: airTrackerClient }));
48
+ server.registerTool(simulateTransferResult_1.simulateTransferUpdateToolConfig.name, simulateTransferResult_1.simulateTransferUpdateToolConfig, (0, withTelemetry_1.withToolTelemetry)(simulateTransferResult_1.simulateTransferUpdateToolConfig.name, async (args) => (0, simulateTransferResult_1.executeSimulateTransferUpdate)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
49
+ server.registerTool(createPaymentLink_1.createPaymentLinkToolConfig.name, createPaymentLink_1.createPaymentLinkToolConfig, (0, withTelemetry_1.withToolTelemetry)(createPaymentLink_1.createPaymentLinkToolConfig.name, async (args) => (0, createPaymentLink_1.executeCreatePaymentLink)(airwallex, args), { disableTelemetry, telemetryClient: airTrackerClient }));
50
+ server.registerTool(listPaymentLinks_1.listPaymentLinksToolConfig.name, listPaymentLinks_1.listPaymentLinksToolConfig, (0, withTelemetry_1.withToolTelemetry)(listPaymentLinks_1.listPaymentLinksToolConfig.name, async () => (0, listPaymentLinks_1.executeListPaymentLinks)(airwallex), { disableTelemetry, telemetryClient: airTrackerClient }));
51
+ }
52
+ return server;
53
+ };
54
+ exports.buildServer = buildServer;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AirTrackerClient = void 0;
7
+ const os_1 = __importDefault(require("os"));
8
+ const uuid_1 = require("uuid");
9
+ const device_1 = require("../utils/device");
10
+ const DEFAULT_COMMON_DATA = {
11
+ accountId: "unknown",
12
+ apiVersion: "0.0.0",
13
+ appName: "awx-dev-mcp-server-local",
14
+ appVersion: "0.0.0",
15
+ deviceId: (0, device_1.extractDeviceId)(),
16
+ env: "unknown",
17
+ networkType: "unknown",
18
+ platform: os_1.default.platform(),
19
+ sessionId: (0, uuid_1.v4)(),
20
+ };
21
+ class AirTrackerClient {
22
+ baseUrl;
23
+ appVersion;
24
+ commonData;
25
+ constructor(baseUrl, awxEnv, appVersion) {
26
+ this.baseUrl = baseUrl;
27
+ this.appVersion = appVersion;
28
+ this.commonData = {
29
+ ...DEFAULT_COMMON_DATA,
30
+ appVersion: this.appVersion,
31
+ env: awxEnv,
32
+ };
33
+ }
34
+ logEvent(eventName, properties) {
35
+ const logData = {
36
+ eventName,
37
+ ...properties,
38
+ };
39
+ fetch(`${this.baseUrl}/airtracker/logs`, {
40
+ body: JSON.stringify({
41
+ commonData: this.commonData,
42
+ data: [{ ...logData, severity: "info" }],
43
+ }),
44
+ headers: {
45
+ "Content-Type": "application/json",
46
+ "User-Agent": "",
47
+ },
48
+ method: "POST",
49
+ signal: AbortSignal.timeout(2000),
50
+ })
51
+ .then(async (response) => {
52
+ if (!response.ok) {
53
+ console.error(`HTTP error! status: ${response.status}, ${await response.text()}`);
54
+ }
55
+ })
56
+ .catch((error) => {
57
+ console.error(`Error logging event ${eventName}:`, error.message);
58
+ });
59
+ }
60
+ }
61
+ exports.AirTrackerClient = AirTrackerClient;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBillingCheckoutToolConfig = exports.createBillingCheckoutSchema = void 0;
4
+ exports.executeCreateBillingCheckout = executeCreateBillingCheckout;
5
+ const uuid_1 = require("uuid");
6
+ const zod_1 = require("zod");
7
+ const descriptions_1 = require("../constants/descriptions");
8
+ exports.createBillingCheckoutSchema = zod_1.z.object({
9
+ mode: zod_1.z.enum(["PAYMENT", "SUBSCRIPTION"], {
10
+ errorMap: () => ({
11
+ message: "Mode must be either PAYMENT or SUBSCRIPTION",
12
+ }),
13
+ }),
14
+ price_id: zod_1.z.string().min(1, "Price ID is required"),
15
+ quantity: zod_1.z
16
+ .number()
17
+ .int()
18
+ .positive("Quantity must be a positive integer")
19
+ .default(1),
20
+ success_url: zod_1.z
21
+ .string()
22
+ .url("Success URL must be a valid URL")
23
+ .default("https://demo.airwallex.com")
24
+ .optional(),
25
+ });
26
+ async function executeCreateBillingCheckout(airwallex, args) {
27
+ try {
28
+ const requestId = (0, uuid_1.v4)();
29
+ const requestBody = {
30
+ line_items: [
31
+ {
32
+ price_id: args.price_id,
33
+ quantity: args.quantity,
34
+ },
35
+ ],
36
+ mode: args.mode,
37
+ request_id: requestId,
38
+ success_url: args.success_url,
39
+ };
40
+ if (args.mode === "SUBSCRIPTION") {
41
+ requestBody.subscription_data = {};
42
+ }
43
+ else if (args.mode === "PAYMENT") {
44
+ requestBody.invoice_data = {};
45
+ }
46
+ const response = (await airwallex.post("/api/v1/billing_checkouts/create", requestBody));
47
+ return {
48
+ content: [
49
+ {
50
+ text: JSON.stringify(response, null, 2),
51
+ type: "text",
52
+ },
53
+ ],
54
+ };
55
+ }
56
+ catch (error) {
57
+ const statusCode = error?.status || error?.statusCode || 500;
58
+ const errorMessage = error?.message || "Unknown error occurred";
59
+ throw new Error(`Failed to create billing checkout (${statusCode}): ${errorMessage}`);
60
+ }
61
+ }
62
+ exports.createBillingCheckoutToolConfig = {
63
+ annotations: {
64
+ openWorldHint: true,
65
+ readOnlyHint: false,
66
+ title: "Create billing checkout",
67
+ },
68
+ description: descriptions_1.TOOL_DESCRIPTIONS.CREATE_BILLING_CHECKOUT,
69
+ inputSchema: exports.createBillingCheckoutSchema,
70
+ name: "create_billing_checkout",
71
+ };
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createBillingPriceToolConfig = exports.createBillingPriceSchema = void 0;
4
+ exports.executeCreateBillingPrice = executeCreateBillingPrice;
5
+ const uuid_1 = require("uuid");
6
+ const zod_1 = require("zod");
7
+ const descriptions_1 = require("../constants/descriptions");
8
+ exports.createBillingPriceSchema = zod_1.z
9
+ .object({
10
+ amount: zod_1.z.number().positive("Amount must be positive"),
11
+ currency: zod_1.z
12
+ .string()
13
+ .length(3, "Currency must be a 3-letter ISO-4217 code")
14
+ .toUpperCase(),
15
+ pricing_model: zod_1.z
16
+ .enum(["FLAT", "PER_UNIT"], {
17
+ errorMap: () => ({
18
+ message: "Pricing model must be either FLAT or PER_UNIT",
19
+ }),
20
+ })
21
+ .describe("PER_UNIT multiplies by quantity, FLAT does not. Prefer PER_UNIT for ONE_OFF and FLAT for RECURRING."),
22
+ product_id: zod_1.z.string().min(1, "Product ID is required"),
23
+ recurring_period: zod_1.z
24
+ .number()
25
+ .int()
26
+ .positive("Recurring period must be a positive integer")
27
+ .optional(),
28
+ recurring_period_unit: zod_1.z
29
+ .enum(["DAY", "WEEK", "MONTH", "YEAR"], {
30
+ errorMap: () => ({
31
+ message: "Recurring period unit must be one of: DAY, WEEK, MONTH, YEAR",
32
+ }),
33
+ })
34
+ .optional(),
35
+ type: zod_1.z.enum(["ONE_OFF", "RECURRING"], {
36
+ errorMap: () => ({ message: "Type must be either ONE_OFF or RECURRING" }),
37
+ }),
38
+ })
39
+ .refine((data) => {
40
+ if (data.type === "RECURRING") {
41
+ return (data.recurring_period !== undefined &&
42
+ data.recurring_period_unit !== undefined);
43
+ }
44
+ return true;
45
+ }, {
46
+ message: "recurring_period and recurring_period_unit are required when type is RECURRING",
47
+ path: ["recurring_period"],
48
+ })
49
+ .refine((data) => {
50
+ if (data.type === "ONE_OFF") {
51
+ return (data.recurring_period === undefined &&
52
+ data.recurring_period_unit === undefined);
53
+ }
54
+ return true;
55
+ }, {
56
+ message: "recurring_period and recurring_period_unit must not be provided when type is ONE_OFF",
57
+ path: ["recurring_period"],
58
+ });
59
+ async function executeCreateBillingPrice(airwallex, args) {
60
+ try {
61
+ const requestId = (0, uuid_1.v4)();
62
+ const requestBody = {
63
+ currency: args.currency,
64
+ pricing_model: args.pricing_model,
65
+ product_id: args.product_id,
66
+ request_id: requestId,
67
+ type: args.type,
68
+ };
69
+ if (args.pricing_model === "FLAT") {
70
+ requestBody.flat_amount = args.amount;
71
+ }
72
+ else {
73
+ requestBody.unit_amount = args.amount;
74
+ }
75
+ if (args.type === "RECURRING") {
76
+ requestBody.recurring = {
77
+ period: args.recurring_period,
78
+ period_unit: args.recurring_period_unit,
79
+ };
80
+ }
81
+ const response = (await airwallex.post("/api/v1/prices/create", requestBody));
82
+ return {
83
+ content: [
84
+ {
85
+ text: JSON.stringify(response, null, 2),
86
+ type: "text",
87
+ },
88
+ ],
89
+ };
90
+ }
91
+ catch (error) {
92
+ const statusCode = error?.status || error?.statusCode || 500;
93
+ const errorMessage = error?.message || "Unknown error occurred";
94
+ throw new Error(`Failed to create billing price (${statusCode}): ${errorMessage}`);
95
+ }
96
+ }
97
+ exports.createBillingPriceToolConfig = {
98
+ annotations: {
99
+ openWorldHint: true,
100
+ readOnlyHint: false,
101
+ title: "Create billing price",
102
+ },
103
+ description: descriptions_1.TOOL_DESCRIPTIONS.CREATE_BILLING_PRICE,
104
+ inputSchema: exports.createBillingPriceSchema,
105
+ name: "create_billing_price",
106
+ };