cryptoiz-mcp 1.0.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/package.json +7 -7
- package/src/index.js +180 -80
package/package.json
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cryptoiz-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "CryptoIZ MCP Server — Solana DEX
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "CryptoIZ MCP Server — Solana DEX signals with x402 micropayments. Pay $0.01 USDC per call.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"cryptoiz-mcp": "src/index.js"
|
|
9
9
|
},
|
|
10
|
-
"
|
|
11
|
-
"start": "node src/index.js"
|
|
12
|
-
},
|
|
13
|
-
"keywords": ["cryptoiz", "solana", "defi", "mcp", "claude", "crypto", "alpha", "signals", "divergence", "accumulation"],
|
|
10
|
+
"keywords": ["cryptoiz", "solana", "defi", "mcp", "claude", "crypto", "alpha", "signals", "x402", "usdc"],
|
|
14
11
|
"author": "CryptoIZ <cryptoiz.suport@gmail.com> (https://cryptoiz.org)",
|
|
15
12
|
"license": "MIT",
|
|
16
13
|
"repository": {
|
|
@@ -19,7 +16,10 @@
|
|
|
19
16
|
},
|
|
20
17
|
"homepage": "https://cryptoiz.org",
|
|
21
18
|
"dependencies": {
|
|
22
|
-
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
20
|
+
"@solana/web3.js": "^1.98.0",
|
|
21
|
+
"@solana/spl-token": "^0.4.9",
|
|
22
|
+
"bs58": "^6.0.0"
|
|
23
23
|
},
|
|
24
24
|
"engines": {
|
|
25
25
|
"node": ">=18.0.0"
|
package/src/index.js
CHANGED
|
@@ -1,149 +1,249 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CryptoIZ MCP Server v2.0.0
|
|
4
|
+
* Solana DEX signals with x402 micropayments — $0.01 USDC per call
|
|
5
|
+
*
|
|
6
|
+
* Setup di Claude Desktop:
|
|
7
|
+
* {
|
|
8
|
+
* "mcpServers": {
|
|
9
|
+
* "cryptoiz": {
|
|
10
|
+
* "command": "npx",
|
|
11
|
+
* "args": ["cryptoiz-mcp"],
|
|
12
|
+
* "env": {
|
|
13
|
+
* "SVM_PRIVATE_KEY": "your-solana-private-key-base58"
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* User harus punya USDC di wallet Solana mereka.
|
|
20
|
+
* Setiap call = $0.01 USDC otomatis terkirim ke CryptoIZ.
|
|
21
|
+
*/
|
|
22
|
+
|
|
2
23
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
24
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
25
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
26
|
+
import { Connection, Keypair, PublicKey, Transaction } from '@solana/web3.js';
|
|
27
|
+
import { getOrCreateAssociatedTokenAccount, createTransferInstruction } from '@solana/spl-token';
|
|
28
|
+
import bs58 from 'bs58';
|
|
29
|
+
|
|
30
|
+
const GATEWAY_URL = 'https://rehqwsypjnjirhuiapqh.supabase.co/functions/v1/mcp-x402-gateway';
|
|
31
|
+
const SVM_PRIVATE_KEY = process.env.SVM_PRIVATE_KEY || '';
|
|
32
|
+
const CRYPTOIZ_WALLET = new PublicKey('DsKmdkYx49Xc1WhqMUAztwhdYPTqieyC98VmnnJdgpXX');
|
|
33
|
+
const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
|
|
34
|
+
const SOL_RPC = 'https://api.mainnet-beta.solana.com';
|
|
35
|
+
const AMOUNT = 10000; // 0.01 USDC
|
|
36
|
+
|
|
37
|
+
// ─── Payment handler ─────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
async function payAndFetch(url) {
|
|
40
|
+
if (!SVM_PRIVATE_KEY) {
|
|
41
|
+
return {
|
|
42
|
+
error: 'SVM_PRIVATE_KEY tidak ditemukan.',
|
|
43
|
+
setup: 'Tambahkan SVM_PRIVATE_KEY (private key Solana kamu) ke config Claude Desktop.',
|
|
44
|
+
help: 'cryptoiz.org/McpApiKey',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// First request
|
|
49
|
+
const first = await fetch(url);
|
|
50
|
+
if (first.status !== 402) return first.json();
|
|
51
|
+
|
|
52
|
+
// 402 received — pay USDC
|
|
53
|
+
try {
|
|
54
|
+
const keypair = Keypair.fromSecretKey(bs58.decode(SVM_PRIVATE_KEY));
|
|
55
|
+
const connection = new Connection(SOL_RPC, 'confirmed');
|
|
5
56
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
57
|
+
const fromAta = await getOrCreateAssociatedTokenAccount(
|
|
58
|
+
connection, keypair, USDC_MINT, keypair.publicKey
|
|
59
|
+
);
|
|
60
|
+
const toAta = await getOrCreateAssociatedTokenAccount(
|
|
61
|
+
connection, keypair, USDC_MINT, CRYPTOIZ_WALLET
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const tx = new Transaction().add(
|
|
65
|
+
createTransferInstruction(fromAta.address, toAta.address, keypair.publicKey, AMOUNT)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const signature = await connection.sendTransaction(tx, [keypair]);
|
|
69
|
+
await connection.confirmTransaction(signature, 'confirmed');
|
|
70
|
+
|
|
71
|
+
// Retry with payment proof
|
|
72
|
+
const payment = Buffer.from(
|
|
73
|
+
JSON.stringify({ signature, network: 'solana', x402Version: 1 })
|
|
74
|
+
).toString('base64');
|
|
75
|
+
|
|
76
|
+
const paidRes = await fetch(url, { headers: { 'x-payment': payment } });
|
|
77
|
+
return paidRes.json();
|
|
78
|
+
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return {
|
|
81
|
+
error: `Payment gagal: ${err.message}`,
|
|
82
|
+
hint: 'Pastikan wallet kamu punya USDC dan SOL untuk gas.',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
18
85
|
}
|
|
19
86
|
|
|
20
|
-
|
|
21
|
-
|
|
87
|
+
// ─── Formatters ───────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
function formatAlpha(data) {
|
|
90
|
+
if (data.error) return `ERROR: ${data.error}\n${data.setup || ''}`;
|
|
91
|
+
if (!data.signals?.length) return 'Tidak ada sinyal Alpha Scanner saat ini.';
|
|
22
92
|
const lines = [
|
|
23
|
-
`CRYPTOIZ ALPHA SCANNER`,
|
|
24
|
-
`
|
|
25
|
-
data.note ? `INFO: ${data.note}` : '',
|
|
93
|
+
`CRYPTOIZ ALPHA SCANNER | ${data.fetched_at?.split('T')[0]}`,
|
|
94
|
+
`Total: ${data.total} sinyal | Powered by cryptoiz.org`,
|
|
26
95
|
'',
|
|
27
|
-
...data.signals.map((s, i) =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}).filter(Boolean),
|
|
33
|
-
'', 'Source: CryptoIZ Alpha Entry Scanner | cryptoiz.org',
|
|
96
|
+
...data.signals.map((s, i) =>
|
|
97
|
+
`${i+1}. ${s.name} — Score: ${s.alpha_score} | ${s.entry_class}\n Phase: ${s.phase_label} | Whale: ${s.whale_delta > 0 ? '+' : ''}${s.whale_delta} | Dolphin: ${s.dolphin_delta > 0 ? '+' : ''}${s.dolphin_delta}`
|
|
98
|
+
),
|
|
99
|
+
'',
|
|
100
|
+
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
34
101
|
];
|
|
35
102
|
return lines.join('\n');
|
|
36
103
|
}
|
|
37
104
|
|
|
38
105
|
function formatDivergence(data) {
|
|
39
|
-
if (
|
|
106
|
+
if (data.error) return `ERROR: ${data.error}\n${data.hint || ''}`;
|
|
107
|
+
if (!data.signals?.length) return `Tidak ada divergence signal (${data.timeframe}).`;
|
|
40
108
|
const lines = [
|
|
41
|
-
`CRYPTOIZ DIVERGENCE
|
|
42
|
-
`
|
|
109
|
+
`CRYPTOIZ DIVERGENCE — ${data.timeframe?.toUpperCase()}`,
|
|
110
|
+
`Total: ${data.total} | Powered by cryptoiz.org`,
|
|
43
111
|
'',
|
|
44
112
|
...data.signals.map((s, i) => {
|
|
45
|
-
const type
|
|
46
|
-
|
|
47
|
-
const conf = s.confidence_score ? `Conf: ${s.confidence_score.toFixed(1)}` : '';
|
|
48
|
-
const mc = s.market_cap_usd ? `MC: $${(s.market_cap_usd/1000).toFixed(0)}K` : '';
|
|
49
|
-
const chg = s.price_change_pct ? `${s.price_change_pct>0?'+':''}${s.price_change_pct.toFixed(1)}%` : '';
|
|
50
|
-
return `${i+1}. ${s.token||s.symbol} [${type}]\n ${[score,conf,mc,chg].filter(Boolean).join(' | ')}`;
|
|
113
|
+
const type = s.divergence_type === 'bullish' ? 'BULLISH 🟢' : s.divergence_type === 'bearish' ? 'BEARISH 🔴' : (s.divergence_type||'').toUpperCase();
|
|
114
|
+
return `${i+1}. ${s.name||s.symbol} [${type}]\n Score: ${s.divergence_score?.toFixed(1)||'-'} | MC: $${((s.market_cap||0)/1000).toFixed(0)}K`;
|
|
51
115
|
}),
|
|
52
|
-
'',
|
|
116
|
+
'',
|
|
117
|
+
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
53
118
|
];
|
|
54
119
|
return lines.join('\n');
|
|
55
120
|
}
|
|
56
121
|
|
|
57
|
-
function
|
|
58
|
-
if (
|
|
122
|
+
function formatAccum(data) {
|
|
123
|
+
if (data.error) return `ERROR: ${data.error}\n${data.hint || ''}`;
|
|
124
|
+
if (!data.tokens?.length) return 'Tidak ada token dalam fase akumulasi.';
|
|
59
125
|
const lines = [
|
|
60
126
|
`CRYPTOIZ AKUMULASI`,
|
|
61
|
-
`
|
|
62
|
-
data.note ? `INFO: ${data.note}` : '',
|
|
127
|
+
`Total: ${data.total} | Powered by cryptoiz.org`,
|
|
63
128
|
'',
|
|
64
|
-
...data.tokens.map((t, i) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
'', 'Source: CryptoIZ Accumulation Dashboard | cryptoiz.org',
|
|
129
|
+
...data.tokens.map((t, i) =>
|
|
130
|
+
`${i+1}. ${t.name} — Composite: ${t.composite_score?.toFixed(1)||'N/A'} | ${t.strength_label}\n AccDist: ${t.accdist_score?.toFixed(0)||'-'} | Holder: ${t.holder_score?.toFixed(1)||'-'} | Market: ${t.market_score?.toFixed(1)||'-'}`
|
|
131
|
+
),
|
|
132
|
+
'',
|
|
133
|
+
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
70
134
|
];
|
|
71
135
|
return lines.join('\n');
|
|
72
136
|
}
|
|
73
137
|
|
|
74
138
|
function formatStatus() {
|
|
139
|
+
const hasKey = !!SVM_PRIVATE_KEY;
|
|
75
140
|
return [
|
|
76
|
-
`CryptoIZ MCP Server
|
|
77
|
-
`
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
`
|
|
82
|
-
`
|
|
83
|
-
|
|
141
|
+
`CryptoIZ MCP Server v2.0.0`,
|
|
142
|
+
`Status : CONNECTED`,
|
|
143
|
+
`Payment : ${hasKey ? 'READY — x402 Solana USDC ($0.01/call)' : 'NOT SET — tambahkan SVM_PRIVATE_KEY'}`,
|
|
144
|
+
``,
|
|
145
|
+
`Tools tersedia:`,
|
|
146
|
+
` 1. get_alpha_scanner — Alpha entry signals terkuat`,
|
|
147
|
+
` 2. get_divergence — Divergence bullish/bearish`,
|
|
148
|
+
` 3. get_accumulation — Token fase akumulasi`,
|
|
149
|
+
` 4. get_status — Info ini`,
|
|
150
|
+
``,
|
|
151
|
+
`Harga : $0.01 USDC per call (Solana)`,
|
|
152
|
+
`Wallet : DsKmdkYx49Xc1WhqMUAztwhdYPTqieyC98VmnnJdgpXX`,
|
|
153
|
+
`Info : cryptoiz.org | @cryptoiz_IDN`,
|
|
84
154
|
].join('\n');
|
|
85
155
|
}
|
|
86
156
|
|
|
157
|
+
// ─── Tools definition ─────────────────────────────────────────────────────────
|
|
158
|
+
|
|
87
159
|
const TOOLS = [
|
|
88
160
|
{
|
|
89
161
|
name: 'get_alpha_scanner',
|
|
90
|
-
description: '
|
|
91
|
-
inputSchema: {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
162
|
+
description: 'Ambil sinyal token Solana terkuat hari ini dari CryptoIZ Alpha Scanner. Biaya: $0.01 USDC per call via Solana.',
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
min_score: { type: 'number', description: 'Minimum alpha score (0-100).' },
|
|
167
|
+
},
|
|
168
|
+
required: [],
|
|
169
|
+
},
|
|
95
170
|
},
|
|
96
171
|
{
|
|
97
172
|
name: 'get_divergence',
|
|
98
|
-
description: '
|
|
99
|
-
inputSchema: {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
173
|
+
description: 'Ambil divergence signals dari CryptoIZ — deteksi bullish/bearish divergence whale/dolphin di Solana. Biaya: $0.01 USDC.',
|
|
174
|
+
inputSchema: {
|
|
175
|
+
type: 'object',
|
|
176
|
+
properties: {
|
|
177
|
+
timeframe: { type: 'string', enum: ['4h', '1d'], description: 'Timeframe. Default: 4h.' },
|
|
178
|
+
limit: { type: 'number', description: 'Max results (1-50). Default: 20.' },
|
|
179
|
+
},
|
|
180
|
+
required: [],
|
|
181
|
+
},
|
|
103
182
|
},
|
|
104
183
|
{
|
|
105
184
|
name: 'get_accumulation',
|
|
106
|
-
description: '
|
|
107
|
-
inputSchema: {
|
|
108
|
-
|
|
109
|
-
|
|
185
|
+
description: 'Ambil token dalam fase akumulasi dari CryptoIZ Accumulation Dashboard. Biaya: $0.01 USDC.',
|
|
186
|
+
inputSchema: {
|
|
187
|
+
type: 'object',
|
|
188
|
+
properties: {
|
|
189
|
+
min_composite: { type: 'number', description: 'Minimum composite score (0-100).' },
|
|
190
|
+
},
|
|
191
|
+
required: [],
|
|
192
|
+
},
|
|
110
193
|
},
|
|
111
194
|
{
|
|
112
195
|
name: 'get_status',
|
|
113
|
-
description: 'Cek status koneksi CryptoIZ MCP dan
|
|
196
|
+
description: 'Cek status koneksi CryptoIZ MCP, payment setup, dan tools yang tersedia.',
|
|
114
197
|
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
115
198
|
},
|
|
116
199
|
];
|
|
117
200
|
|
|
201
|
+
// ─── Server ───────────────────────────────────────────────────────────────────
|
|
202
|
+
|
|
118
203
|
const server = new Server(
|
|
119
|
-
{ name: 'cryptoiz-mcp', version: '
|
|
204
|
+
{ name: 'cryptoiz-mcp', version: '2.0.0' },
|
|
120
205
|
{ capabilities: { tools: {} } }
|
|
121
206
|
);
|
|
122
207
|
|
|
123
208
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
209
|
+
|
|
124
210
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
125
211
|
const { name, arguments: args } = request.params;
|
|
212
|
+
|
|
126
213
|
try {
|
|
127
|
-
if (name === 'get_status')
|
|
214
|
+
if (name === 'get_status') {
|
|
215
|
+
return { content: [{ type: 'text', text: formatStatus() }] };
|
|
216
|
+
}
|
|
217
|
+
|
|
128
218
|
if (name === 'get_alpha_scanner') {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
if (args?.
|
|
132
|
-
|
|
219
|
+
const url = `${GATEWAY_URL}?tool=get_alpha_scanner`;
|
|
220
|
+
const data = await payAndFetch(url);
|
|
221
|
+
if (args?.min_score && data.signals) {
|
|
222
|
+
data.signals = data.signals.filter(s => s.alpha_score >= args.min_score);
|
|
223
|
+
data.total = data.signals.length;
|
|
224
|
+
}
|
|
225
|
+
return { content: [{ type: 'text', text: formatAlpha(data) }] };
|
|
133
226
|
}
|
|
227
|
+
|
|
134
228
|
if (name === 'get_divergence') {
|
|
135
|
-
const params = {};
|
|
136
|
-
if (args?.timeframe) params.tf
|
|
137
|
-
if (args?.limit) params.limit
|
|
138
|
-
const data = await
|
|
229
|
+
const params = new URLSearchParams({ tool: 'get_divergence' });
|
|
230
|
+
if (args?.timeframe) params.set('tf', args.timeframe);
|
|
231
|
+
if (args?.limit) params.set('limit', String(args.limit));
|
|
232
|
+
const data = await payAndFetch(`${GATEWAY_URL}?${params}`);
|
|
139
233
|
return { content: [{ type: 'text', text: formatDivergence(data) }] };
|
|
140
234
|
}
|
|
235
|
+
|
|
141
236
|
if (name === 'get_accumulation') {
|
|
142
|
-
const data = await
|
|
143
|
-
if (args?.min_composite && data.
|
|
144
|
-
|
|
237
|
+
const data = await payAndFetch(`${GATEWAY_URL}?tool=get_accumulation`);
|
|
238
|
+
if (args?.min_composite && data.tokens) {
|
|
239
|
+
data.tokens = data.tokens.filter(t => t.composite_score >= args.min_composite);
|
|
240
|
+
data.total = data.tokens.length;
|
|
241
|
+
}
|
|
242
|
+
return { content: [{ type: 'text', text: formatAccum(data) }] };
|
|
145
243
|
}
|
|
244
|
+
|
|
146
245
|
return { content: [{ type: 'text', text: `Tool tidak dikenal: ${name}` }], isError: true };
|
|
246
|
+
|
|
147
247
|
} catch (err) {
|
|
148
248
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
149
249
|
}
|