cryptoiz-mcp 2.0.0 → 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 -153
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,105 +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
|
-
`${i+1}. ${s.name}
|
|
98
|
-
|
|
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')),
|
|
99
69
|
'',
|
|
100
|
-
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
70
|
+
'📊 Data by CryptoIZ — cryptoiz.org | @cryptoiz_IDN',
|
|
101
71
|
];
|
|
102
72
|
return lines.join('\n');
|
|
103
73
|
}
|
|
104
74
|
|
|
105
75
|
function formatDivergence(data) {
|
|
106
|
-
if (data.error) return
|
|
76
|
+
if (data.error) return `❌ ERROR: ${data.error}`;
|
|
107
77
|
if (!data.signals?.length) return `Tidak ada divergence signal (${data.timeframe}).`;
|
|
108
78
|
const lines = [
|
|
109
|
-
|
|
110
|
-
|
|
79
|
+
`╔═══ CRYPTOIZ DIVERGENCE SCANNER ═══╗`,
|
|
80
|
+
`📅 ${data.timeframe?.toUpperCase()} | Total: ${data.total}`,
|
|
81
|
+
`💰 $0.01 USDC | cryptoiz.org | @cryptoiz_IDN`,
|
|
82
|
+
`╚════════════════════════════════════╝`,
|
|
111
83
|
'',
|
|
112
84
|
...data.signals.map((s, i) => {
|
|
113
|
-
const type = s.divergence_type === 'bullish' ? 'BULLISH
|
|
114
|
-
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');
|
|
115
98
|
}),
|
|
116
99
|
'',
|
|
117
|
-
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
100
|
+
'📊 Data by CryptoIZ — cryptoiz.org | @cryptoiz_IDN',
|
|
118
101
|
];
|
|
119
102
|
return lines.join('\n');
|
|
120
103
|
}
|
|
121
104
|
|
|
122
105
|
function formatAccum(data) {
|
|
123
|
-
if (data.error) return
|
|
106
|
+
if (data.error) return `❌ ERROR: ${data.error}`;
|
|
124
107
|
if (!data.tokens?.length) return 'Tidak ada token dalam fase akumulasi.';
|
|
125
108
|
const lines = [
|
|
126
|
-
|
|
127
|
-
|
|
109
|
+
`╔═══ CRYPTOIZ AKUMULASI ═══╗`,
|
|
110
|
+
`📅 ${data.fetched_at?.split('T')[0]} | Total: ${data.total}`,
|
|
111
|
+
`💰 $0.01 USDC | cryptoiz.org | @cryptoiz_IDN`,
|
|
112
|
+
`╚══════════════════════════╝`,
|
|
128
113
|
'',
|
|
129
|
-
...data.tokens.map((t, i) =>
|
|
130
|
-
`${i+1}. ${t.name}
|
|
131
|
-
|
|
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')),
|
|
132
123
|
'',
|
|
133
|
-
'📊 cryptoiz.org | @cryptoiz_IDN',
|
|
124
|
+
'📊 Data by CryptoIZ — cryptoiz.org | @cryptoiz_IDN',
|
|
134
125
|
];
|
|
135
126
|
return lines.join('\n');
|
|
136
127
|
}
|
|
@@ -138,112 +129,60 @@ function formatAccum(data) {
|
|
|
138
129
|
function formatStatus() {
|
|
139
130
|
const hasKey = !!SVM_PRIVATE_KEY;
|
|
140
131
|
return [
|
|
141
|
-
|
|
142
|
-
`Status : CONNECTED`,
|
|
143
|
-
`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
|
+
`╚═══════════════════════════════════╝`,
|
|
144
137
|
``,
|
|
145
138
|
`Tools tersedia:`,
|
|
146
|
-
` 1. get_alpha_scanner — Alpha entry signals
|
|
147
|
-
` 2. get_divergence — Divergence bullish/bearish`,
|
|
148
|
-
` 3. get_accumulation — Token fase akumulasi`,
|
|
149
|
-
` 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`,
|
|
150
143
|
``,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
`Info : cryptoiz.org | @cryptoiz_IDN`,
|
|
144
|
+
`💰 $0.01 USDC per call via Solana`,
|
|
145
|
+
`🌐 cryptoiz.org | @cryptoiz_IDN`,
|
|
154
146
|
].join('\n');
|
|
155
147
|
}
|
|
156
148
|
|
|
157
|
-
// ─── Tools definition ─────────────────────────────────────────────────────────
|
|
158
|
-
|
|
159
149
|
const TOOLS = [
|
|
160
|
-
{
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
type: 'object',
|
|
165
|
-
properties: {
|
|
166
|
-
min_score: { type: 'number', description: 'Minimum alpha score (0-100).' },
|
|
167
|
-
},
|
|
168
|
-
required: [],
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
name: 'get_divergence',
|
|
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
|
-
},
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
name: 'get_accumulation',
|
|
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
|
-
},
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: 'get_status',
|
|
196
|
-
description: 'Cek status koneksi CryptoIZ MCP, payment setup, dan tools yang tersedia.',
|
|
197
|
-
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
198
|
-
},
|
|
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: [] } },
|
|
199
154
|
];
|
|
200
155
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const server = new Server(
|
|
204
|
-
{ name: 'cryptoiz-mcp', version: '2.0.0' },
|
|
205
|
-
{ capabilities: { tools: {} } }
|
|
206
|
-
);
|
|
207
|
-
|
|
156
|
+
const server = new Server({ name: 'cryptoiz-mcp', version: '2.0.2' }, { capabilities: { tools: {} } });
|
|
208
157
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
209
158
|
|
|
210
159
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
211
160
|
const { name, arguments: args } = request.params;
|
|
212
|
-
|
|
213
161
|
try {
|
|
214
|
-
if (name === 'get_status') {
|
|
215
|
-
return { content: [{ type: 'text', text: formatStatus() }] };
|
|
216
|
-
}
|
|
162
|
+
if (name === 'get_status') return { content: [{ type: 'text', text: formatStatus() }] };
|
|
217
163
|
|
|
218
164
|
if (name === 'get_alpha_scanner') {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
if (args?.
|
|
222
|
-
data.signals = data.signals.filter(s => s.alpha_score >= args.min_score);
|
|
223
|
-
data.total = data.signals.length;
|
|
224
|
-
}
|
|
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; }
|
|
225
168
|
return { content: [{ type: 'text', text: formatAlpha(data) }] };
|
|
226
169
|
}
|
|
227
170
|
|
|
228
171
|
if (name === 'get_divergence') {
|
|
229
|
-
const
|
|
230
|
-
if (args?.timeframe)
|
|
231
|
-
if (args?.limit)
|
|
232
|
-
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}`);
|
|
233
176
|
return { content: [{ type: 'text', text: formatDivergence(data) }] };
|
|
234
177
|
}
|
|
235
178
|
|
|
236
179
|
if (name === 'get_accumulation') {
|
|
237
180
|
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
|
-
}
|
|
181
|
+
if (args?.min_composite && data.tokens) { data.tokens = data.tokens.filter(t => t.composite_score >= args.min_composite); data.total = data.tokens.length; }
|
|
242
182
|
return { content: [{ type: 'text', text: formatAccum(data) }] };
|
|
243
183
|
}
|
|
244
184
|
|
|
245
185
|
return { content: [{ type: 'text', text: `Tool tidak dikenal: ${name}` }], isError: true };
|
|
246
|
-
|
|
247
186
|
} catch (err) {
|
|
248
187
|
return { content: [{ type: 'text', text: `Error: ${err.message}` }], isError: true };
|
|
249
188
|
}
|