stables-mcp-server 1.3.0 → 2.0.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/README.md +52 -41
- package/build/index.d.ts +1 -1
- package/build/index.js +4 -4
- package/build/index.js.map +1 -1
- package/build/lib/stables-client.d.ts +215 -127
- package/build/lib/stables-client.d.ts.map +1 -1
- package/build/lib/stables-client.js +66 -54
- package/build/lib/stables-client.js.map +1 -1
- package/build/tools/api-keys.d.ts.map +1 -1
- package/build/tools/api-keys.js +7 -3
- package/build/tools/api-keys.js.map +1 -1
- package/build/tools/customers.d.ts +1 -1
- package/build/tools/customers.d.ts.map +1 -1
- package/build/tools/customers.js +240 -107
- package/build/tools/customers.js.map +1 -1
- package/build/tools/payment-methods.d.ts +12 -0
- package/build/tools/payment-methods.d.ts.map +1 -0
- package/build/tools/payment-methods.js +119 -0
- package/build/tools/payment-methods.js.map +1 -0
- package/build/tools/quotes.d.ts +5 -1
- package/build/tools/quotes.d.ts.map +1 -1
- package/build/tools/quotes.js +103 -59
- package/build/tools/quotes.js.map +1 -1
- package/build/tools/transfers.d.ts +4 -1
- package/build/tools/transfers.d.ts.map +1 -1
- package/build/tools/transfers.js +212 -100
- package/build/tools/transfers.js.map +1 -1
- package/build/tools/virtual-accounts.d.ts +1 -1
- package/build/tools/virtual-accounts.d.ts.map +1 -1
- package/build/tools/virtual-accounts.js +56 -70
- package/build/tools/virtual-accounts.js.map +1 -1
- package/build/tools/webhooks.d.ts +1 -1
- package/build/tools/webhooks.d.ts.map +1 -1
- package/build/tools/webhooks.js +38 -18
- package/build/tools/webhooks.js.map +1 -1
- package/package.json +10 -3
- package/build/tools/notifications.d.ts +0 -8
- package/build/tools/notifications.d.ts.map +0 -1
- package/build/tools/notifications.js +0 -125
- package/build/tools/notifications.js.map +0 -1
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payment method validation for the Stables MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors POST /api/v1/payment-methods/validate. The endpoint always answers
|
|
5
|
+
* 200 and reports the outcome in the body, so this is the cheap way to discover
|
|
6
|
+
* what a corridor requires — particularly the enhanced beneficiary fields on
|
|
7
|
+
* AED, CAD, EUR, GBP, MXN and USD — before committing a short-lived quote.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
export function registerPaymentMethodTools(server, client) {
|
|
11
|
+
server.tool("validate_payment_method", "Check bank payout details against the destination currency's rules without creating a quote or transfer. " +
|
|
12
|
+
"Use this before create_transfer: it reports exactly which fields are missing or wrong, including the " +
|
|
13
|
+
"recipient type, date of birth and beneficiary address required for AED, CAD, EUR, GBP, MXN and USD.", {
|
|
14
|
+
network: z
|
|
15
|
+
.enum(["bank", "swift", "payid"])
|
|
16
|
+
.describe("Payout network to validate against: local rails ('bank'), 'swift', or 'payid'"),
|
|
17
|
+
accountHolderName: z
|
|
18
|
+
.string()
|
|
19
|
+
.describe("Account holder's full name, or the company name for a business beneficiary"),
|
|
20
|
+
bankName: z.string().describe("Name of the destination bank"),
|
|
21
|
+
bankCountry: z.string().describe("Bank country, ISO 2-letter"),
|
|
22
|
+
currency: z.string().describe("Payout currency, ISO 3-letter"),
|
|
23
|
+
recipientType: z.enum(["individual", "business"]).optional().describe("Beneficiary type"),
|
|
24
|
+
dateOfBirth: z.string().optional().describe("Beneficiary date of birth, YYYY-MM-DD"),
|
|
25
|
+
addressStreet: z.string().optional().describe("Beneficiary street address"),
|
|
26
|
+
addressCity: z.string().optional().describe("Beneficiary city"),
|
|
27
|
+
addressState: z.string().optional().describe("Beneficiary state or region"),
|
|
28
|
+
addressPostalCode: z.string().optional().describe("Beneficiary postal code"),
|
|
29
|
+
addressCountry: z.string().optional().describe("Beneficiary country, ISO 2-letter"),
|
|
30
|
+
iban: z.string().optional().describe("IBAN"),
|
|
31
|
+
accountNumber: z.string().optional().describe("Bank account number"),
|
|
32
|
+
accountType: z.enum(["savings", "checking", "payment"]).optional().describe("Account type"),
|
|
33
|
+
swiftCode: z.string().optional().describe("SWIFT code"),
|
|
34
|
+
bicCode: z.string().optional().describe("BIC"),
|
|
35
|
+
routingNumber: z.string().optional().describe("ABA routing number (US)"),
|
|
36
|
+
sortCode: z.string().optional().describe("Sort code (UK)"),
|
|
37
|
+
ifscCode: z.string().optional().describe("IFSC code (India)"),
|
|
38
|
+
bsbCode: z.string().optional().describe("BSB code (Australia)"),
|
|
39
|
+
bankCode: z.string().optional().describe("Bank code / institution number (CAD)"),
|
|
40
|
+
branchCode: z.string().optional().describe("Branch code / transit number (CAD)"),
|
|
41
|
+
cnaps: z.string().optional().describe("CNAPS (China)"),
|
|
42
|
+
}, async (input) => {
|
|
43
|
+
try {
|
|
44
|
+
const hasAddress = Boolean(input.addressStreet &&
|
|
45
|
+
input.addressCity &&
|
|
46
|
+
input.addressState &&
|
|
47
|
+
input.addressPostalCode &&
|
|
48
|
+
input.addressCountry);
|
|
49
|
+
const destination = {
|
|
50
|
+
type: "bank",
|
|
51
|
+
account_holder_name: input.accountHolderName,
|
|
52
|
+
bank_name: input.bankName,
|
|
53
|
+
bank_country: input.bankCountry,
|
|
54
|
+
currency: input.currency,
|
|
55
|
+
...(input.recipientType && { recipient_type: input.recipientType }),
|
|
56
|
+
...(input.dateOfBirth && { date_of_birth: input.dateOfBirth }),
|
|
57
|
+
...(hasAddress && {
|
|
58
|
+
address: {
|
|
59
|
+
street: input.addressStreet,
|
|
60
|
+
city: input.addressCity,
|
|
61
|
+
state: input.addressState,
|
|
62
|
+
postal_code: input.addressPostalCode,
|
|
63
|
+
country: input.addressCountry,
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
...(input.iban && { iban: input.iban }),
|
|
67
|
+
...(input.accountNumber && { account_number: input.accountNumber }),
|
|
68
|
+
...(input.accountType && { account_type: input.accountType }),
|
|
69
|
+
...(input.swiftCode && { swift_code: input.swiftCode }),
|
|
70
|
+
...(input.bicCode && { bic_code: input.bicCode }),
|
|
71
|
+
...(input.routingNumber && { aba_code: input.routingNumber }),
|
|
72
|
+
...(input.sortCode && { sort_code: input.sortCode }),
|
|
73
|
+
...(input.ifscCode && { ifsc_code: input.ifscCode }),
|
|
74
|
+
...(input.bsbCode && { bsb_code: input.bsbCode }),
|
|
75
|
+
...(input.bankCode && { bank_code: input.bankCode }),
|
|
76
|
+
...(input.branchCode && { branch_code: input.branchCode }),
|
|
77
|
+
...(input.cnaps && { cnaps: input.cnaps }),
|
|
78
|
+
};
|
|
79
|
+
const result = await client.validatePaymentMethod(input.network, destination);
|
|
80
|
+
if (result.valid) {
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: `These payout details are valid for ${input.currency} over ${input.network}. Safe to use with create_transfer.`,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const problems = (result.errors ?? [])
|
|
91
|
+
.map((e) => `- ${e.field ? `${e.field}: ` : ""}${e.message} (${e.code})`)
|
|
92
|
+
.join("\n");
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: "text",
|
|
97
|
+
text: `These payout details are NOT valid for ${input.currency} over ${input.network}:
|
|
98
|
+
|
|
99
|
+
${problems || "- No detail returned."}
|
|
100
|
+
|
|
101
|
+
Collect the missing information before calling create_transfer.`,
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
return {
|
|
108
|
+
content: [
|
|
109
|
+
{
|
|
110
|
+
type: "text",
|
|
111
|
+
text: `Failed to validate payment method: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
isError: true,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=payment-methods.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-methods.js","sourceRoot":"","sources":["../../src/tools/payment-methods.ts"],"names":[],"mappings":"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"}
|
package/build/tools/quotes.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Quote Tools for Stables MCP Server
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Mirrors POST /api/v1/quotes and GET /api/v1/quotes/:id as of 2026-07-30. The
|
|
5
|
+
* request takes `source`/`destination` (was `from`/`to`), the response is the
|
|
6
|
+
* quote itself with no `{ quote }` wrapper, and `paymentMethodType` is gone —
|
|
7
|
+
* the payment network is now `destination.network`.
|
|
4
8
|
*/
|
|
5
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
10
|
import { StablesApiClient } from "../lib/stables-client.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.d.ts","sourceRoot":"","sources":["../../src/tools/quotes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"quotes.d.ts","sourceRoot":"","sources":["../../src/tools/quotes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAS,MAAM,0BAA0B,CAAC;AAoCnE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,QA4J7E"}
|
package/build/tools/quotes.js
CHANGED
|
@@ -1,66 +1,115 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Quote Tools for Stables MCP Server
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Mirrors POST /api/v1/quotes and GET /api/v1/quotes/:id as of 2026-07-30. The
|
|
5
|
+
* request takes `source`/`destination` (was `from`/`to`), the response is the
|
|
6
|
+
* quote itself with no `{ quote }` wrapper, and `paymentMethodType` is gone —
|
|
7
|
+
* the payment network is now `destination.network`.
|
|
4
8
|
*/
|
|
5
9
|
import { z } from "zod";
|
|
10
|
+
const LIVE_NETWORKS = [
|
|
11
|
+
"arbitrum",
|
|
12
|
+
"avalanche",
|
|
13
|
+
"base",
|
|
14
|
+
"ethereum",
|
|
15
|
+
"optimism",
|
|
16
|
+
"polygon",
|
|
17
|
+
"solana",
|
|
18
|
+
"tron",
|
|
19
|
+
];
|
|
20
|
+
function secondsUntil(iso) {
|
|
21
|
+
return Math.max(0, Math.floor((new Date(iso).getTime() - Date.now()) / 1000));
|
|
22
|
+
}
|
|
23
|
+
function describeAmount(side) {
|
|
24
|
+
return `${side.amount} ${side.currency}${side.network ? ` (${side.network})` : ""}`;
|
|
25
|
+
}
|
|
26
|
+
function feeLines(quote) {
|
|
27
|
+
const fees = quote.fees;
|
|
28
|
+
let out = `Total Fees: ${fees.total_fee.amount} ${fees.total_fee.currency}`;
|
|
29
|
+
if (fees.fx_fee)
|
|
30
|
+
out += `\n FX Fee: ${fees.fx_fee.amount} ${fees.fx_fee.currency}`;
|
|
31
|
+
if (fees.platform_fee)
|
|
32
|
+
out += `\n Platform Fee: ${fees.platform_fee.amount} ${fees.platform_fee.currency}`;
|
|
33
|
+
if (fees.payment_method_fee)
|
|
34
|
+
out += `\n Payment Method Fee: ${fees.payment_method_fee.amount} ${fees.payment_method_fee.currency}`;
|
|
35
|
+
if (fees.network_fee)
|
|
36
|
+
out += `\n Network Fee: ${fees.network_fee.amount} ${fees.network_fee.currency}`;
|
|
37
|
+
if (fees.integrator_fee)
|
|
38
|
+
out += `\n Integrator Fee: ${fees.integrator_fee.amount} ${fees.integrator_fee.currency}`;
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
6
41
|
export function registerQuoteTools(server, client) {
|
|
7
42
|
// Create Quote
|
|
8
|
-
server.tool("create_quote", "Get a quote for currency exchange
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
43
|
+
server.tool("create_quote", "Get a quote for a currency exchange: the rate, the fees, and what the customer receives. Quotes are short-lived, so create one immediately before the transfer. " +
|
|
44
|
+
"Off-ramp converts a stablecoin to fiat (source network required, destination country required); on-ramp converts fiat to a stablecoin (destination address required). " +
|
|
45
|
+
"Set preview to price without committing.", {
|
|
46
|
+
sourceCurrency: z
|
|
47
|
+
.string()
|
|
48
|
+
.describe("Source currency: a stablecoin for off-ramp (e.g. 'USDT'), fiat for on-ramp"),
|
|
49
|
+
sourceAmount: z.string().describe("Amount to convert, in major units (e.g. '125.75')"),
|
|
50
|
+
sourceNetwork: z
|
|
51
|
+
.enum(LIVE_NETWORKS)
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Blockchain network of the source. Required for off-ramp, omitted for on-ramp"),
|
|
54
|
+
destinationCurrency: z
|
|
55
|
+
.string()
|
|
56
|
+
.describe("Destination currency: fiat for off-ramp (e.g. 'EUR'), a stablecoin for on-ramp"),
|
|
57
|
+
destinationCountry: z
|
|
58
|
+
.string()
|
|
59
|
+
.optional()
|
|
60
|
+
.describe("Destination country, ISO 2-letter. Required for off-ramp"),
|
|
61
|
+
destinationNetwork: z
|
|
62
|
+
.string()
|
|
63
|
+
.optional()
|
|
64
|
+
.describe("Off-ramp: payment network, 'swift' or 'bank'. On-ramp: blockchain network. Replaces the old paymentMethodType"),
|
|
65
|
+
destinationAddress: z
|
|
66
|
+
.string()
|
|
67
|
+
.optional()
|
|
68
|
+
.describe("Destination wallet address. Required for on-ramp"),
|
|
69
|
+
preview: z
|
|
70
|
+
.boolean()
|
|
71
|
+
.optional()
|
|
72
|
+
.describe("Price the quote without persisting it — use to show an estimate"),
|
|
73
|
+
metadata: z.record(z.string()).optional().describe("Optional metadata"),
|
|
74
|
+
}, async (input) => {
|
|
17
75
|
try {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
network: fromNetwork,
|
|
76
|
+
const quote = await client.createQuote({
|
|
77
|
+
source: {
|
|
78
|
+
currency: input.sourceCurrency,
|
|
79
|
+
amount: input.sourceAmount,
|
|
80
|
+
...(input.sourceNetwork && { network: input.sourceNetwork }),
|
|
24
81
|
},
|
|
25
|
-
|
|
26
|
-
currency:
|
|
27
|
-
country:
|
|
28
|
-
|
|
82
|
+
destination: {
|
|
83
|
+
currency: input.destinationCurrency,
|
|
84
|
+
...(input.destinationCountry && { country: input.destinationCountry }),
|
|
85
|
+
...(input.destinationNetwork && { network: input.destinationNetwork }),
|
|
86
|
+
...(input.destinationAddress && { address: input.destinationAddress }),
|
|
29
87
|
},
|
|
88
|
+
...(input.preview !== undefined && { preview: input.preview }),
|
|
89
|
+
...(input.metadata && { metadata: input.metadata }),
|
|
30
90
|
});
|
|
31
|
-
const quote = response.quote;
|
|
32
|
-
const expiresIn = Math.max(0, Math.floor((new Date(quote.expiresAt).getTime() - Date.now()) / 1000));
|
|
33
|
-
let feeDetails = `Total Fees: ${quote.fees.totalFee.amount} ${quote.fees.totalFee.currency}`;
|
|
34
|
-
if (quote.fees.fxFee)
|
|
35
|
-
feeDetails += `\n FX Fee: ${quote.fees.fxFee.amount} ${quote.fees.fxFee.currency}`;
|
|
36
|
-
if (quote.fees.platformFee)
|
|
37
|
-
feeDetails += `\n Platform Fee: ${quote.fees.platformFee.amount} ${quote.fees.platformFee.currency}`;
|
|
38
|
-
if (quote.fees.paymentMethodFee)
|
|
39
|
-
feeDetails += `\n Payment Method Fee: ${quote.fees.paymentMethodFee.amount} ${quote.fees.paymentMethodFee.currency}`;
|
|
40
|
-
if (quote.fees.networkFee)
|
|
41
|
-
feeDetails += `\n Network Fee: ${quote.fees.networkFee.amount} ${quote.fees.networkFee.currency}`;
|
|
42
|
-
if (quote.fees.integratorFee)
|
|
43
|
-
feeDetails += `\n Integrator Fee: ${quote.fees.integratorFee.amount} ${quote.fees.integratorFee.currency}`;
|
|
44
91
|
return {
|
|
45
92
|
content: [
|
|
46
93
|
{
|
|
47
94
|
type: "text",
|
|
48
95
|
text: `Quote created successfully!
|
|
49
96
|
|
|
50
|
-
Quote ID: ${quote.
|
|
97
|
+
Quote ID: ${quote.quote_id}
|
|
51
98
|
Status: ${quote.status}
|
|
52
99
|
|
|
53
100
|
Converting:
|
|
54
|
-
From: ${
|
|
55
|
-
To: ${quote.
|
|
101
|
+
From: ${describeAmount(quote.source)}
|
|
102
|
+
To: ${describeAmount(quote.destination)}
|
|
56
103
|
|
|
57
|
-
Exchange Rate: ${quote.
|
|
58
|
-
${
|
|
104
|
+
Exchange Rate: ${quote.exchange_rate}
|
|
105
|
+
${feeLines(quote)}
|
|
59
106
|
|
|
60
|
-
Expires in: ${
|
|
61
|
-
Expires at: ${quote.
|
|
107
|
+
Expires in: ${secondsUntil(quote.expires_at)} seconds
|
|
108
|
+
Expires at: ${quote.expires_at}
|
|
62
109
|
|
|
63
|
-
|
|
110
|
+
${quote.status === "preview"
|
|
111
|
+
? "This is a preview and was not persisted. Create a real quote before transferring."
|
|
112
|
+
: "To execute it, call 'create_transfer' with this quote ID and the payout destination."}`,
|
|
64
113
|
},
|
|
65
114
|
],
|
|
66
115
|
};
|
|
@@ -82,24 +131,19 @@ To execute this quote, use 'create_transfer' with this quoteId and include bank
|
|
|
82
131
|
quoteId: z.string().describe("The quote ID to look up"),
|
|
83
132
|
}, async ({ quoteId }) => {
|
|
84
133
|
try {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const isUsed = quote.status === "QUOTE_STATUS_USED";
|
|
89
|
-
const isCancelled = quote.status === "QUOTE_STATUS_CANCELLED";
|
|
90
|
-
let statusMessage = "";
|
|
91
|
-
if (isUsed) {
|
|
134
|
+
const quote = await client.getQuote(quoteId);
|
|
135
|
+
let statusMessage;
|
|
136
|
+
if (quote.status === "used") {
|
|
92
137
|
statusMessage = "This quote has already been used to create a transfer.";
|
|
93
138
|
}
|
|
94
|
-
else if (
|
|
139
|
+
else if (quote.status === "cancelled") {
|
|
95
140
|
statusMessage = "This quote has been cancelled.";
|
|
96
141
|
}
|
|
97
|
-
else if (
|
|
142
|
+
else if (quote.status === "expired" || new Date(quote.expires_at) < new Date()) {
|
|
98
143
|
statusMessage = "This quote has expired. Create a new quote to proceed.";
|
|
99
144
|
}
|
|
100
145
|
else {
|
|
101
|
-
|
|
102
|
-
statusMessage = `This quote is active and expires in ${expiresIn} seconds.`;
|
|
146
|
+
statusMessage = `This quote is active and expires in ${secondsUntil(quote.expires_at)} seconds.`;
|
|
103
147
|
}
|
|
104
148
|
return {
|
|
105
149
|
content: [
|
|
@@ -107,18 +151,18 @@ To execute this quote, use 'create_transfer' with this quoteId and include bank
|
|
|
107
151
|
type: "text",
|
|
108
152
|
text: `Quote Details:
|
|
109
153
|
|
|
110
|
-
Quote ID: ${quote.
|
|
154
|
+
Quote ID: ${quote.quote_id}
|
|
111
155
|
Status: ${quote.status}
|
|
112
156
|
|
|
113
157
|
Converting:
|
|
114
|
-
From: ${
|
|
115
|
-
To: ${quote.
|
|
158
|
+
From: ${describeAmount(quote.source)}
|
|
159
|
+
To: ${describeAmount(quote.destination)}
|
|
116
160
|
|
|
117
|
-
Exchange Rate: ${quote.
|
|
118
|
-
Total Fees: ${quote.fees.
|
|
161
|
+
Exchange Rate: ${quote.exchange_rate}
|
|
162
|
+
Total Fees: ${quote.fees.total_fee.amount} ${quote.fees.total_fee.currency}
|
|
119
163
|
|
|
120
|
-
Created: ${quote.
|
|
121
|
-
Expires: ${quote.
|
|
164
|
+
Created: ${quote.created_at}
|
|
165
|
+
Expires: ${quote.expires_at}
|
|
122
166
|
|
|
123
167
|
${statusMessage}`,
|
|
124
168
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quotes.js","sourceRoot":"","sources":["../../src/tools/quotes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"quotes.js","sourceRoot":"","sources":["../../src/tools/quotes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Transfer Tools for Stables MCP Server
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Mirrors POST /api/v1/transfer as of 2026-07-30. The wire is snake_case and the
|
|
5
|
+
* payout target is a discriminated `destination`, not the old nested
|
|
6
|
+
* `paymentMethod.bankTransfer`.
|
|
4
7
|
*/
|
|
5
8
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
9
|
import { StablesApiClient } from "../lib/stables-client.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transfers.d.ts","sourceRoot":"","sources":["../../src/tools/transfers.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"transfers.d.ts","sourceRoot":"","sources":["../../src/tools/transfers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAuC,MAAM,0BAA0B,CAAC;AA2CjG,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,QAyUhF"}
|