@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 +22 -0
- package/README.md +148 -0
- package/dist/constants/config.js +8 -0
- package/dist/constants/descriptions.js +86 -0
- package/dist/index.js +55 -0
- package/dist/integration-best-practices.md +15 -0
- package/dist/server.js +54 -0
- package/dist/services/AirTrackerClient.js +61 -0
- package/dist/tools/createBillingCheckout.js +71 -0
- package/dist/tools/createBillingPrice.js +106 -0
- package/dist/tools/createBillingProduct.js +49 -0
- package/dist/tools/createPaymentLink.js +70 -0
- package/dist/tools/createTransfer.js +83 -0
- package/dist/tools/getBalances.js +35 -0
- package/dist/tools/getFxQuote.js +63 -0
- package/dist/tools/index.js +25 -0
- package/dist/tools/listBeneficiaries.js +38 -0
- package/dist/tools/listBillingPrices.js +46 -0
- package/dist/tools/listBillingProducts.js +38 -0
- package/dist/tools/listGlobalAccounts.js +36 -0
- package/dist/tools/listPaymentLinks.js +38 -0
- package/dist/tools/listTransfers.js +35 -0
- package/dist/tools/readIntegrationBestPractices.js +32 -0
- package/dist/tools/retrieveDocs.js +63 -0
- package/dist/tools/simulateDeposit.js +54 -0
- package/dist/tools/simulateTransferResult.js +42 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/device.js +13 -0
- package/dist/utils/version.js +43 -0
- package/dist/utils/withTelemetry.js +16 -0
- package/package.json +65 -0
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
|
+
[](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
|
+
};
|