stables-mcp-server 1.4.0 → 2.1.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 +70 -48
- package/build/index.js +3 -3
- package/build/index.js.map +1 -1
- package/build/lib/stables-client.d.ts +313 -136
- package/build/lib/stables-client.d.ts.map +1 -1
- package/build/lib/stables-client.js +107 -19
- 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.map +1 -1
- package/build/tools/customers.js +239 -106
- package/build/tools/customers.js.map +1 -1
- 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/sandbox.d.ts +11 -0
- package/build/tools/sandbox.d.ts.map +1 -0
- package/build/tools/sandbox.js +78 -0
- package/build/tools/sandbox.js.map +1 -0
- package/build/tools/transfers.d.ts +4 -1
- package/build/tools/transfers.d.ts.map +1 -1
- package/build/tools/transfers.js +212 -112
- package/build/tools/transfers.js.map +1 -1
- package/build/tools/virtual-accounts.d.ts.map +1 -1
- package/build/tools/virtual-accounts.js +127 -84
- package/build/tools/virtual-accounts.js.map +1 -1
- package/build/tools/webhooks.d.ts.map +1 -1
- package/build/tools/webhooks.js +98 -24
- package/build/tools/webhooks.js.map +1 -1
- package/package.json +12 -4
- 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
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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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox simulation tools for the Stables MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* These endpoints exist only on non-live deployments. Without them an agent
|
|
5
|
+
* cannot drive a deposit to a terminal state in test — a real bank payment would
|
|
6
|
+
* be required — so a test integration could never see a completed payout.
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { StablesApiClient } from "../lib/stables-client.js";
|
|
10
|
+
export declare function registerSandboxTools(server: McpServer, client: StablesApiClient): void;
|
|
11
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/tools/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,gBAAgB,QAuF/E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox simulation tools for the Stables MCP Server.
|
|
3
|
+
*
|
|
4
|
+
* These endpoints exist only on non-live deployments. Without them an agent
|
|
5
|
+
* cannot drive a deposit to a terminal state in test — a real bank payment would
|
|
6
|
+
* be required — so a test integration could never see a completed payout.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
export function registerSandboxTools(server, client) {
|
|
10
|
+
server.tool("simulate_route_deposit", "SANDBOX ONLY. Simulate a fiat deposit into a payment route so you can watch the conversion and payout complete without a real bank transfer. Fails on production.", {
|
|
11
|
+
customerId: z.string().describe("The customer ID"),
|
|
12
|
+
virtualAccountId: z.string().describe("The payment route (virtual account) ID"),
|
|
13
|
+
amount: z.string().describe("Deposit amount in major units (e.g. '500.00')"),
|
|
14
|
+
scenario: z
|
|
15
|
+
.enum(["create_only", "completed", "failed"])
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("'completed' runs the deposit through to payout, 'failed' exercises the failure path, 'create_only' stops at the deposit"),
|
|
18
|
+
senderName: z.string().optional().describe("Name to attribute the deposit to"),
|
|
19
|
+
senderReference: z.string().optional().describe("Payment reference on the deposit"),
|
|
20
|
+
externalDepositId: z.string().optional().describe("Your own reference for this deposit"),
|
|
21
|
+
}, async ({ customerId, virtualAccountId, amount, scenario, senderName, senderReference, externalDepositId, }) => {
|
|
22
|
+
try {
|
|
23
|
+
const result = await client.simulateVirtualAccountDeposit(customerId, virtualAccountId, {
|
|
24
|
+
amount,
|
|
25
|
+
...(scenario && { scenario }),
|
|
26
|
+
...(senderName && { sender_name: senderName }),
|
|
27
|
+
...(senderReference && { sender_reference: senderReference }),
|
|
28
|
+
...(externalDepositId && { external_deposit_id: externalDepositId }),
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: `Simulated deposit of ${amount} into route ${virtualAccountId}:\n\n${JSON.stringify(result, null, 2)}\n\nUse 'get_virtual_account_history' to see the deposit and its payout.`,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
text: `Failed to simulate the deposit: ${error instanceof Error ? error.message : "Unknown error"}\n\nThis endpoint exists only in sandbox — on production it will not be found.`,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
isError: true,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
server.tool("simulate_transfer_deposit", "SANDBOX ONLY. Simulate the customer sending the crypto an off-ramp transfer is waiting on, so the transfer can progress to payout without an on-chain payment. Fails on production.", {
|
|
52
|
+
transferId: z.string().describe("The transfer awaiting funds"),
|
|
53
|
+
}, async ({ transferId }) => {
|
|
54
|
+
try {
|
|
55
|
+
const result = await client.simulateTransferDeposit(transferId);
|
|
56
|
+
return {
|
|
57
|
+
content: [
|
|
58
|
+
{
|
|
59
|
+
type: "text",
|
|
60
|
+
text: `Simulated the inbound deposit for transfer ${transferId}:\n\n${JSON.stringify(result, null, 2)}\n\nUse 'get_transfer' to follow the status.`,
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `Failed to simulate the transfer deposit: ${error instanceof Error ? error.message : "Unknown error"}\n\nThis endpoint exists only in sandbox — on production it will not be found.`,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/tools/sandbox.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"}
|