crossfin-mcp 1.8.1 → 1.8.2
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 +1 -1
- package/dist/index.js +36 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ crossfin-mcp
|
|
|
39
39
|
|
|
40
40
|
### Routing engine
|
|
41
41
|
|
|
42
|
-
- `find_optimal_route` — find optimal crypto transfer route across 6 exchanges
|
|
42
|
+
- `find_optimal_route` — find optimal crypto transfer route across 6 exchanges (paid via x402, requires `EVM_PRIVATE_KEY`)
|
|
43
43
|
- `list_exchange_fees` — compare trading and withdrawal fees across exchanges
|
|
44
44
|
- `compare_exchange_prices` — compare live prices for a coin across Korean exchanges
|
|
45
45
|
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,15 @@ import { z } from 'zod/v4';
|
|
|
5
5
|
import { x402Client, wrapFetchWithPayment, x402HTTPClient } from '@x402/fetch';
|
|
6
6
|
import { registerExactEvmScheme } from '@x402/evm/exact/client';
|
|
7
7
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
8
|
+
import { createRequire } from 'node:module';
|
|
8
9
|
import { createWallet, defaultLedgerPath, getBalance, listTransactions, setBudget, transfer, } from './ledgerStore.js';
|
|
9
10
|
const LEDGER_PATH = process.env.CROSSFIN_LEDGER_PATH?.trim() || defaultLedgerPath();
|
|
10
11
|
const API_BASE = (process.env.CROSSFIN_API_URL?.trim() || 'https://crossfin.dev').replace(/\/$/, '');
|
|
11
12
|
const EVM_PRIVATE_KEY = process.env.EVM_PRIVATE_KEY?.trim() ?? '';
|
|
12
13
|
const API_ORIGIN = new URL(API_BASE).origin;
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
const pkg = require('../package.json');
|
|
16
|
+
const MCP_VERSION = typeof pkg.version === 'string' && pkg.version.trim() ? pkg.version.trim() : '0.0.0';
|
|
13
17
|
function ensureCrossfinPaidUrl(raw) {
|
|
14
18
|
let url;
|
|
15
19
|
try {
|
|
@@ -53,7 +57,7 @@ function basescanLink(networkId, txHash) {
|
|
|
53
57
|
return `https://basescan.org/tx/${txHash}`;
|
|
54
58
|
return null;
|
|
55
59
|
}
|
|
56
|
-
const server = new McpServer({ name: 'crossfin', version:
|
|
60
|
+
const server = new McpServer({ name: 'crossfin', version: MCP_VERSION });
|
|
57
61
|
const railSchema = z.enum(['manual', 'kakaopay', 'toss', 'stripe', 'x402']);
|
|
58
62
|
async function apiFetch(path) {
|
|
59
63
|
const res = await fetch(`${API_BASE}${path}`);
|
|
@@ -399,7 +403,8 @@ server.registerTool('find_optimal_route', {
|
|
|
399
403
|
title: 'Find optimal route',
|
|
400
404
|
description: 'Find the cheapest/fastest path to move money across Asian exchanges. ' +
|
|
401
405
|
'Example: KRW on Bithumb → USDC on Binance. ' +
|
|
402
|
-
'Supports 6 exchanges (Bithumb, Upbit, Coinone, Korbit, GoPax, Binance) and 12 bridge coins.'
|
|
406
|
+
'Supports 6 exchanges (Bithumb, Upbit, Coinone, Korbit, GoPax, Binance) and 12 bridge coins. ' +
|
|
407
|
+
'Paid tool: calls /api/premium/route/find ($0.10) via x402 (requires EVM_PRIVATE_KEY).',
|
|
403
408
|
inputSchema: z.object({
|
|
404
409
|
from: z
|
|
405
410
|
.string()
|
|
@@ -414,6 +419,12 @@ server.registerTool('find_optimal_route', {
|
|
|
414
419
|
.describe('Routing strategy: cheapest (default), fastest, or balanced'),
|
|
415
420
|
}),
|
|
416
421
|
}, async ({ from, to, amount, strategy }) => {
|
|
422
|
+
if (!paidFetch || !httpClient) {
|
|
423
|
+
return {
|
|
424
|
+
content: [{ type: 'text', text: 'EVM_PRIVATE_KEY not configured — cannot call paid routing endpoint' }],
|
|
425
|
+
isError: true,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
417
428
|
try {
|
|
418
429
|
const qs = new URLSearchParams({
|
|
419
430
|
from,
|
|
@@ -422,8 +433,29 @@ server.registerTool('find_optimal_route', {
|
|
|
422
433
|
});
|
|
423
434
|
if (strategy)
|
|
424
435
|
qs.set('strategy', strategy);
|
|
425
|
-
const
|
|
426
|
-
|
|
436
|
+
const targetUrl = ensureCrossfinPaidUrl(`${API_BASE}/api/premium/route/find?${qs.toString()}`);
|
|
437
|
+
const res = await paidFetch(targetUrl, { method: 'GET' });
|
|
438
|
+
const body = await res.text();
|
|
439
|
+
let data;
|
|
440
|
+
try {
|
|
441
|
+
data = JSON.parse(body);
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
data = body;
|
|
445
|
+
}
|
|
446
|
+
const settle = httpClient.getPaymentSettleResponse((name) => res.headers.get(name));
|
|
447
|
+
const txHash = settle?.transaction;
|
|
448
|
+
const networkId = settle?.network;
|
|
449
|
+
const scanLink = basescanLink(networkId, txHash);
|
|
450
|
+
const result = {
|
|
451
|
+
status: res.status,
|
|
452
|
+
payer: payerAddress,
|
|
453
|
+
paid: !!settle,
|
|
454
|
+
txHash: txHash ?? null,
|
|
455
|
+
basescan: scanLink,
|
|
456
|
+
data,
|
|
457
|
+
};
|
|
458
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
427
459
|
}
|
|
428
460
|
catch (e) {
|
|
429
461
|
return {
|