cryptoiz-mcp 2.0.1 → 2.0.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/package.json +1 -1
- package/src/index.js +92 -156
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,25 +1,4 @@
|
|
|
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
|
-
|
|
23
2
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
24
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
25
4
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -32,108 +11,117 @@ const SVM_PRIVATE_KEY = process.env.SVM_PRIVATE_KEY || '';
|
|
|
32
11
|
const CRYPTOIZ_WALLET = new PublicKey('DsKmdkYx49Xc1WhqMUAztwhdYPTqieyC98VmnnJdgpXX');
|
|
33
12
|
const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
|
|
34
13
|
const SOL_RPC = 'https://api.mainnet-beta.solana.com';
|
|
35
|
-
const AMOUNT = 10000;
|
|
36
|
-
|
|
37
|
-
// ─── Payment handler ─────────────────────────────────────────────────────────
|
|
14
|
+
const AMOUNT = 10000;
|
|
38
15
|
|
|
39
16
|
async function payAndFetch(url) {
|
|
40
17
|
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
|
-
};
|
|
18
|
+
return { error: 'SVM_PRIVATE_KEY tidak ditemukan.', setup: 'Tambahkan SVM_PRIVATE_KEY ke config Claude Desktop.', help: 'cryptoiz.org' };
|
|
46
19
|
}
|
|
47
|
-
|
|
48
|
-
// First request
|
|
49
20
|
const first = await fetch(url);
|
|
50
21
|
if (first.status !== 402) return first.json();
|
|
51
|
-
|
|
52
|
-
// 402 received — pay USDC
|
|
53
22
|
try {
|
|
54
23
|
const keypair = Keypair.fromSecretKey(bs58.decode(SVM_PRIVATE_KEY));
|
|
55
24
|
const connection = new Connection(SOL_RPC, 'confirmed');
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
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
|
-
|
|
25
|
+
const fromAta = await getOrCreateAssociatedTokenAccount(connection, keypair, USDC_MINT, keypair.publicKey);
|
|
26
|
+
const toAta = await getOrCreateAssociatedTokenAccount(connection, keypair, USDC_MINT, CRYPTOIZ_WALLET);
|
|
27
|
+
const tx = new Transaction().add(createTransferInstruction(fromAta.address, toAta.address, keypair.publicKey, AMOUNT));
|
|
68
28
|
const signature = await connection.sendTransaction(tx, [keypair]);
|
|
69
29
|
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
|
-
|
|
30
|
+
const payment = Buffer.from(JSON.stringify({ signature, network: 'solana', x402Version: 1 })).toString('base64');
|
|
76
31
|
const paidRes = await fetch(url, { headers: { 'x-payment': payment } });
|
|
77
32
|
return paidRes.json();
|
|
78
|
-
|
|
79
33
|
} catch (err) {
|
|
80
|
-
return {
|
|
81
|
-
error: `Payment gagal: ${err.message}`,
|
|
82
|
-
hint: 'Pastikan wallet kamu punya USDC dan SOL untuk gas.',
|
|
83
|
-
};
|
|
34
|
+
return { error: `Payment gagal: ${err.message}`, hint: 'Pastikan wallet punya USDC dan SOL untuk gas.' };
|
|
84
35
|
}
|
|
85
36
|
}
|
|
86
37
|
|
|
87
38
|
// ─── Formatters ───────────────────────────────────────────────────────────────
|
|
88
39
|
|
|
40
|
+
function fmt(n, dec=2) { return n != null ? parseFloat(n).toFixed(dec) : 'N/A'; }
|
|
41
|
+
function fmtMC(mc) { if (!mc) return 'N/A'; if (mc >= 1e6) return `$${(mc/1e6).toFixed(2)}M`; return `$${(mc/1000).toFixed(0)}K`; }
|
|
42
|
+
function fmtDelta(d) { return d > 0 ? `+${d}` : `${d}`; }
|
|
43
|
+
function fmtRisk(r) { return r > 0 ? `⚠️ ${fmt(r)}%` : '✅ 0%'; }
|
|
44
|
+
|
|
89
45
|
function formatAlpha(data) {
|
|
90
|
-
if (data.error) return
|
|
46
|
+
if (data.error) return `❌ ERROR: ${data.error}\n${data.setup||''}`;
|
|
91
47
|
if (!data.signals?.length) return 'Tidak ada sinyal Alpha Scanner saat ini.';
|
|
92
48
|
const lines = [
|
|
93
|
-
|
|
94
|
-
|
|
49
|
+
`╔═══ CRYPTOIZ ALPHA SCANNER ═══╗`,
|
|
50
|
+
`📅 ${data.fetched_at?.split('T')[0]} | Total: ${data.total} sinyal`,
|
|
51
|
+
`💰 $0.01 USDC | cryptoiz.org | @cryptoiz_IDN`,
|
|
52
|
+
`╚═══════════════════════════════╝`,
|
|
95
53
|
'',
|
|
96
|
-
...data.signals.map((s, i) =>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
54
|
+
...data.signals.map((s, i) => [
|
|
55
|
+
`${i+1}. ━━━ ${s.name} ━━━`,
|
|
56
|
+
` 📊 Alpha Score : ${fmt(s.alpha_score)} | ${s.entry_class}`,
|
|
57
|
+
` 📍 Phase : ${s.phase} (Conf: ${fmt(s.phase_confidence)}% | Age: ${s.phase_age_bars} bars)`,
|
|
58
|
+
` 🌐 Regime : ${s.regime}`,
|
|
59
|
+
` 🐋 Whale : ${fmtDelta(s.whale_delta)} (${fmt(s.whale_pct)}% supply)`,
|
|
60
|
+
` 🐬 Dolphin : ${fmtDelta(s.dolphin_delta)} (${fmt(s.dolphin_pct)}% supply)`,
|
|
61
|
+
` 🦐 Shrimp : ${fmtDelta(s.shrimp_delta)}`,
|
|
62
|
+
` 📈 Sub-scores : Accum ${fmt(s.accumulation_score)} | Timing ${fmt(s.timing_score)} | Safety ${fmt(s.safety_score)}`,
|
|
63
|
+
` ⚠️ Risks : Liq ${fmtRisk(s.liquidity_drain_risk)} | Trap ${fmtRisk(s.smart_money_trap_risk)}`,
|
|
64
|
+
` 📉 Price Drop : ${s.price_drop_pct != null ? fmt(s.price_drop_pct)+'%' : 'N/A'}`,
|
|
65
|
+
` 💰 Market Cap : ${fmtMC(s.market_cap_usd)}`,
|
|
66
|
+
` 💵 Price : $${s.price_usd ? parseFloat(s.price_usd).toFixed(6) : 'N/A'}`,
|
|
67
|
+
` 🔗 Address : ${s.token_address}`,
|
|
68
|
+
].join('\n')),
|
|
102
69
|
'',
|
|
103
|
-
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
70
|
+
'📊 Data by CryptoIZ — cryptoiz.org | @cryptoiz_IDN',
|
|
104
71
|
];
|
|
105
72
|
return lines.join('\n');
|
|
106
73
|
}
|
|
107
74
|
|
|
108
75
|
function formatDivergence(data) {
|
|
109
|
-
if (data.error) return
|
|
76
|
+
if (data.error) return `❌ ERROR: ${data.error}`;
|
|
110
77
|
if (!data.signals?.length) return `Tidak ada divergence signal (${data.timeframe}).`;
|
|
111
78
|
const lines = [
|
|
112
|
-
|
|
113
|
-
|
|
79
|
+
`╔═══ CRYPTOIZ DIVERGENCE SCANNER ═══╗`,
|
|
80
|
+
`📅 ${data.timeframe?.toUpperCase()} | Total: ${data.total}`,
|
|
81
|
+
`💰 $0.01 USDC | cryptoiz.org | @cryptoiz_IDN`,
|
|
82
|
+
`╚════════════════════════════════════╝`,
|
|
114
83
|
'',
|
|
115
84
|
...data.signals.map((s, i) => {
|
|
116
|
-
const type = s.divergence_type === 'bullish' ? 'BULLISH
|
|
117
|
-
return
|
|
85
|
+
const type = s.divergence_type === 'bullish' ? '🟢 BULLISH' : s.divergence_type === 'bearish' ? '🔴 BEARISH' : (s.divergence_type||'').toUpperCase();
|
|
86
|
+
return [
|
|
87
|
+
`${i+1}. ━━━ ${s.name||s.symbol} ━━━`,
|
|
88
|
+
` 📊 Divergence : ${type} (Score: ${fmt(s.divergence_score)})`,
|
|
89
|
+
` 🎯 Confidence : ${fmt(s.confidence_score)}%`,
|
|
90
|
+
` 📍 Phase : ${s.phase_label}`,
|
|
91
|
+
` 🐋 Whale : ${fmtDelta(s.whale_delta)} | 🐬 Dolphin: ${fmtDelta(s.dolphin_delta)}`,
|
|
92
|
+
` 💰 Market Cap : ${fmtMC(s.market_cap_usd)}`,
|
|
93
|
+
` 💵 Price : $${s.price_usd ? parseFloat(s.price_usd).toFixed(6) : 'N/A'}`,
|
|
94
|
+
` 📉 Change : ${s.price_change_pct ? fmt(s.price_change_pct)+'%' : 'N/A'}`,
|
|
95
|
+
` 🔗 Address : ${s.token_address}`,
|
|
96
|
+
` 🕐 Detected : ${s.detected_at ? new Date(s.detected_at).toUTCString() : 'N/A'}`,
|
|
97
|
+
].join('\n');
|
|
118
98
|
}),
|
|
119
99
|
'',
|
|
120
|
-
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
100
|
+
'📊 Data by CryptoIZ — cryptoiz.org | @cryptoiz_IDN',
|
|
121
101
|
];
|
|
122
102
|
return lines.join('\n');
|
|
123
103
|
}
|
|
124
104
|
|
|
125
105
|
function formatAccum(data) {
|
|
126
|
-
if (data.error) return
|
|
106
|
+
if (data.error) return `❌ ERROR: ${data.error}`;
|
|
127
107
|
if (!data.tokens?.length) return 'Tidak ada token dalam fase akumulasi.';
|
|
128
108
|
const lines = [
|
|
129
|
-
|
|
130
|
-
|
|
109
|
+
`╔═══ CRYPTOIZ AKUMULASI ═══╗`,
|
|
110
|
+
`📅 ${data.fetched_at?.split('T')[0]} | Total: ${data.total}`,
|
|
111
|
+
`💰 $0.01 USDC | cryptoiz.org | @cryptoiz_IDN`,
|
|
112
|
+
`╚══════════════════════════╝`,
|
|
131
113
|
'',
|
|
132
|
-
...data.tokens.map((t, i) =>
|
|
133
|
-
`${i+1}. ${t.name}
|
|
134
|
-
|
|
114
|
+
...data.tokens.map((t, i) => [
|
|
115
|
+
`${i+1}. ━━━ ${t.name} ━━━`,
|
|
116
|
+
` 📊 Composite : ${fmt(t.composite_score)} | ${t.strength_label}`,
|
|
117
|
+
` 🔄 AccDist : ${t.accdist_label} (Score: ${fmt(t.accdist_score)})`,
|
|
118
|
+
` 📈 Structure : ${fmt(t.structure_score)}`,
|
|
119
|
+
` 👥 Holder : ${fmt(t.holder_score)}`,
|
|
120
|
+
` 📉 Market : ${fmt(t.market_score)}`,
|
|
121
|
+
` 🔗 Address : ${t.token_address}`,
|
|
122
|
+
].join('\n')),
|
|
135
123
|
'',
|
|
136
|
-
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
124
|
+
'📊 Data by CryptoIZ — cryptoiz.org | @cryptoiz_IDN',
|
|
137
125
|
];
|
|
138
126
|
return lines.join('\n');
|
|
139
127
|
}
|
|
@@ -141,112 +129,60 @@ function formatAccum(data) {
|
|
|
141
129
|
function formatStatus() {
|
|
142
130
|
const hasKey = !!SVM_PRIVATE_KEY;
|
|
143
131
|
return [
|
|
144
|
-
|
|
145
|
-
`Status : CONNECTED`,
|
|
146
|
-
`Payment : ${hasKey ? 'READY — x402 Solana USDC ($0.01/call)' : 'NOT SET — tambahkan SVM_PRIVATE_KEY'}`,
|
|
132
|
+
`╔═══ CryptoIZ MCP Server v2.0.2 ═══╗`,
|
|
133
|
+
` Status : ✅ CONNECTED`,
|
|
134
|
+
` Payment : ${hasKey ? '✅ READY — x402 Solana USDC ($0.01/call)' : '❌ NOT SET — tambahkan SVM_PRIVATE_KEY'}`,
|
|
135
|
+
` Wallet : DsKmdkYx49Xc1WhqMUAztwhdYPTqieyC98VmnnJdgpXX`,
|
|
136
|
+
`╚═══════════════════════════════════╝`,
|
|
147
137
|
``,
|
|
148
138
|
`Tools tersedia:`,
|
|
149
|
-
` 1. get_alpha_scanner — Alpha entry signals
|
|
150
|
-
` 2. get_divergence — Divergence bullish/bearish`,
|
|
151
|
-
` 3. get_accumulation — Token fase akumulasi`,
|
|
152
|
-
` 4. get_status — Info ini`,
|
|
139
|
+
` 1. get_alpha_scanner — Alpha entry signals + address + MC`,
|
|
140
|
+
` 2. get_divergence — Divergence bullish/bearish + address`,
|
|
141
|
+
` 3. get_accumulation — Token fase akumulasi + address`,
|
|
142
|
+
` 4. get_status — Info server ini`,
|
|
153
143
|
``,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
`Info : cryptoiz.org | @cryptoiz_IDN`,
|
|
144
|
+
`💰 $0.01 USDC per call via Solana`,
|
|
145
|
+
`🌐 cryptoiz.org | @cryptoiz_IDN`,
|
|
157
146
|
].join('\n');
|
|
158
147
|
}
|
|
159
148
|
|
|
160
|
-
// ─── Tools definition ─────────────────────────────────────────────────────────
|
|
161
|
-
|
|
162
149
|
const TOOLS = [
|
|
163
|
-
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
type: 'object',
|
|
168
|
-
properties: {
|
|
169
|
-
min_score: { type: 'number', description: 'Minimum alpha score (0-100).' },
|
|
170
|
-
},
|
|
171
|
-
required: [],
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
{
|
|
175
|
-
name: 'get_divergence',
|
|
176
|
-
description: 'Ambil divergence signals dari CryptoIZ — deteksi bullish/bearish divergence whale/dolphin di Solana. Biaya: $0.01 USDC.',
|
|
177
|
-
inputSchema: {
|
|
178
|
-
type: 'object',
|
|
179
|
-
properties: {
|
|
180
|
-
timeframe: { type: 'string', enum: ['4h', '1d'], description: 'Timeframe. Default: 4h.' },
|
|
181
|
-
limit: { type: 'number', description: 'Max results (1-50). Default: 20.' },
|
|
182
|
-
},
|
|
183
|
-
required: [],
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
name: 'get_accumulation',
|
|
188
|
-
description: 'Ambil token dalam fase akumulasi dari CryptoIZ Accumulation Dashboard. Biaya: $0.01 USDC.',
|
|
189
|
-
inputSchema: {
|
|
190
|
-
type: 'object',
|
|
191
|
-
properties: {
|
|
192
|
-
min_composite: { type: 'number', description: 'Minimum composite score (0-100).' },
|
|
193
|
-
},
|
|
194
|
-
required: [],
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
name: 'get_status',
|
|
199
|
-
description: 'Cek status koneksi CryptoIZ MCP, payment setup, dan tools yang tersedia.',
|
|
200
|
-
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
201
|
-
},
|
|
150
|
+
{ name: 'get_alpha_scanner', description: 'Sinyal token Solana terkuat dari CryptoIZ Alpha Scanner — lengkap dengan address, MC, price, holder signals, sub-scores, risks. $0.01 USDC/call.', inputSchema: { type: 'object', properties: { min_score: { type: 'number', description: 'Minimum alpha score (0-100).' }, entry_class: { type: 'string', enum: ['ALPHA_EARLY','ALPHA_BUILDING','WATCHLIST_ONLY'] } }, required: [] } },
|
|
151
|
+
{ name: 'get_divergence', description: 'Divergence signals dari CryptoIZ — bullish/bearish dengan address, MC, confidence score. $0.01 USDC/call.', inputSchema: { type: 'object', properties: { timeframe: { type: 'string', enum: ['4h','1d'] }, limit: { type: 'number' } }, required: [] } },
|
|
152
|
+
{ name: 'get_accumulation', description: 'Token fase akumulasi dari CryptoIZ Accumulation Dashboard — composite score, holder, market score + address. $0.01 USDC/call.', inputSchema: { type: 'object', properties: { min_composite: { type: 'number' } }, required: [] } },
|
|
153
|
+
{ name: 'get_status', description: 'Status koneksi CryptoIZ MCP dan info payment.', inputSchema: { type: 'object', properties: {}, required: [] } },
|
|
202
154
|
];
|
|
203
155
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const server = new Server(
|
|
207
|
-
{ name: 'cryptoiz-mcp', version: '2.0.1' },
|
|
208
|
-
{ capabilities: { tools: {} } }
|
|
209
|
-
);
|
|
210
|
-
|
|
156
|
+
const server = new Server({ name: 'cryptoiz-mcp', version: '2.0.2' }, { capabilities: { tools: {} } });
|
|
211
157
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
212
158
|
|
|
213
159
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
214
160
|
const { name, arguments: args } = request.params;
|
|
215
|
-
|
|
216
161
|
try {
|
|
217
|
-
if (name === 'get_status') {
|
|
218
|
-
return { content: [{ type: 'text', text: formatStatus() }] };
|
|
219
|
-
}
|
|
162
|
+
if (name === 'get_status') return { content: [{ type: 'text', text: formatStatus() }] };
|
|
220
163
|
|
|
221
164
|
if (name === 'get_alpha_scanner') {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
if (args?.
|
|
225
|
-
data.signals = data.signals.filter(s => s.alpha_score >= args.min_score);
|
|
226
|
-
data.total = data.signals.length;
|
|
227
|
-
}
|
|
165
|
+
const data = await payAndFetch(`${GATEWAY_URL}?tool=get_alpha_scanner`);
|
|
166
|
+
if (args?.min_score && data.signals) { data.signals = data.signals.filter(s => s.alpha_score >= args.min_score); data.total = data.signals.length; }
|
|
167
|
+
if (args?.entry_class && data.signals) { data.signals = data.signals.filter(s => s.entry_class === args.entry_class); data.total = data.signals.length; }
|
|
228
168
|
return { content: [{ type: 'text', text: formatAlpha(data) }] };
|
|
229
169
|
}
|
|
230
170
|
|
|
231
171
|
if (name === 'get_divergence') {
|
|
232
|
-
const
|
|
233
|
-
if (args?.timeframe)
|
|
234
|
-
if (args?.limit)
|
|
235
|
-
const data = await payAndFetch(`${GATEWAY_URL}?${
|
|
172
|
+
const p = new URLSearchParams({ tool: 'get_divergence' });
|
|
173
|
+
if (args?.timeframe) p.set('tf', args.timeframe);
|
|
174
|
+
if (args?.limit) p.set('limit', String(args.limit));
|
|
175
|
+
const data = await payAndFetch(`${GATEWAY_URL}?${p}`);
|
|
236
176
|
return { content: [{ type: 'text', text: formatDivergence(data) }] };
|
|
237
177
|
}
|
|
238
178
|
|
|
239
179
|
if (name === 'get_accumulation') {
|
|
240
180
|
const data = await payAndFetch(`${GATEWAY_URL}?tool=get_accumulation`);
|
|
241
|
-
if (args?.min_composite && data.tokens) {
|
|
242
|
-
data.tokens = data.tokens.filter(t => t.composite_score >= args.min_composite);
|
|
243
|
-
data.total = data.tokens.length;
|
|
244
|
-
}
|
|
181
|
+
if (args?.min_composite && data.tokens) { data.tokens = data.tokens.filter(t => t.composite_score >= args.min_composite); data.total = data.tokens.length; }
|
|
245
182
|
return { content: [{ type: 'text', text: formatAccum(data) }] };
|
|
246
183
|
}
|
|
247
184
|
|
|
248
185
|
return { content: [{ type: 'text', text: `Tool tidak dikenal: ${name}` }], isError: true };
|
|
249
|
-
|
|
250
186
|
} catch (err) {
|
|
251
187
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
252
188
|
}
|