x402trade-mcp 1.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 +143 -0
- package/dist/client.js +46 -0
- package/dist/index.js +295 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# x402Trade MCP Server
|
|
2
|
+
|
|
3
|
+
让 Claude、Cursor 等 AI 助手直接在 x402Trade 上交易。
|
|
4
|
+
|
|
5
|
+
## 5 分钟快速接入
|
|
6
|
+
|
|
7
|
+
### 1. 安装
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @x402trade/mcp-server
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
或者直接用 npx(无需安装):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @x402trade/mcp-server
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 2. 配置 Claude Desktop
|
|
20
|
+
|
|
21
|
+
打开 Claude Desktop 配置文件:
|
|
22
|
+
|
|
23
|
+
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
24
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
25
|
+
|
|
26
|
+
添加以下配置:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"x402trade": {
|
|
32
|
+
"command": "npx",
|
|
33
|
+
"args": ["@x402trade/mcp-server"],
|
|
34
|
+
"env": {
|
|
35
|
+
"PRIVATE_KEY": "0x你的以太坊钱包私钥"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
重启 Claude Desktop,即可使用。
|
|
43
|
+
|
|
44
|
+
### 3. 配置 Cursor
|
|
45
|
+
|
|
46
|
+
在 Cursor 设置 → MCP → Add Server:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"name": "x402trade",
|
|
51
|
+
"command": "npx @x402trade/mcp-server",
|
|
52
|
+
"env": {
|
|
53
|
+
"PRIVATE_KEY": "0x你的以太坊钱包私钥"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 你能对 Claude 说什么
|
|
61
|
+
|
|
62
|
+
配置完成后,你可以直接用自然语言交易:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
你: ETH 现在多少钱?
|
|
66
|
+
Claude: 调用 x402_get_price → ETH 当前价格 $2,247.50,24h 涨 +2.3%
|
|
67
|
+
|
|
68
|
+
你: 帮我用 $500 买 ETH,限价 $2200
|
|
69
|
+
Claude: 调用 x402_place_order → 订单已提交,订单号 abc-123,挂单中
|
|
70
|
+
|
|
71
|
+
你: 我现在有多少 USDC?
|
|
72
|
+
Claude: 调用 x402_get_balance → USDC 余额 $1,250.00,ETH 余额 0.45
|
|
73
|
+
|
|
74
|
+
你: 把刚才那个订单取消
|
|
75
|
+
Claude: 调用 x402_cancel_order → 订单已取消,$500 USDC 已解锁
|
|
76
|
+
|
|
77
|
+
你: 帮我看看 ETH-USDC 的买卖盘
|
|
78
|
+
Claude: 调用 x402_get_orderbook → 显示当前 bids 和 asks
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 可用工具
|
|
84
|
+
|
|
85
|
+
| 工具 | 说明 | 费用 |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `x402_get_price` | 当前价格、24h 涨跌、成交量 | $0.001 |
|
|
88
|
+
| `x402_get_orderbook` | 实时买卖盘 | $0.001 |
|
|
89
|
+
| `x402_get_balance` | 你的 USDC + ETH 余额 | 免费 |
|
|
90
|
+
| `x402_place_order` | 下买单或卖单(限价/市价) | $0.01 |
|
|
91
|
+
| `x402_cancel_order` | 撤销挂单 | 免费 |
|
|
92
|
+
| `x402_get_orders` | 查看我的订单记录 | $0.001 |
|
|
93
|
+
| `x402_get_recent_trades` | 市场最近成交记录 | $0.001 |
|
|
94
|
+
| `x402_deposit_info` | 充值地址和说明 | 免费 |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 充值 USDC
|
|
99
|
+
|
|
100
|
+
1. 调用 `x402_deposit_info` 获取充值地址
|
|
101
|
+
2. 在 Base L2 (Chain ID: 8453) 向该地址发送 USDC
|
|
102
|
+
3. 约 15 秒后余额自动更新
|
|
103
|
+
|
|
104
|
+
**注意:只支持 Base L2 上的 USDC,不支持其他网络。**
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 环境变量
|
|
109
|
+
|
|
110
|
+
| 变量 | 说明 | 默认值 |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `PRIVATE_KEY` | 你的以太坊钱包私钥(`0x...`) | 无(只读模式) |
|
|
113
|
+
| `X402_GATEWAY_URL` | 交易所 API 地址 | `https://api.getx402.trade` |
|
|
114
|
+
|
|
115
|
+
**私钥安全提示:** 私钥只在你的本地机器上使用,不会发送到任何服务器。MCP Server 用私钥在本地签名交易请求,签名后的数据发送给交易所验证。
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 本地开发
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git clone https://github.com/bidendavid/x402trade.git
|
|
123
|
+
cd x402trade/services/x402-mcp
|
|
124
|
+
npm install
|
|
125
|
+
PRIVATE_KEY=0x... npm run dev
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 交易对
|
|
131
|
+
|
|
132
|
+
目前支持:
|
|
133
|
+
- `ETH-USDC` — ETH 对 USDC
|
|
134
|
+
- `BTC-USDC` — BTC 对 USDC
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 链接
|
|
139
|
+
|
|
140
|
+
- **交易所 API**: https://api.getx402.trade
|
|
141
|
+
- **官网**: https://getx402.trade
|
|
142
|
+
- **GitHub**: https://github.com/bidendavid/x402trade
|
|
143
|
+
- **x402 协议**: https://x402.org
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Order matters: more specific prefixes must come before shorter ones
|
|
2
|
+
export function getPrice(path) {
|
|
3
|
+
if (path.startsWith('/trades'))
|
|
4
|
+
return '0.001'; // before /trade
|
|
5
|
+
if (path.startsWith('/trade'))
|
|
6
|
+
return '0.01';
|
|
7
|
+
if (path.startsWith('/ticker'))
|
|
8
|
+
return '0.001';
|
|
9
|
+
if (path.startsWith('/orderbook'))
|
|
10
|
+
return '0.001';
|
|
11
|
+
if (path.startsWith('/orders/'))
|
|
12
|
+
return '0'; // DELETE /orders/:id — before /orders
|
|
13
|
+
if (path.startsWith('/orders'))
|
|
14
|
+
return '0.001'; // GET /orders list
|
|
15
|
+
if (path.startsWith('/balance'))
|
|
16
|
+
return '0';
|
|
17
|
+
return '0';
|
|
18
|
+
}
|
|
19
|
+
export async function buildPaymentHeader(wallet, amount) {
|
|
20
|
+
const nonce = Date.now().toString();
|
|
21
|
+
const signature = await wallet.signMessage(`x402:${nonce}:${amount}`);
|
|
22
|
+
return Buffer.from(JSON.stringify({
|
|
23
|
+
wallet: wallet.address,
|
|
24
|
+
signature,
|
|
25
|
+
amount,
|
|
26
|
+
nonce,
|
|
27
|
+
})).toString('base64');
|
|
28
|
+
}
|
|
29
|
+
export async function callApi(gatewayUrl, wallet, method, path, body) {
|
|
30
|
+
const price = getPrice(path);
|
|
31
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
32
|
+
if (wallet) {
|
|
33
|
+
headers['x402-payment'] = await buildPaymentHeader(wallet, price);
|
|
34
|
+
}
|
|
35
|
+
const res = await fetch(`${gatewayUrl}${path}`, {
|
|
36
|
+
method,
|
|
37
|
+
headers,
|
|
38
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
39
|
+
});
|
|
40
|
+
const data = await res.json();
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
const msg = data?.error ?? res.statusText;
|
|
43
|
+
throw new Error(`${res.status}: ${msg}`);
|
|
44
|
+
}
|
|
45
|
+
return data;
|
|
46
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* x402Trade MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Lets AI assistants (Claude, Cursor, etc.) interact with x402Trade directly.
|
|
6
|
+
*
|
|
7
|
+
* Setup:
|
|
8
|
+
* PRIVATE_KEY=0x... (your Ethereum wallet private key)
|
|
9
|
+
* X402_GATEWAY_URL=https://api.getx402.trade (optional, defaults to production)
|
|
10
|
+
*
|
|
11
|
+
* Tools exposed:
|
|
12
|
+
* x402_get_price — Current price, 24h change and volume for a pair
|
|
13
|
+
* x402_get_orderbook — Live bids and asks
|
|
14
|
+
* x402_get_balance — Your USDC and ETH balance
|
|
15
|
+
* x402_place_order — Place a limit or market order
|
|
16
|
+
* x402_cancel_order — Cancel an open order
|
|
17
|
+
* x402_get_orders — List your open/recent orders
|
|
18
|
+
* x402_get_recent_trades — Recent fills on a pair
|
|
19
|
+
* x402_deposit_info — How to deposit USDC
|
|
20
|
+
*/
|
|
21
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
22
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
23
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
24
|
+
import { ethers } from 'ethers';
|
|
25
|
+
import { callApi } from './client.js';
|
|
26
|
+
// ── Config ────────────────────────────────────────────────────────────────────
|
|
27
|
+
const GATEWAY_URL = (process.env.X402_GATEWAY_URL || 'https://api.getx402.trade').replace(/\/$/, '');
|
|
28
|
+
const PRIVATE_KEY = process.env.PRIVATE_KEY || '';
|
|
29
|
+
const wallet = PRIVATE_KEY ? new ethers.Wallet(PRIVATE_KEY) : null;
|
|
30
|
+
if (!wallet) {
|
|
31
|
+
process.stderr.write('[x402-mcp] WARNING: PRIVATE_KEY not set. Read-only mode (no trading).\n');
|
|
32
|
+
}
|
|
33
|
+
function api(method, path, body) {
|
|
34
|
+
return callApi(GATEWAY_URL, wallet, method, path, body);
|
|
35
|
+
}
|
|
36
|
+
// ── Tool Definitions ──────────────────────────────────────────────────────────
|
|
37
|
+
const TOOLS = [
|
|
38
|
+
{
|
|
39
|
+
name: 'x402_get_price',
|
|
40
|
+
description: 'Get the current price, 24h change, volume, high and low for a trading pair on x402Trade.',
|
|
41
|
+
inputSchema: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
pair: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: 'Trading pair. Supported: ETH-USDC, BTC-USDC',
|
|
47
|
+
enum: ['ETH-USDC', 'BTC-USDC'],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
required: ['pair'],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'x402_get_orderbook',
|
|
55
|
+
description: 'Get the live order book (bids and asks) for a trading pair. Shows current buy and sell orders.',
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {
|
|
59
|
+
pair: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
description: 'Trading pair. Supported: ETH-USDC, BTC-USDC',
|
|
62
|
+
enum: ['ETH-USDC', 'BTC-USDC'],
|
|
63
|
+
},
|
|
64
|
+
depth: {
|
|
65
|
+
type: 'number',
|
|
66
|
+
description: 'Number of price levels to return per side. Default 10, max 50.',
|
|
67
|
+
default: 10,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
required: ['pair'],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'x402_get_balance',
|
|
75
|
+
description: 'Get the USDC and ETH balance for the configured wallet on x402Trade. Requires PRIVATE_KEY to be set.',
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: 'object',
|
|
78
|
+
properties: {},
|
|
79
|
+
required: [],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'x402_place_order',
|
|
84
|
+
description: `Place a buy or sell order on x402Trade.
|
|
85
|
+
- Limit order: executes at your specified price or better. Stays in the order book if not immediately matched.
|
|
86
|
+
- Market order: executes immediately at the best available price.
|
|
87
|
+
Costs $0.01 USDC per call. Requires PRIVATE_KEY and sufficient USDC balance.`,
|
|
88
|
+
inputSchema: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
pair: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
description: 'Trading pair. Supported: ETH-USDC, BTC-USDC',
|
|
94
|
+
enum: ['ETH-USDC', 'BTC-USDC'],
|
|
95
|
+
},
|
|
96
|
+
side: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
description: 'Buy or sell',
|
|
99
|
+
enum: ['buy', 'sell'],
|
|
100
|
+
},
|
|
101
|
+
type: {
|
|
102
|
+
type: 'string',
|
|
103
|
+
description: 'Order type: limit (specify price) or market (best available price)',
|
|
104
|
+
enum: ['limit', 'market'],
|
|
105
|
+
},
|
|
106
|
+
amount: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
description: 'Amount of base token (ETH or BTC) to buy or sell. E.g. "0.1" for 0.1 ETH.',
|
|
109
|
+
},
|
|
110
|
+
price: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
description: 'Price in USDC per base token. Required for limit orders. E.g. "2200" for $2200 per ETH.',
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
required: ['pair', 'side', 'type', 'amount'],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'x402_cancel_order',
|
|
120
|
+
description: 'Cancel an open or partially filled order. Cancellation is free. Locked funds are returned to your balance.',
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: 'object',
|
|
123
|
+
properties: {
|
|
124
|
+
order_id: {
|
|
125
|
+
type: 'string',
|
|
126
|
+
description: 'The order ID to cancel (UUID format, returned when you placed the order)',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
required: ['order_id'],
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: 'x402_get_orders',
|
|
134
|
+
description: 'List your recent orders — open, filled, cancelled, and partially filled. Costs $0.001 USDC.',
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: {
|
|
138
|
+
pair: {
|
|
139
|
+
type: 'string',
|
|
140
|
+
description: 'Filter by trading pair. Leave empty to see all pairs.',
|
|
141
|
+
enum: ['ETH-USDC', 'BTC-USDC'],
|
|
142
|
+
},
|
|
143
|
+
limit: {
|
|
144
|
+
type: 'number',
|
|
145
|
+
description: 'Number of orders to return. Default 20, max 50.',
|
|
146
|
+
default: 20,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
required: [],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'x402_get_recent_trades',
|
|
154
|
+
description: 'Get recent completed trades (fills) on a trading pair. Costs $0.001 USDC.',
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: 'object',
|
|
157
|
+
properties: {
|
|
158
|
+
pair: {
|
|
159
|
+
type: 'string',
|
|
160
|
+
description: 'Trading pair. Supported: ETH-USDC, BTC-USDC',
|
|
161
|
+
enum: ['ETH-USDC', 'BTC-USDC'],
|
|
162
|
+
},
|
|
163
|
+
limit: {
|
|
164
|
+
type: 'number',
|
|
165
|
+
description: 'Number of recent trades to return. Default 20, max 50.',
|
|
166
|
+
default: 20,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
required: ['pair'],
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'x402_deposit_info',
|
|
174
|
+
description: 'Get instructions and the deposit address for adding USDC to your x402Trade balance.',
|
|
175
|
+
inputSchema: {
|
|
176
|
+
type: 'object',
|
|
177
|
+
properties: {},
|
|
178
|
+
required: [],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
// ── Tool Handlers ─────────────────────────────────────────────────────────────
|
|
183
|
+
async function handleTool(name, args) {
|
|
184
|
+
switch (name) {
|
|
185
|
+
case 'x402_get_price': {
|
|
186
|
+
const pair = args.pair;
|
|
187
|
+
const data = await api('GET', `/ticker?pair=${pair}`);
|
|
188
|
+
return JSON.stringify({
|
|
189
|
+
pair,
|
|
190
|
+
price_usdc: data.price,
|
|
191
|
+
change_24h_pct: data.change24h,
|
|
192
|
+
volume_24h_usdc: data.volume24h,
|
|
193
|
+
high_24h: data.high24h,
|
|
194
|
+
low_24h: data.low24h,
|
|
195
|
+
}, null, 2);
|
|
196
|
+
}
|
|
197
|
+
case 'x402_get_orderbook': {
|
|
198
|
+
const pair = args.pair;
|
|
199
|
+
const depth = Math.min(args.depth || 10, 50);
|
|
200
|
+
const data = await api('GET', `/orderbook?pair=${pair}&depth=${depth}`);
|
|
201
|
+
return JSON.stringify(data, null, 2);
|
|
202
|
+
}
|
|
203
|
+
case 'x402_get_balance': {
|
|
204
|
+
if (!wallet)
|
|
205
|
+
return 'Error: PRIVATE_KEY not configured. Set PRIVATE_KEY env var to check balance.';
|
|
206
|
+
const data = await api('GET', '/balance');
|
|
207
|
+
return JSON.stringify({
|
|
208
|
+
wallet: wallet.address,
|
|
209
|
+
usdc_balance: data.usdc_balance,
|
|
210
|
+
usdc_locked_in_orders: data.usdc_locked,
|
|
211
|
+
eth_balance: data.eth_balance,
|
|
212
|
+
eth_locked_in_orders: data.eth_locked,
|
|
213
|
+
}, null, 2);
|
|
214
|
+
}
|
|
215
|
+
case 'x402_place_order': {
|
|
216
|
+
if (!wallet)
|
|
217
|
+
return 'Error: PRIVATE_KEY not configured. Cannot place orders without a wallet.';
|
|
218
|
+
const { pair, side, type, amount, price } = args;
|
|
219
|
+
if (type === 'limit' && !price)
|
|
220
|
+
return 'Error: price is required for limit orders.';
|
|
221
|
+
const data = await api('POST', '/trade', { pair, side, type, amount, price });
|
|
222
|
+
return JSON.stringify({
|
|
223
|
+
order_id: data.orderId,
|
|
224
|
+
status: data.status,
|
|
225
|
+
filled_amount: data.filledAmount,
|
|
226
|
+
avg_fill_price: data.avgPrice,
|
|
227
|
+
trades_count: data.trades?.length ?? 0,
|
|
228
|
+
}, null, 2);
|
|
229
|
+
}
|
|
230
|
+
case 'x402_cancel_order': {
|
|
231
|
+
if (!wallet)
|
|
232
|
+
return 'Error: PRIVATE_KEY not configured. Cannot cancel orders without a wallet.';
|
|
233
|
+
const orderId = args.order_id;
|
|
234
|
+
const data = await api('DELETE', `/orders/${orderId}`);
|
|
235
|
+
return JSON.stringify({
|
|
236
|
+
cancelled: data.success,
|
|
237
|
+
order_id: data.orderId,
|
|
238
|
+
remaining_amount_unlocked: data.remainingAmount,
|
|
239
|
+
}, null, 2);
|
|
240
|
+
}
|
|
241
|
+
case 'x402_get_orders': {
|
|
242
|
+
if (!wallet)
|
|
243
|
+
return 'Error: PRIVATE_KEY not configured. Cannot list orders without a wallet.';
|
|
244
|
+
const pair = args.pair;
|
|
245
|
+
const limit = Math.min(args.limit || 20, 50);
|
|
246
|
+
const qs = new URLSearchParams({ limit: String(limit) });
|
|
247
|
+
if (pair)
|
|
248
|
+
qs.set('pair', pair);
|
|
249
|
+
const data = await api('GET', `/orders?${qs}`);
|
|
250
|
+
return JSON.stringify(data, null, 2);
|
|
251
|
+
}
|
|
252
|
+
case 'x402_get_recent_trades': {
|
|
253
|
+
const pair = args.pair;
|
|
254
|
+
const limit = Math.min(args.limit || 20, 50);
|
|
255
|
+
const data = await api('GET', `/trades?pair=${pair}&limit=${limit}`);
|
|
256
|
+
return JSON.stringify(data, null, 2);
|
|
257
|
+
}
|
|
258
|
+
case 'x402_deposit_info': {
|
|
259
|
+
const walletAddr = wallet?.address ?? '(configure PRIVATE_KEY to see your wallet address)';
|
|
260
|
+
return JSON.stringify({
|
|
261
|
+
instructions: [
|
|
262
|
+
'1. Send USDC to the deposit address below on Base L2 (Chain ID: 8453)',
|
|
263
|
+
'2. Wait ~15 seconds for the transaction to confirm',
|
|
264
|
+
'3. Your balance updates automatically — call x402_get_balance to verify',
|
|
265
|
+
],
|
|
266
|
+
deposit_address: 'See DEPOSIT_ADDRESS in exchange config (ask the exchange operator)',
|
|
267
|
+
network: 'Base L2 (Ethereum Layer 2)',
|
|
268
|
+
token: 'USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bA02913C)',
|
|
269
|
+
your_wallet: walletAddr,
|
|
270
|
+
minimum_deposit: 'No minimum',
|
|
271
|
+
note: 'Only send USDC on Base L2. Sending on other networks will result in lost funds.',
|
|
272
|
+
}, null, 2);
|
|
273
|
+
}
|
|
274
|
+
default:
|
|
275
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// ── MCP Server ────────────────────────────────────────────────────────────────
|
|
279
|
+
const server = new Server({ name: 'x402trade', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
280
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
281
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
282
|
+
const { name, arguments: args } = request.params;
|
|
283
|
+
try {
|
|
284
|
+
const result = await handleTool(name, (args ?? {}));
|
|
285
|
+
return { content: [{ type: 'text', text: result }] };
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
289
|
+
return { content: [{ type: 'text', text: `Error: ${message}` }], isError: true };
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
293
|
+
const transport = new StdioServerTransport();
|
|
294
|
+
await server.connect(transport);
|
|
295
|
+
process.stderr.write(`[x402-mcp] Server started. Wallet: ${wallet?.address ?? 'not configured (read-only)'}\n`);
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "x402trade-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for x402Trade — lets AI agents (Claude, Cursor, etc.) trade on x402Trade",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"x402-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"test:integration": "tsx src/__tests__/integration.ts"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
20
|
+
"ethers": "^6.13.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.0.0",
|
|
24
|
+
"tsx": "^4.0.0",
|
|
25
|
+
"typescript": "^5.0.0",
|
|
26
|
+
"vitest": "^2.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|