@codespar/mcp-moonpay 0.1.0 → 0.2.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/dist/index.js +244 -6
- package/package.json +1 -1
- package/server.json +31 -3
- package/src/index.ts +239 -6
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* option for agents paying out in crypto or end users buying crypto with
|
|
10
10
|
* local currency.
|
|
11
11
|
*
|
|
12
|
-
* Tools (
|
|
12
|
+
* Tools (20):
|
|
13
13
|
* get_buy_quote — preview a fiat -> crypto exchange before committing
|
|
14
14
|
* create_buy_transaction — create a buy transaction (fiat -> crypto)
|
|
15
15
|
* get_buy_transaction — retrieve a buy transaction by id
|
|
@@ -17,18 +17,35 @@
|
|
|
17
17
|
* get_sell_quote — preview a crypto -> fiat exchange
|
|
18
18
|
* create_sell_transaction — create a sell transaction (crypto -> fiat)
|
|
19
19
|
* get_sell_transaction — retrieve a sell transaction by id
|
|
20
|
+
* refund_sell_transaction — request a refund on an off-ramp transaction
|
|
20
21
|
* create_customer — create a KYC'd end user
|
|
21
22
|
* get_customer — retrieve a customer by id
|
|
23
|
+
* get_customer_kyc_status — fetch KYC verification status for a customer
|
|
24
|
+
* list_customer_transactions — list all (buy + sell) transactions tied to a customer
|
|
25
|
+
* get_transaction_receipt — fetch a tax-/audit-grade receipt for a completed transaction
|
|
22
26
|
* list_currencies — list supported fiat + crypto assets (dynamic discovery)
|
|
27
|
+
* get_currency — retrieve metadata for a single currency by code
|
|
28
|
+
* list_countries — list supported countries with allowed flows (buy/sell) per geography
|
|
29
|
+
* list_payment_methods — list payment methods supported for a fiat / country pair
|
|
30
|
+
* get_user_country — IP-based geolocation + alpha-3 country code (compliance helper)
|
|
31
|
+
* sign_buy_url — HMAC-sign a hosted-checkout buy widget URL with apiKey + params
|
|
32
|
+
* sign_sell_url — HMAC-sign a hosted-checkout sell widget URL with apiKey + params
|
|
23
33
|
*
|
|
24
34
|
* Authentication
|
|
25
|
-
*
|
|
35
|
+
* REST API requests carry:
|
|
26
36
|
* Authorization: Api-Key <API_KEY>
|
|
27
37
|
* Sandbox vs production is selected by which key you pass; the base URL is the same.
|
|
28
38
|
*
|
|
39
|
+
* Hosted widget URLs (buy.moonpay.com / sell.moonpay.com) are HMAC-SHA256
|
|
40
|
+
* signed using the publishable key + secret key (see sign_buy_url / sign_sell_url).
|
|
41
|
+
*
|
|
29
42
|
* Environment
|
|
30
|
-
* MOONPAY_API_KEY
|
|
31
|
-
*
|
|
43
|
+
* MOONPAY_API_KEY — REST API key (required, secret)
|
|
44
|
+
* MOONPAY_PUBLISHABLE_KEY — publishable key for widget URLs (optional, used by sign_*_url)
|
|
45
|
+
* MOONPAY_SECRET_KEY — secret key for HMAC signing widget URLs (optional, used by sign_*_url)
|
|
46
|
+
* MOONPAY_BASE_URL — optional; defaults to https://api.moonpay.com
|
|
47
|
+
* MOONPAY_BUY_WIDGET_URL — optional; defaults to https://buy.moonpay.com
|
|
48
|
+
* MOONPAY_SELL_WIDGET_URL — optional; defaults to https://sell.moonpay.com
|
|
32
49
|
*
|
|
33
50
|
* Docs: https://dev.moonpay.com
|
|
34
51
|
*/
|
|
@@ -37,8 +54,13 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
37
54
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
38
55
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
39
56
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
57
|
+
import { createHmac } from "node:crypto";
|
|
40
58
|
const API_KEY = process.env.MOONPAY_API_KEY || "";
|
|
59
|
+
const PUBLISHABLE_KEY = process.env.MOONPAY_PUBLISHABLE_KEY || "";
|
|
60
|
+
const SECRET_KEY = process.env.MOONPAY_SECRET_KEY || "";
|
|
41
61
|
const BASE_URL = process.env.MOONPAY_BASE_URL || "https://api.moonpay.com";
|
|
62
|
+
const BUY_WIDGET_URL = process.env.MOONPAY_BUY_WIDGET_URL || "https://buy.moonpay.com";
|
|
63
|
+
const SELL_WIDGET_URL = process.env.MOONPAY_SELL_WIDGET_URL || "https://sell.moonpay.com";
|
|
42
64
|
async function moonpayRequest(method, path, body) {
|
|
43
65
|
const res = await fetch(`${BASE_URL}${path}`, {
|
|
44
66
|
method,
|
|
@@ -72,7 +94,36 @@ function qs(params) {
|
|
|
72
94
|
search.set(k, String(v));
|
|
73
95
|
return `?${search.toString()}`;
|
|
74
96
|
}
|
|
75
|
-
|
|
97
|
+
/**
|
|
98
|
+
* HMAC-SHA256 sign a MoonPay widget URL.
|
|
99
|
+
*
|
|
100
|
+
* MoonPay's hosted widget (buy.moonpay.com / sell.moonpay.com) requires that
|
|
101
|
+
* the query string be signed with the merchant's secret key. The signature is
|
|
102
|
+
* computed over the URL's query portion (including the leading `?`) and
|
|
103
|
+
* appended as `&signature=<base64>`.
|
|
104
|
+
*
|
|
105
|
+
* Requires MOONPAY_PUBLISHABLE_KEY (added as `apiKey` param) and
|
|
106
|
+
* MOONPAY_SECRET_KEY (used as the HMAC key) to be set.
|
|
107
|
+
*/
|
|
108
|
+
function signWidgetUrl(widgetBase, params) {
|
|
109
|
+
if (!PUBLISHABLE_KEY)
|
|
110
|
+
throw new Error("MOONPAY_PUBLISHABLE_KEY is not set; cannot sign widget URL.");
|
|
111
|
+
if (!SECRET_KEY)
|
|
112
|
+
throw new Error("MOONPAY_SECRET_KEY is not set; cannot sign widget URL.");
|
|
113
|
+
const merged = { apiKey: PUBLISHABLE_KEY, ...params };
|
|
114
|
+
const entries = Object.entries(merged).filter(([, v]) => v !== undefined && v !== null && v !== "");
|
|
115
|
+
const search = new URLSearchParams();
|
|
116
|
+
for (const [k, v] of entries) {
|
|
117
|
+
if (typeof v === "object")
|
|
118
|
+
search.set(k, JSON.stringify(v));
|
|
119
|
+
else
|
|
120
|
+
search.set(k, String(v));
|
|
121
|
+
}
|
|
122
|
+
const query = `?${search.toString()}`;
|
|
123
|
+
const signature = createHmac("sha256", SECRET_KEY).update(query).digest("base64");
|
|
124
|
+
return `${widgetBase}${query}&signature=${encodeURIComponent(signature)}`;
|
|
125
|
+
}
|
|
126
|
+
const server = new Server({ name: "mcp-moonpay", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
76
127
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
77
128
|
tools: [
|
|
78
129
|
{
|
|
@@ -182,6 +233,19 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
182
233
|
required: ["id"],
|
|
183
234
|
},
|
|
184
235
|
},
|
|
236
|
+
{
|
|
237
|
+
name: "refund_sell_transaction",
|
|
238
|
+
description: "Request a refund on an off-ramp (sell) transaction. Used when the destination bank rejects payout or the user disputes the trade. Reason codes are MoonPay-defined.",
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: "object",
|
|
241
|
+
properties: {
|
|
242
|
+
id: { type: "string", description: "MoonPay sell transaction id to refund" },
|
|
243
|
+
reason: { type: "string", description: "Reason code or free-text justification for the refund" },
|
|
244
|
+
amount: { type: "number", description: "Optional partial refund amount (in the transaction's base/crypto currency). Omit for full refund." },
|
|
245
|
+
},
|
|
246
|
+
required: ["id"],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
185
249
|
{
|
|
186
250
|
name: "create_customer",
|
|
187
251
|
description: "Create a MoonPay customer (KYC'd end user). Required before creating transactions that must be tied to an identified individual.",
|
|
@@ -220,6 +284,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
220
284
|
required: ["id"],
|
|
221
285
|
},
|
|
222
286
|
},
|
|
287
|
+
{
|
|
288
|
+
name: "get_customer_kyc_status",
|
|
289
|
+
description: "Fetch KYC verification status (and any pending document requirements) for a MoonPay customer. Use to gate flows that require an approved customer before transacting.",
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: {
|
|
293
|
+
id: { type: "string", description: "MoonPay customer id" },
|
|
294
|
+
},
|
|
295
|
+
required: ["id"],
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: "list_customer_transactions",
|
|
300
|
+
description: "List all transactions (buy + sell) tied to a single MoonPay customer. Convenience wrapper for unified history / reconciliation by user.",
|
|
301
|
+
inputSchema: {
|
|
302
|
+
type: "object",
|
|
303
|
+
properties: {
|
|
304
|
+
customerId: { type: "string", description: "MoonPay customer id" },
|
|
305
|
+
status: { type: "string", description: "Optional status filter (e.g. pending, completed, failed)" },
|
|
306
|
+
limit: { type: "number", description: "Max results to return" },
|
|
307
|
+
},
|
|
308
|
+
required: ["customerId"],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
name: "get_transaction_receipt",
|
|
313
|
+
description: "Fetch a tax-/audit-grade receipt for a completed buy or sell transaction. Useful for end-user reporting or accounting export.",
|
|
314
|
+
inputSchema: {
|
|
315
|
+
type: "object",
|
|
316
|
+
properties: {
|
|
317
|
+
id: { type: "string", description: "MoonPay transaction id (buy or sell)" },
|
|
318
|
+
type: { type: "string", enum: ["buy", "sell"], description: "Whether the id refers to a buy or sell transaction. Defaults to buy." },
|
|
319
|
+
},
|
|
320
|
+
required: ["id"],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
223
323
|
{
|
|
224
324
|
name: "list_currencies",
|
|
225
325
|
description: "List supported currencies (fiat + crypto). Essential for agents: use this to discover currency codes dynamically rather than hard-coding, and to check which assets/fiats are currently enabled.",
|
|
@@ -230,6 +330,96 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
230
330
|
},
|
|
231
331
|
},
|
|
232
332
|
},
|
|
333
|
+
{
|
|
334
|
+
name: "get_currency",
|
|
335
|
+
description: "Retrieve metadata for a single currency (fiat or crypto) by its MoonPay code. Returns network, decimals, min/max amounts, fee structure.",
|
|
336
|
+
inputSchema: {
|
|
337
|
+
type: "object",
|
|
338
|
+
properties: {
|
|
339
|
+
currencyCode: { type: "string", description: "Currency code (e.g. btc, usdc, brl, usd)" },
|
|
340
|
+
},
|
|
341
|
+
required: ["currencyCode"],
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
name: "list_countries",
|
|
346
|
+
description: "List countries supported by MoonPay along with which flows (buy / sell / NFT) are allowed per geography. Use this to gate UI before initiating a quote.",
|
|
347
|
+
inputSchema: {
|
|
348
|
+
type: "object",
|
|
349
|
+
properties: {},
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: "list_payment_methods",
|
|
354
|
+
description: "List payment methods supported for a given fiat currency / country combination (e.g. credit_debit_card, sepa_bank_transfer, pix). Use to populate checkout selectors dynamically.",
|
|
355
|
+
inputSchema: {
|
|
356
|
+
type: "object",
|
|
357
|
+
properties: {
|
|
358
|
+
currencyCode: { type: "string", description: "Fiat currency code (e.g. brl, usd, eur)" },
|
|
359
|
+
country: { type: "string", description: "ISO-3166 alpha-2 country code (e.g. BR, US, MX)" },
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: "get_user_country",
|
|
365
|
+
description: "Resolve the caller's (or a given IP's) country via MoonPay's IP-address geolocation endpoint. Returns ISO alpha-2 + alpha-3 country, plus state for US. Compliance helper to gate flows by jurisdiction before quoting or creating a transaction.",
|
|
366
|
+
inputSchema: {
|
|
367
|
+
type: "object",
|
|
368
|
+
properties: {
|
|
369
|
+
ipAddress: { type: "string", description: "Optional IP to check. If omitted, MoonPay resolves from the request origin." },
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
name: "sign_buy_url",
|
|
375
|
+
description: "Build and HMAC-SHA256 sign a MoonPay buy widget URL (buy.moonpay.com). Returns a ready-to-redirect URL with the merchant's apiKey + signature appended. Requires MOONPAY_PUBLISHABLE_KEY and MOONPAY_SECRET_KEY in the environment. Use when embedding the hosted onramp in your own UI.",
|
|
376
|
+
inputSchema: {
|
|
377
|
+
type: "object",
|
|
378
|
+
properties: {
|
|
379
|
+
currencyCode: { type: "string", description: "Crypto currency code to buy (e.g. btc, usdc)" },
|
|
380
|
+
baseCurrencyCode: { type: "string", description: "Fiat currency code (e.g. usd, brl)" },
|
|
381
|
+
baseCurrencyAmount: { type: "number", description: "Pre-fill fiat amount" },
|
|
382
|
+
quoteCurrencyAmount: { type: "number", description: "Pre-fill crypto amount" },
|
|
383
|
+
walletAddress: { type: "string", description: "Pre-fill destination wallet address" },
|
|
384
|
+
walletAddressTag: { type: "string", description: "Destination tag / memo for chains that require it" },
|
|
385
|
+
email: { type: "string", description: "Pre-fill end-user email" },
|
|
386
|
+
externalCustomerId: { type: "string", description: "Your internal user id, propagated to MoonPay" },
|
|
387
|
+
externalTransactionId: { type: "string", description: "Your internal transaction reference" },
|
|
388
|
+
redirectURL: { type: "string", description: "Where to redirect after the hosted flow completes" },
|
|
389
|
+
paymentMethod: { type: "string", description: "Pre-select payment method (e.g. credit_debit_card, pix)" },
|
|
390
|
+
theme: { type: "string", description: "Widget theme (light / dark)" },
|
|
391
|
+
colorCode: { type: "string", description: "Hex accent color for the widget" },
|
|
392
|
+
language: { type: "string", description: "BCP-47 language tag (e.g. en, pt-BR)" },
|
|
393
|
+
showWalletAddressForm: { type: "boolean", description: "If true, force the widget to show the wallet form even when prefilled" },
|
|
394
|
+
extraParams: { type: "object", description: "Additional widget parameters passed through as-is (object values are JSON-stringified)" },
|
|
395
|
+
},
|
|
396
|
+
required: ["currencyCode"],
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: "sign_sell_url",
|
|
401
|
+
description: "Build and HMAC-SHA256 sign a MoonPay sell widget URL (sell.moonpay.com). Returns a ready-to-redirect URL with apiKey + signature appended. Requires MOONPAY_PUBLISHABLE_KEY and MOONPAY_SECRET_KEY in the environment.",
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: "object",
|
|
404
|
+
properties: {
|
|
405
|
+
baseCurrencyCode: { type: "string", description: "Crypto currency code being sold (e.g. btc, usdc)" },
|
|
406
|
+
quoteCurrencyCode: { type: "string", description: "Fiat currency to receive (e.g. usd, brl)" },
|
|
407
|
+
baseCurrencyAmount: { type: "number", description: "Pre-fill crypto amount" },
|
|
408
|
+
quoteCurrencyAmount: { type: "number", description: "Pre-fill fiat amount" },
|
|
409
|
+
refundWalletAddress: { type: "string", description: "Wallet to refund crypto to if the sell fails" },
|
|
410
|
+
email: { type: "string", description: "Pre-fill end-user email" },
|
|
411
|
+
externalCustomerId: { type: "string", description: "Your internal user id" },
|
|
412
|
+
externalTransactionId: { type: "string", description: "Your internal transaction reference" },
|
|
413
|
+
redirectURL: { type: "string", description: "Where to redirect after the hosted flow completes" },
|
|
414
|
+
payoutMethod: { type: "string", description: "Pre-select payout method (e.g. sepa_bank_transfer, pix)" },
|
|
415
|
+
theme: { type: "string", description: "Widget theme (light / dark)" },
|
|
416
|
+
colorCode: { type: "string", description: "Hex accent color for the widget" },
|
|
417
|
+
language: { type: "string", description: "BCP-47 language tag (e.g. en, pt-BR)" },
|
|
418
|
+
extraParams: { type: "object", description: "Additional widget parameters passed through as-is (object values are JSON-stringified)" },
|
|
419
|
+
},
|
|
420
|
+
required: ["baseCurrencyCode"],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
233
423
|
],
|
|
234
424
|
}));
|
|
235
425
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -275,14 +465,62 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
275
465
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("POST", "/v3/sell_transactions", a), null, 2) }] };
|
|
276
466
|
case "get_sell_transaction":
|
|
277
467
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/sell_transactions/${encodeURIComponent(String(a.id ?? ""))}`), null, 2) }] };
|
|
468
|
+
case "refund_sell_transaction": {
|
|
469
|
+
const id = encodeURIComponent(String(a.id ?? ""));
|
|
470
|
+
const body = {};
|
|
471
|
+
if (a.reason !== undefined)
|
|
472
|
+
body.reason = a.reason;
|
|
473
|
+
if (a.amount !== undefined)
|
|
474
|
+
body.amount = a.amount;
|
|
475
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("POST", `/v3/sell_transactions/${id}/refund`, body), null, 2) }] };
|
|
476
|
+
}
|
|
278
477
|
case "create_customer":
|
|
279
478
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("POST", "/v1/customers", a), null, 2) }] };
|
|
280
479
|
case "get_customer":
|
|
281
480
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v1/customers/${encodeURIComponent(String(a.id ?? ""))}`), null, 2) }] };
|
|
481
|
+
case "get_customer_kyc_status": {
|
|
482
|
+
const id = encodeURIComponent(String(a.id ?? ""));
|
|
483
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v1/customers/${id}/kyc_status`), null, 2) }] };
|
|
484
|
+
}
|
|
485
|
+
case "list_customer_transactions": {
|
|
486
|
+
const id = encodeURIComponent(String(a.customerId ?? ""));
|
|
487
|
+
const query = qs({ status: a.status, limit: a.limit });
|
|
488
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v1/customers/${id}/transactions${query}`), null, 2) }] };
|
|
489
|
+
}
|
|
490
|
+
case "get_transaction_receipt": {
|
|
491
|
+
const id = encodeURIComponent(String(a.id ?? ""));
|
|
492
|
+
const type = String(a.type ?? "buy");
|
|
493
|
+
const path = type === "sell" ? `/v3/sell_transactions/${id}/receipt` : `/v1/transactions/${id}/receipt`;
|
|
494
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", path), null, 2) }] };
|
|
495
|
+
}
|
|
282
496
|
case "list_currencies": {
|
|
283
497
|
const query = qs({ show: a.show });
|
|
284
498
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/currencies${query}`), null, 2) }] };
|
|
285
499
|
}
|
|
500
|
+
case "get_currency": {
|
|
501
|
+
const code = encodeURIComponent(String(a.currencyCode ?? ""));
|
|
502
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/currencies/${code}`), null, 2) }] };
|
|
503
|
+
}
|
|
504
|
+
case "list_countries":
|
|
505
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/countries`), null, 2) }] };
|
|
506
|
+
case "list_payment_methods": {
|
|
507
|
+
const query = qs({ currencyCode: a.currencyCode, country: a.country });
|
|
508
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/payment_methods${query}`), null, 2) }] };
|
|
509
|
+
}
|
|
510
|
+
case "get_user_country": {
|
|
511
|
+
const query = qs({ ipAddress: a.ipAddress });
|
|
512
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v4/ip_address${query}`), null, 2) }] };
|
|
513
|
+
}
|
|
514
|
+
case "sign_buy_url": {
|
|
515
|
+
const { extraParams, ...rest } = a;
|
|
516
|
+
const url = signWidgetUrl(BUY_WIDGET_URL, { ...rest, ...(extraParams ?? {}) });
|
|
517
|
+
return { content: [{ type: "text", text: JSON.stringify({ url }, null, 2) }] };
|
|
518
|
+
}
|
|
519
|
+
case "sign_sell_url": {
|
|
520
|
+
const { extraParams, ...rest } = a;
|
|
521
|
+
const url = signWidgetUrl(SELL_WIDGET_URL, { ...rest, ...(extraParams ?? {}) });
|
|
522
|
+
return { content: [{ type: "text", text: JSON.stringify({ url }, null, 2) }] };
|
|
523
|
+
}
|
|
286
524
|
default:
|
|
287
525
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
288
526
|
}
|
|
@@ -309,7 +547,7 @@ async function main() {
|
|
|
309
547
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
310
548
|
t.onclose = () => { if (t.sessionId)
|
|
311
549
|
transports.delete(t.sessionId); };
|
|
312
|
-
const s = new Server({ name: "mcp-moonpay", version: "0.
|
|
550
|
+
const s = new Server({ name: "mcp-moonpay", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
313
551
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
314
552
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
315
553
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-moonpay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server for MoonPay — fiat-to-crypto on/off-ramp covering 100+ crypto assets, multi-geography, Pix supported for Brazil",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/server.json
CHANGED
|
@@ -7,29 +7,57 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/crypto/moonpay"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.2.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@codespar/mcp-moonpay",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.2.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
19
19
|
"environmentVariables": [
|
|
20
20
|
{
|
|
21
21
|
"name": "MOONPAY_API_KEY",
|
|
22
|
-
"description": "MoonPay API key (sandbox or production — the key selects the environment)",
|
|
22
|
+
"description": "MoonPay REST API key (sandbox or production — the key selects the environment)",
|
|
23
23
|
"isRequired": true,
|
|
24
24
|
"format": "string",
|
|
25
25
|
"isSecret": true
|
|
26
26
|
},
|
|
27
|
+
{
|
|
28
|
+
"name": "MOONPAY_PUBLISHABLE_KEY",
|
|
29
|
+
"description": "MoonPay publishable key used as `apiKey` in signed widget URLs (required for sign_buy_url / sign_sell_url).",
|
|
30
|
+
"isRequired": false,
|
|
31
|
+
"format": "string",
|
|
32
|
+
"isSecret": false
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"name": "MOONPAY_SECRET_KEY",
|
|
36
|
+
"description": "MoonPay secret key used to HMAC-SHA256 sign widget URLs (required for sign_buy_url / sign_sell_url).",
|
|
37
|
+
"isRequired": false,
|
|
38
|
+
"format": "string",
|
|
39
|
+
"isSecret": true
|
|
40
|
+
},
|
|
27
41
|
{
|
|
28
42
|
"name": "MOONPAY_BASE_URL",
|
|
29
43
|
"description": "MoonPay API base URL. Defaults to https://api.moonpay.com.",
|
|
30
44
|
"isRequired": false,
|
|
31
45
|
"format": "string",
|
|
32
46
|
"isSecret": false
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "MOONPAY_BUY_WIDGET_URL",
|
|
50
|
+
"description": "MoonPay buy widget base URL. Defaults to https://buy.moonpay.com.",
|
|
51
|
+
"isRequired": false,
|
|
52
|
+
"format": "string",
|
|
53
|
+
"isSecret": false
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"name": "MOONPAY_SELL_WIDGET_URL",
|
|
57
|
+
"description": "MoonPay sell widget base URL. Defaults to https://sell.moonpay.com.",
|
|
58
|
+
"isRequired": false,
|
|
59
|
+
"format": "string",
|
|
60
|
+
"isSecret": false
|
|
33
61
|
}
|
|
34
62
|
]
|
|
35
63
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* option for agents paying out in crypto or end users buying crypto with
|
|
11
11
|
* local currency.
|
|
12
12
|
*
|
|
13
|
-
* Tools (
|
|
13
|
+
* Tools (20):
|
|
14
14
|
* get_buy_quote — preview a fiat -> crypto exchange before committing
|
|
15
15
|
* create_buy_transaction — create a buy transaction (fiat -> crypto)
|
|
16
16
|
* get_buy_transaction — retrieve a buy transaction by id
|
|
@@ -18,18 +18,35 @@
|
|
|
18
18
|
* get_sell_quote — preview a crypto -> fiat exchange
|
|
19
19
|
* create_sell_transaction — create a sell transaction (crypto -> fiat)
|
|
20
20
|
* get_sell_transaction — retrieve a sell transaction by id
|
|
21
|
+
* refund_sell_transaction — request a refund on an off-ramp transaction
|
|
21
22
|
* create_customer — create a KYC'd end user
|
|
22
23
|
* get_customer — retrieve a customer by id
|
|
24
|
+
* get_customer_kyc_status — fetch KYC verification status for a customer
|
|
25
|
+
* list_customer_transactions — list all (buy + sell) transactions tied to a customer
|
|
26
|
+
* get_transaction_receipt — fetch a tax-/audit-grade receipt for a completed transaction
|
|
23
27
|
* list_currencies — list supported fiat + crypto assets (dynamic discovery)
|
|
28
|
+
* get_currency — retrieve metadata for a single currency by code
|
|
29
|
+
* list_countries — list supported countries with allowed flows (buy/sell) per geography
|
|
30
|
+
* list_payment_methods — list payment methods supported for a fiat / country pair
|
|
31
|
+
* get_user_country — IP-based geolocation + alpha-3 country code (compliance helper)
|
|
32
|
+
* sign_buy_url — HMAC-sign a hosted-checkout buy widget URL with apiKey + params
|
|
33
|
+
* sign_sell_url — HMAC-sign a hosted-checkout sell widget URL with apiKey + params
|
|
24
34
|
*
|
|
25
35
|
* Authentication
|
|
26
|
-
*
|
|
36
|
+
* REST API requests carry:
|
|
27
37
|
* Authorization: Api-Key <API_KEY>
|
|
28
38
|
* Sandbox vs production is selected by which key you pass; the base URL is the same.
|
|
29
39
|
*
|
|
40
|
+
* Hosted widget URLs (buy.moonpay.com / sell.moonpay.com) are HMAC-SHA256
|
|
41
|
+
* signed using the publishable key + secret key (see sign_buy_url / sign_sell_url).
|
|
42
|
+
*
|
|
30
43
|
* Environment
|
|
31
|
-
* MOONPAY_API_KEY
|
|
32
|
-
*
|
|
44
|
+
* MOONPAY_API_KEY — REST API key (required, secret)
|
|
45
|
+
* MOONPAY_PUBLISHABLE_KEY — publishable key for widget URLs (optional, used by sign_*_url)
|
|
46
|
+
* MOONPAY_SECRET_KEY — secret key for HMAC signing widget URLs (optional, used by sign_*_url)
|
|
47
|
+
* MOONPAY_BASE_URL — optional; defaults to https://api.moonpay.com
|
|
48
|
+
* MOONPAY_BUY_WIDGET_URL — optional; defaults to https://buy.moonpay.com
|
|
49
|
+
* MOONPAY_SELL_WIDGET_URL — optional; defaults to https://sell.moonpay.com
|
|
33
50
|
*
|
|
34
51
|
* Docs: https://dev.moonpay.com
|
|
35
52
|
*/
|
|
@@ -42,9 +59,14 @@ import {
|
|
|
42
59
|
CallToolRequestSchema,
|
|
43
60
|
ListToolsRequestSchema,
|
|
44
61
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
62
|
+
import { createHmac } from "node:crypto";
|
|
45
63
|
|
|
46
64
|
const API_KEY = process.env.MOONPAY_API_KEY || "";
|
|
65
|
+
const PUBLISHABLE_KEY = process.env.MOONPAY_PUBLISHABLE_KEY || "";
|
|
66
|
+
const SECRET_KEY = process.env.MOONPAY_SECRET_KEY || "";
|
|
47
67
|
const BASE_URL = process.env.MOONPAY_BASE_URL || "https://api.moonpay.com";
|
|
68
|
+
const BUY_WIDGET_URL = process.env.MOONPAY_BUY_WIDGET_URL || "https://buy.moonpay.com";
|
|
69
|
+
const SELL_WIDGET_URL = process.env.MOONPAY_SELL_WIDGET_URL || "https://sell.moonpay.com";
|
|
48
70
|
|
|
49
71
|
async function moonpayRequest(method: string, path: string, body?: unknown): Promise<unknown> {
|
|
50
72
|
const res = await fetch(`${BASE_URL}${path}`, {
|
|
@@ -77,8 +99,34 @@ function qs(params: Record<string, unknown>): string {
|
|
|
77
99
|
return `?${search.toString()}`;
|
|
78
100
|
}
|
|
79
101
|
|
|
102
|
+
/**
|
|
103
|
+
* HMAC-SHA256 sign a MoonPay widget URL.
|
|
104
|
+
*
|
|
105
|
+
* MoonPay's hosted widget (buy.moonpay.com / sell.moonpay.com) requires that
|
|
106
|
+
* the query string be signed with the merchant's secret key. The signature is
|
|
107
|
+
* computed over the URL's query portion (including the leading `?`) and
|
|
108
|
+
* appended as `&signature=<base64>`.
|
|
109
|
+
*
|
|
110
|
+
* Requires MOONPAY_PUBLISHABLE_KEY (added as `apiKey` param) and
|
|
111
|
+
* MOONPAY_SECRET_KEY (used as the HMAC key) to be set.
|
|
112
|
+
*/
|
|
113
|
+
function signWidgetUrl(widgetBase: string, params: Record<string, unknown>): string {
|
|
114
|
+
if (!PUBLISHABLE_KEY) throw new Error("MOONPAY_PUBLISHABLE_KEY is not set; cannot sign widget URL.");
|
|
115
|
+
if (!SECRET_KEY) throw new Error("MOONPAY_SECRET_KEY is not set; cannot sign widget URL.");
|
|
116
|
+
const merged: Record<string, unknown> = { apiKey: PUBLISHABLE_KEY, ...params };
|
|
117
|
+
const entries = Object.entries(merged).filter(([, v]) => v !== undefined && v !== null && v !== "");
|
|
118
|
+
const search = new URLSearchParams();
|
|
119
|
+
for (const [k, v] of entries) {
|
|
120
|
+
if (typeof v === "object") search.set(k, JSON.stringify(v));
|
|
121
|
+
else search.set(k, String(v));
|
|
122
|
+
}
|
|
123
|
+
const query = `?${search.toString()}`;
|
|
124
|
+
const signature = createHmac("sha256", SECRET_KEY).update(query).digest("base64");
|
|
125
|
+
return `${widgetBase}${query}&signature=${encodeURIComponent(signature)}`;
|
|
126
|
+
}
|
|
127
|
+
|
|
80
128
|
const server = new Server(
|
|
81
|
-
{ name: "mcp-moonpay", version: "0.
|
|
129
|
+
{ name: "mcp-moonpay", version: "0.2.0" },
|
|
82
130
|
{ capabilities: { tools: {} } }
|
|
83
131
|
);
|
|
84
132
|
|
|
@@ -191,6 +239,19 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
191
239
|
required: ["id"],
|
|
192
240
|
},
|
|
193
241
|
},
|
|
242
|
+
{
|
|
243
|
+
name: "refund_sell_transaction",
|
|
244
|
+
description: "Request a refund on an off-ramp (sell) transaction. Used when the destination bank rejects payout or the user disputes the trade. Reason codes are MoonPay-defined.",
|
|
245
|
+
inputSchema: {
|
|
246
|
+
type: "object",
|
|
247
|
+
properties: {
|
|
248
|
+
id: { type: "string", description: "MoonPay sell transaction id to refund" },
|
|
249
|
+
reason: { type: "string", description: "Reason code or free-text justification for the refund" },
|
|
250
|
+
amount: { type: "number", description: "Optional partial refund amount (in the transaction's base/crypto currency). Omit for full refund." },
|
|
251
|
+
},
|
|
252
|
+
required: ["id"],
|
|
253
|
+
},
|
|
254
|
+
},
|
|
194
255
|
{
|
|
195
256
|
name: "create_customer",
|
|
196
257
|
description: "Create a MoonPay customer (KYC'd end user). Required before creating transactions that must be tied to an identified individual.",
|
|
@@ -229,6 +290,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
229
290
|
required: ["id"],
|
|
230
291
|
},
|
|
231
292
|
},
|
|
293
|
+
{
|
|
294
|
+
name: "get_customer_kyc_status",
|
|
295
|
+
description: "Fetch KYC verification status (and any pending document requirements) for a MoonPay customer. Use to gate flows that require an approved customer before transacting.",
|
|
296
|
+
inputSchema: {
|
|
297
|
+
type: "object",
|
|
298
|
+
properties: {
|
|
299
|
+
id: { type: "string", description: "MoonPay customer id" },
|
|
300
|
+
},
|
|
301
|
+
required: ["id"],
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: "list_customer_transactions",
|
|
306
|
+
description: "List all transactions (buy + sell) tied to a single MoonPay customer. Convenience wrapper for unified history / reconciliation by user.",
|
|
307
|
+
inputSchema: {
|
|
308
|
+
type: "object",
|
|
309
|
+
properties: {
|
|
310
|
+
customerId: { type: "string", description: "MoonPay customer id" },
|
|
311
|
+
status: { type: "string", description: "Optional status filter (e.g. pending, completed, failed)" },
|
|
312
|
+
limit: { type: "number", description: "Max results to return" },
|
|
313
|
+
},
|
|
314
|
+
required: ["customerId"],
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
name: "get_transaction_receipt",
|
|
319
|
+
description: "Fetch a tax-/audit-grade receipt for a completed buy or sell transaction. Useful for end-user reporting or accounting export.",
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: "object",
|
|
322
|
+
properties: {
|
|
323
|
+
id: { type: "string", description: "MoonPay transaction id (buy or sell)" },
|
|
324
|
+
type: { type: "string", enum: ["buy", "sell"], description: "Whether the id refers to a buy or sell transaction. Defaults to buy." },
|
|
325
|
+
},
|
|
326
|
+
required: ["id"],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
232
329
|
{
|
|
233
330
|
name: "list_currencies",
|
|
234
331
|
description: "List supported currencies (fiat + crypto). Essential for agents: use this to discover currency codes dynamically rather than hard-coding, and to check which assets/fiats are currently enabled.",
|
|
@@ -239,6 +336,96 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
239
336
|
},
|
|
240
337
|
},
|
|
241
338
|
},
|
|
339
|
+
{
|
|
340
|
+
name: "get_currency",
|
|
341
|
+
description: "Retrieve metadata for a single currency (fiat or crypto) by its MoonPay code. Returns network, decimals, min/max amounts, fee structure.",
|
|
342
|
+
inputSchema: {
|
|
343
|
+
type: "object",
|
|
344
|
+
properties: {
|
|
345
|
+
currencyCode: { type: "string", description: "Currency code (e.g. btc, usdc, brl, usd)" },
|
|
346
|
+
},
|
|
347
|
+
required: ["currencyCode"],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: "list_countries",
|
|
352
|
+
description: "List countries supported by MoonPay along with which flows (buy / sell / NFT) are allowed per geography. Use this to gate UI before initiating a quote.",
|
|
353
|
+
inputSchema: {
|
|
354
|
+
type: "object",
|
|
355
|
+
properties: {},
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
name: "list_payment_methods",
|
|
360
|
+
description: "List payment methods supported for a given fiat currency / country combination (e.g. credit_debit_card, sepa_bank_transfer, pix). Use to populate checkout selectors dynamically.",
|
|
361
|
+
inputSchema: {
|
|
362
|
+
type: "object",
|
|
363
|
+
properties: {
|
|
364
|
+
currencyCode: { type: "string", description: "Fiat currency code (e.g. brl, usd, eur)" },
|
|
365
|
+
country: { type: "string", description: "ISO-3166 alpha-2 country code (e.g. BR, US, MX)" },
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: "get_user_country",
|
|
371
|
+
description: "Resolve the caller's (or a given IP's) country via MoonPay's IP-address geolocation endpoint. Returns ISO alpha-2 + alpha-3 country, plus state for US. Compliance helper to gate flows by jurisdiction before quoting or creating a transaction.",
|
|
372
|
+
inputSchema: {
|
|
373
|
+
type: "object",
|
|
374
|
+
properties: {
|
|
375
|
+
ipAddress: { type: "string", description: "Optional IP to check. If omitted, MoonPay resolves from the request origin." },
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: "sign_buy_url",
|
|
381
|
+
description: "Build and HMAC-SHA256 sign a MoonPay buy widget URL (buy.moonpay.com). Returns a ready-to-redirect URL with the merchant's apiKey + signature appended. Requires MOONPAY_PUBLISHABLE_KEY and MOONPAY_SECRET_KEY in the environment. Use when embedding the hosted onramp in your own UI.",
|
|
382
|
+
inputSchema: {
|
|
383
|
+
type: "object",
|
|
384
|
+
properties: {
|
|
385
|
+
currencyCode: { type: "string", description: "Crypto currency code to buy (e.g. btc, usdc)" },
|
|
386
|
+
baseCurrencyCode: { type: "string", description: "Fiat currency code (e.g. usd, brl)" },
|
|
387
|
+
baseCurrencyAmount: { type: "number", description: "Pre-fill fiat amount" },
|
|
388
|
+
quoteCurrencyAmount: { type: "number", description: "Pre-fill crypto amount" },
|
|
389
|
+
walletAddress: { type: "string", description: "Pre-fill destination wallet address" },
|
|
390
|
+
walletAddressTag: { type: "string", description: "Destination tag / memo for chains that require it" },
|
|
391
|
+
email: { type: "string", description: "Pre-fill end-user email" },
|
|
392
|
+
externalCustomerId: { type: "string", description: "Your internal user id, propagated to MoonPay" },
|
|
393
|
+
externalTransactionId: { type: "string", description: "Your internal transaction reference" },
|
|
394
|
+
redirectURL: { type: "string", description: "Where to redirect after the hosted flow completes" },
|
|
395
|
+
paymentMethod: { type: "string", description: "Pre-select payment method (e.g. credit_debit_card, pix)" },
|
|
396
|
+
theme: { type: "string", description: "Widget theme (light / dark)" },
|
|
397
|
+
colorCode: { type: "string", description: "Hex accent color for the widget" },
|
|
398
|
+
language: { type: "string", description: "BCP-47 language tag (e.g. en, pt-BR)" },
|
|
399
|
+
showWalletAddressForm: { type: "boolean", description: "If true, force the widget to show the wallet form even when prefilled" },
|
|
400
|
+
extraParams: { type: "object", description: "Additional widget parameters passed through as-is (object values are JSON-stringified)" },
|
|
401
|
+
},
|
|
402
|
+
required: ["currencyCode"],
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: "sign_sell_url",
|
|
407
|
+
description: "Build and HMAC-SHA256 sign a MoonPay sell widget URL (sell.moonpay.com). Returns a ready-to-redirect URL with apiKey + signature appended. Requires MOONPAY_PUBLISHABLE_KEY and MOONPAY_SECRET_KEY in the environment.",
|
|
408
|
+
inputSchema: {
|
|
409
|
+
type: "object",
|
|
410
|
+
properties: {
|
|
411
|
+
baseCurrencyCode: { type: "string", description: "Crypto currency code being sold (e.g. btc, usdc)" },
|
|
412
|
+
quoteCurrencyCode: { type: "string", description: "Fiat currency to receive (e.g. usd, brl)" },
|
|
413
|
+
baseCurrencyAmount: { type: "number", description: "Pre-fill crypto amount" },
|
|
414
|
+
quoteCurrencyAmount: { type: "number", description: "Pre-fill fiat amount" },
|
|
415
|
+
refundWalletAddress: { type: "string", description: "Wallet to refund crypto to if the sell fails" },
|
|
416
|
+
email: { type: "string", description: "Pre-fill end-user email" },
|
|
417
|
+
externalCustomerId: { type: "string", description: "Your internal user id" },
|
|
418
|
+
externalTransactionId: { type: "string", description: "Your internal transaction reference" },
|
|
419
|
+
redirectURL: { type: "string", description: "Where to redirect after the hosted flow completes" },
|
|
420
|
+
payoutMethod: { type: "string", description: "Pre-select payout method (e.g. sepa_bank_transfer, pix)" },
|
|
421
|
+
theme: { type: "string", description: "Widget theme (light / dark)" },
|
|
422
|
+
colorCode: { type: "string", description: "Hex accent color for the widget" },
|
|
423
|
+
language: { type: "string", description: "BCP-47 language tag (e.g. en, pt-BR)" },
|
|
424
|
+
extraParams: { type: "object", description: "Additional widget parameters passed through as-is (object values are JSON-stringified)" },
|
|
425
|
+
},
|
|
426
|
+
required: ["baseCurrencyCode"],
|
|
427
|
+
},
|
|
428
|
+
},
|
|
242
429
|
],
|
|
243
430
|
}));
|
|
244
431
|
|
|
@@ -286,14 +473,60 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
286
473
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("POST", "/v3/sell_transactions", a), null, 2) }] };
|
|
287
474
|
case "get_sell_transaction":
|
|
288
475
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/sell_transactions/${encodeURIComponent(String(a.id ?? ""))}`), null, 2) }] };
|
|
476
|
+
case "refund_sell_transaction": {
|
|
477
|
+
const id = encodeURIComponent(String(a.id ?? ""));
|
|
478
|
+
const body: Record<string, unknown> = {};
|
|
479
|
+
if (a.reason !== undefined) body.reason = a.reason;
|
|
480
|
+
if (a.amount !== undefined) body.amount = a.amount;
|
|
481
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("POST", `/v3/sell_transactions/${id}/refund`, body), null, 2) }] };
|
|
482
|
+
}
|
|
289
483
|
case "create_customer":
|
|
290
484
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("POST", "/v1/customers", a), null, 2) }] };
|
|
291
485
|
case "get_customer":
|
|
292
486
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v1/customers/${encodeURIComponent(String(a.id ?? ""))}`), null, 2) }] };
|
|
487
|
+
case "get_customer_kyc_status": {
|
|
488
|
+
const id = encodeURIComponent(String(a.id ?? ""));
|
|
489
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v1/customers/${id}/kyc_status`), null, 2) }] };
|
|
490
|
+
}
|
|
491
|
+
case "list_customer_transactions": {
|
|
492
|
+
const id = encodeURIComponent(String(a.customerId ?? ""));
|
|
493
|
+
const query = qs({ status: a.status, limit: a.limit });
|
|
494
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v1/customers/${id}/transactions${query}`), null, 2) }] };
|
|
495
|
+
}
|
|
496
|
+
case "get_transaction_receipt": {
|
|
497
|
+
const id = encodeURIComponent(String(a.id ?? ""));
|
|
498
|
+
const type = String(a.type ?? "buy");
|
|
499
|
+
const path = type === "sell" ? `/v3/sell_transactions/${id}/receipt` : `/v1/transactions/${id}/receipt`;
|
|
500
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", path), null, 2) }] };
|
|
501
|
+
}
|
|
293
502
|
case "list_currencies": {
|
|
294
503
|
const query = qs({ show: a.show });
|
|
295
504
|
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/currencies${query}`), null, 2) }] };
|
|
296
505
|
}
|
|
506
|
+
case "get_currency": {
|
|
507
|
+
const code = encodeURIComponent(String(a.currencyCode ?? ""));
|
|
508
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/currencies/${code}`), null, 2) }] };
|
|
509
|
+
}
|
|
510
|
+
case "list_countries":
|
|
511
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/countries`), null, 2) }] };
|
|
512
|
+
case "list_payment_methods": {
|
|
513
|
+
const query = qs({ currencyCode: a.currencyCode, country: a.country });
|
|
514
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v3/payment_methods${query}`), null, 2) }] };
|
|
515
|
+
}
|
|
516
|
+
case "get_user_country": {
|
|
517
|
+
const query = qs({ ipAddress: a.ipAddress });
|
|
518
|
+
return { content: [{ type: "text", text: JSON.stringify(await moonpayRequest("GET", `/v4/ip_address${query}`), null, 2) }] };
|
|
519
|
+
}
|
|
520
|
+
case "sign_buy_url": {
|
|
521
|
+
const { extraParams, ...rest } = a as { extraParams?: Record<string, unknown> } & Record<string, unknown>;
|
|
522
|
+
const url = signWidgetUrl(BUY_WIDGET_URL, { ...rest, ...(extraParams ?? {}) });
|
|
523
|
+
return { content: [{ type: "text", text: JSON.stringify({ url }, null, 2) }] };
|
|
524
|
+
}
|
|
525
|
+
case "sign_sell_url": {
|
|
526
|
+
const { extraParams, ...rest } = a as { extraParams?: Record<string, unknown> } & Record<string, unknown>;
|
|
527
|
+
const url = signWidgetUrl(SELL_WIDGET_URL, { ...rest, ...(extraParams ?? {}) });
|
|
528
|
+
return { content: [{ type: "text", text: JSON.stringify({ url }, null, 2) }] };
|
|
529
|
+
}
|
|
297
530
|
default:
|
|
298
531
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
299
532
|
}
|
|
@@ -316,7 +549,7 @@ async function main() {
|
|
|
316
549
|
if (!sid && isInitializeRequest(req.body)) {
|
|
317
550
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
318
551
|
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
319
|
-
const s = new Server({ name: "mcp-moonpay", version: "0.
|
|
552
|
+
const s = new Server({ name: "mcp-moonpay", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
320
553
|
(server as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.forEach((v, k) => (s as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.set(k, v));
|
|
321
554
|
(server as unknown as { _notificationHandlers?: Map<unknown, unknown> })._notificationHandlers?.forEach((v, k) => (s as unknown as { _notificationHandlers: Map<unknown, unknown> })._notificationHandlers.set(k, v));
|
|
322
555
|
await s.connect(t);
|