@aicryptorisk/mcp-server 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 ADDED
@@ -0,0 +1,133 @@
1
+ # AI Crypto Risk MCP Server
2
+
3
+ An MCP (Model Context Protocol) server that provides AI assistants with cryptocurrency risk analysis capabilities.
4
+
5
+ ## Features
6
+
7
+ - **Token Risk Analysis**: Analyze any crypto token for scam indicators
8
+ - **Deep Research**: Request comprehensive 4-pillar research reports
9
+ - **Multi-Chain Support**: Ethereum, BSC, Polygon, Arbitrum, Base, Solana
10
+ - **Usage Metering**: Built-in API key authentication and usage tracking
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ # From npm (when published)
16
+ npm install -g @aicryptorisk/mcp-server
17
+
18
+ # Or from source
19
+ cd mcp-server
20
+ npm install
21
+ ```
22
+
23
+ ## Configuration
24
+
25
+ ### For Claude Desktop
26
+
27
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on Mac or `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "aicryptorisk": {
33
+ "command": "node",
34
+ "args": ["/path/to/mcp-server/index.js"],
35
+ "env": {
36
+ "AICRYPTORISK_API_KEY": "your-api-key-here"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### For Claude Code
44
+
45
+ Add to your project's `.claude/settings.json`:
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "aicryptorisk": {
51
+ "command": "node",
52
+ "args": ["./mcp-server/index.js"],
53
+ "env": {
54
+ "AICRYPTORISK_API_KEY": "your-api-key-here"
55
+ }
56
+ }
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Available Tools
62
+
63
+ ### `analyze_crypto_risk`
64
+
65
+ Analyze a token for scam indicators and risk factors.
66
+
67
+ **Input:**
68
+ - `contract_address` (required): Token contract address
69
+ - `chain` (optional): Blockchain network (default: ethereum)
70
+ - `coin_name` (optional): Name for better context
71
+
72
+ **Returns:**
73
+ - Risk score (0-100)
74
+ - Risk level (Low/Medium/High/Critical)
75
+ - Red flags and warnings
76
+ - Recommendations
77
+
78
+ ### `request_deep_research`
79
+
80
+ Request comprehensive 4-pillar analysis (delivered via email).
81
+
82
+ **Input:**
83
+ - `contract_address` (required): Token contract address
84
+ - `chain` (required): Blockchain network
85
+ - `coin_name` (required): Token name
86
+ - `symbol` (optional): Token symbol
87
+ - `email` (required): Email for report delivery
88
+
89
+ ### `get_supported_chains`
90
+
91
+ Get list of supported blockchain networks.
92
+
93
+ ## Pricing Tiers
94
+
95
+ | Tier | Daily Limit | Price |
96
+ |------|-------------|-------|
97
+ | Free | 10 scans | $0 |
98
+ | Pro | 100 scans | $19/mo |
99
+ | Enterprise | Unlimited | Contact us |
100
+
101
+ Get your API key at: https://aicryptorisk.com/api-keys
102
+
103
+ ## Example Usage
104
+
105
+ Once configured, you can ask Claude:
106
+
107
+ > "Analyze this token for scam risk: 0x1234...abcd on ethereum"
108
+
109
+ > "Research this new memecoin PEPE2 at 0xabc... on BSC and send the report to my@email.com"
110
+
111
+ ## Development
112
+
113
+ ```bash
114
+ # Install dependencies
115
+ npm install
116
+
117
+ # Run locally (dev mode, no auth required)
118
+ npm start
119
+
120
+ # With auth (production)
121
+ SUPABASE_URL=your-url \
122
+ SUPABASE_SERVICE_ROLE_KEY=your-key \
123
+ AICRYPTORISK_API_KEY=test-key \
124
+ npm start
125
+ ```
126
+
127
+ ## Database Setup
128
+
129
+ Run `schema.sql` in your Supabase SQL editor to create the required tables for API key management and usage tracking.
130
+
131
+ ## License
132
+
133
+ MIT
package/index.js ADDED
@@ -0,0 +1,370 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ } from "@modelcontextprotocol/sdk/types.js";
9
+ import { createClient } from "@supabase/supabase-js";
10
+ import crypto from "crypto";
11
+
12
+ // Configuration from environment
13
+ const SUPABASE_URL = process.env.SUPABASE_URL;
14
+ const SUPABASE_SERVICE_ROLE_KEY = process.env.SUPABASE_SERVICE_ROLE_KEY;
15
+ const API_KEY = process.env.AICRYPTORISK_API_KEY;
16
+ const API_BASE_URL = process.env.API_BASE_URL || "https://aicryptorisk.com";
17
+
18
+ // Initialize Supabase client for auth and usage tracking
19
+ const supabase = SUPABASE_URL && SUPABASE_SERVICE_ROLE_KEY
20
+ ? createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY)
21
+ : null;
22
+
23
+ // Supported chains
24
+ const SUPPORTED_CHAINS = {
25
+ ethereum: "1",
26
+ bsc: "56",
27
+ polygon: "137",
28
+ arbitrum: "42161",
29
+ base: "8453",
30
+ solana: "solana",
31
+ };
32
+
33
+ /**
34
+ * Validate API key and check usage limits
35
+ */
36
+ async function validateApiKey(apiKey) {
37
+ if (!apiKey) {
38
+ return { valid: false, error: "API key required. Get one at https://aicryptorisk.com/api-keys" };
39
+ }
40
+
41
+ if (!supabase) {
42
+ // Local development mode - accept any key
43
+ console.error("[MCP] Warning: Supabase not configured, skipping auth");
44
+ return { valid: true, userId: "dev-mode", tier: "unlimited" };
45
+ }
46
+
47
+ try {
48
+ // Check API key in database
49
+ const { data: keyData, error } = await supabase
50
+ .from("api_keys")
51
+ .select("id, user_id, tier, calls_today, daily_limit, is_active")
52
+ .eq("key_hash", hashApiKey(apiKey))
53
+ .single();
54
+
55
+ if (error || !keyData) {
56
+ return { valid: false, error: "Invalid API key" };
57
+ }
58
+
59
+ if (!keyData.is_active) {
60
+ return { valid: false, error: "API key is disabled" };
61
+ }
62
+
63
+ if (keyData.daily_limit && keyData.calls_today >= keyData.daily_limit) {
64
+ return {
65
+ valid: false,
66
+ error: `Daily limit reached (${keyData.daily_limit} calls). Upgrade at https://aicryptorisk.com/pricing`
67
+ };
68
+ }
69
+
70
+ return {
71
+ valid: true,
72
+ userId: keyData.user_id,
73
+ tier: keyData.tier,
74
+ keyId: keyData.id
75
+ };
76
+ } catch (err) {
77
+ console.error("[MCP] Auth error:", err.message);
78
+ return { valid: false, error: "Authentication service unavailable" };
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Hash API key for secure storage lookup (SHA-256)
84
+ */
85
+ function hashApiKey(key) {
86
+ return crypto.createHash("sha256").update(key).digest("hex");
87
+ }
88
+
89
+ /**
90
+ * Track API usage for billing
91
+ */
92
+ async function trackUsage(keyId, tool, success) {
93
+ if (!supabase || !keyId) return;
94
+
95
+ try {
96
+ // Increment daily counter
97
+ await supabase.rpc("increment_api_calls", { key_id: keyId });
98
+
99
+ // Log detailed usage
100
+ await supabase.from("api_usage_log").insert({
101
+ api_key_id: keyId,
102
+ tool_name: tool,
103
+ success,
104
+ timestamp: new Date().toISOString(),
105
+ });
106
+ } catch (err) {
107
+ console.error("[MCP] Usage tracking error:", err.message);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Analyze a crypto token for risk indicators
113
+ */
114
+ async function analyzeToken(contractAddress, chain, coinName) {
115
+ const chainId = SUPPORTED_CHAINS[chain.toLowerCase()] || chain;
116
+
117
+ try {
118
+ // Call our MCP-specific API endpoint (uses API key auth)
119
+ const response = await fetch(`${API_BASE_URL}/api/scam-likely/mcp`, {
120
+ method: "POST",
121
+ headers: {
122
+ "Content-Type": "application/json",
123
+ "X-API-Key": API_KEY,
124
+ },
125
+ body: JSON.stringify({
126
+ contractAddress,
127
+ chainId,
128
+ coinName: coinName || undefined,
129
+ }),
130
+ });
131
+
132
+ if (!response.ok) {
133
+ const error = await response.text();
134
+ throw new Error(`API error: ${response.status} - ${error}`);
135
+ }
136
+
137
+ return await response.json();
138
+ } catch (err) {
139
+ throw new Error(`Failed to analyze token: ${err.message}`);
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Request deep research on a token (async, results sent via email)
145
+ */
146
+ async function requestDeepResearch(contractAddress, chain, coinName, symbol, email) {
147
+ try {
148
+ const response = await fetch(`${API_BASE_URL}/api/scam-likely/research`, {
149
+ method: "POST",
150
+ headers: {
151
+ "Content-Type": "application/json",
152
+ },
153
+ body: JSON.stringify({
154
+ contractAddress,
155
+ chain,
156
+ coinName,
157
+ symbol,
158
+ email,
159
+ }),
160
+ });
161
+
162
+ if (!response.ok) {
163
+ const error = await response.text();
164
+ throw new Error(`Research API error: ${response.status} - ${error}`);
165
+ }
166
+
167
+ return await response.json();
168
+ } catch (err) {
169
+ throw new Error(`Failed to request research: ${err.message}`);
170
+ }
171
+ }
172
+
173
+ // Create MCP server
174
+ const server = new Server(
175
+ {
176
+ name: "aicryptorisk",
177
+ version: "1.0.0",
178
+ },
179
+ {
180
+ capabilities: {
181
+ tools: {},
182
+ },
183
+ }
184
+ );
185
+
186
+ // List available tools
187
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
188
+ return {
189
+ tools: [
190
+ {
191
+ name: "analyze_crypto_risk",
192
+ description: `Analyze a cryptocurrency token for scam indicators and risk factors.
193
+
194
+ Returns a comprehensive risk analysis including:
195
+ - Overall risk score (0-100, higher = more risky)
196
+ - Risk level (Low/Medium/High/Critical)
197
+ - On-chain risk indicators (honeypot, ownership, liquidity)
198
+ - Specific red flags and warnings
199
+ - Actionable recommendations
200
+
201
+ Supports Ethereum, BSC, Polygon, Arbitrum, Base, and Solana.`,
202
+ inputSchema: {
203
+ type: "object",
204
+ properties: {
205
+ contract_address: {
206
+ type: "string",
207
+ description: "The token contract address to analyze",
208
+ },
209
+ chain: {
210
+ type: "string",
211
+ enum: ["ethereum", "bsc", "polygon", "arbitrum", "base", "solana"],
212
+ description: "The blockchain network (default: ethereum)",
213
+ },
214
+ coin_name: {
215
+ type: "string",
216
+ description: "Optional: Name of the coin for better context",
217
+ },
218
+ },
219
+ required: ["contract_address"],
220
+ },
221
+ },
222
+ {
223
+ name: "request_deep_research",
224
+ description: `Request comprehensive 4-pillar research on a cryptocurrency token.
225
+
226
+ This triggers an in-depth analysis covering:
227
+ 1. On-Chain Analysis - Contract security, liquidity, holder distribution
228
+ 2. Social Media Intelligence - Community sentiment, influencer activity
229
+ 3. Institutional Interest - VC backing, exchange listings, partnerships
230
+ 4. Off-Chain Intelligence - Team background, legal status, news
231
+
232
+ Results are delivered via email within 5-10 minutes.`,
233
+ inputSchema: {
234
+ type: "object",
235
+ properties: {
236
+ contract_address: {
237
+ type: "string",
238
+ description: "The token contract address to research",
239
+ },
240
+ chain: {
241
+ type: "string",
242
+ enum: ["ethereum", "bsc", "polygon", "arbitrum", "base", "solana"],
243
+ description: "The blockchain network",
244
+ },
245
+ coin_name: {
246
+ type: "string",
247
+ description: "Name of the coin",
248
+ },
249
+ symbol: {
250
+ type: "string",
251
+ description: "Token symbol (e.g., ETH, BTC)",
252
+ },
253
+ email: {
254
+ type: "string",
255
+ description: "Email address to receive the research report",
256
+ },
257
+ },
258
+ required: ["contract_address", "chain", "coin_name", "email"],
259
+ },
260
+ },
261
+ {
262
+ name: "get_supported_chains",
263
+ description: "Get list of supported blockchain networks for token analysis",
264
+ inputSchema: {
265
+ type: "object",
266
+ properties: {},
267
+ },
268
+ },
269
+ ],
270
+ };
271
+ });
272
+
273
+ // Handle tool calls
274
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
275
+ const { name, arguments: args } = request.params;
276
+
277
+ // Validate API key
278
+ const auth = await validateApiKey(API_KEY);
279
+ if (!auth.valid) {
280
+ return {
281
+ content: [
282
+ {
283
+ type: "text",
284
+ text: JSON.stringify({ error: auth.error }, null, 2),
285
+ },
286
+ ],
287
+ isError: true,
288
+ };
289
+ }
290
+
291
+ try {
292
+ let result;
293
+
294
+ switch (name) {
295
+ case "analyze_crypto_risk": {
296
+ const { contract_address, chain = "ethereum", coin_name } = args;
297
+
298
+ if (!contract_address) {
299
+ throw new Error("contract_address is required");
300
+ }
301
+
302
+ result = await analyzeToken(contract_address, chain, coin_name);
303
+ await trackUsage(auth.keyId, "analyze_crypto_risk", true);
304
+ break;
305
+ }
306
+
307
+ case "request_deep_research": {
308
+ const { contract_address, chain, coin_name, symbol, email } = args;
309
+
310
+ if (!contract_address || !chain || !coin_name || !email) {
311
+ throw new Error("contract_address, chain, coin_name, and email are required");
312
+ }
313
+
314
+ result = await requestDeepResearch(contract_address, chain, coin_name, symbol, email);
315
+ await trackUsage(auth.keyId, "request_deep_research", true);
316
+ break;
317
+ }
318
+
319
+ case "get_supported_chains": {
320
+ result = {
321
+ chains: Object.keys(SUPPORTED_CHAINS),
322
+ details: {
323
+ ethereum: { chainId: "1", name: "Ethereum Mainnet" },
324
+ bsc: { chainId: "56", name: "BNB Smart Chain" },
325
+ polygon: { chainId: "137", name: "Polygon" },
326
+ arbitrum: { chainId: "42161", name: "Arbitrum One" },
327
+ base: { chainId: "8453", name: "Base" },
328
+ solana: { chainId: "solana", name: "Solana" },
329
+ },
330
+ };
331
+ break;
332
+ }
333
+
334
+ default:
335
+ throw new Error(`Unknown tool: ${name}`);
336
+ }
337
+
338
+ return {
339
+ content: [
340
+ {
341
+ type: "text",
342
+ text: JSON.stringify(result, null, 2),
343
+ },
344
+ ],
345
+ };
346
+ } catch (error) {
347
+ await trackUsage(auth.keyId, name, false);
348
+ return {
349
+ content: [
350
+ {
351
+ type: "text",
352
+ text: JSON.stringify({ error: error.message }, null, 2),
353
+ },
354
+ ],
355
+ isError: true,
356
+ };
357
+ }
358
+ });
359
+
360
+ // Start the server
361
+ async function main() {
362
+ const transport = new StdioServerTransport();
363
+ await server.connect(transport);
364
+ console.error("[MCP] AI Crypto Risk server running");
365
+ }
366
+
367
+ main().catch((error) => {
368
+ console.error("[MCP] Fatal error:", error);
369
+ process.exit(1);
370
+ });
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@aicryptorisk/mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP Server for AI Crypto Risk Analysis - Analyze tokens for scam indicators",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "aicryptorisk-mcp": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node index.js",
12
+ "setup": "node setup.js"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.0.0",
16
+ "@supabase/supabase-js": "^2.58.0"
17
+ },
18
+ "keywords": [
19
+ "mcp",
20
+ "crypto",
21
+ "risk",
22
+ "scam",
23
+ "analysis",
24
+ "ai",
25
+ "claude"
26
+ ],
27
+ "author": "AICryptoRisk",
28
+ "license": "MIT",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/djangamane/montecrypto"
32
+ },
33
+ "homepage": "https://aicryptorisk.com"
34
+ }
package/schema.sql ADDED
@@ -0,0 +1,89 @@
1
+ -- API Keys table for MCP server authentication
2
+ -- Run this in your Supabase SQL editor
3
+
4
+ -- API Keys table
5
+ CREATE TABLE IF NOT EXISTS api_keys (
6
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
7
+ user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
8
+ key_hash TEXT NOT NULL UNIQUE,
9
+ key_prefix TEXT NOT NULL, -- First 8 chars for display (e.g., "acr_1a2b...")
10
+ name TEXT NOT NULL DEFAULT 'Default',
11
+ tier TEXT NOT NULL DEFAULT 'free', -- free, pro, enterprise
12
+ daily_limit INTEGER DEFAULT 10, -- null = unlimited
13
+ calls_today INTEGER DEFAULT 0,
14
+ calls_total INTEGER DEFAULT 0,
15
+ is_active BOOLEAN DEFAULT true,
16
+ created_at TIMESTAMPTZ DEFAULT NOW(),
17
+ last_used_at TIMESTAMPTZ,
18
+ last_reset_at DATE DEFAULT CURRENT_DATE
19
+ );
20
+
21
+ -- Usage log for billing and analytics
22
+ CREATE TABLE IF NOT EXISTS api_usage_log (
23
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
24
+ api_key_id UUID REFERENCES api_keys(id) ON DELETE SET NULL,
25
+ tool_name TEXT NOT NULL,
26
+ success BOOLEAN NOT NULL,
27
+ timestamp TIMESTAMPTZ DEFAULT NOW(),
28
+ metadata JSONB DEFAULT '{}'
29
+ );
30
+
31
+ -- Index for fast lookups
32
+ CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash);
33
+ CREATE INDEX IF NOT EXISTS idx_api_keys_user ON api_keys(user_id);
34
+ CREATE INDEX IF NOT EXISTS idx_usage_log_key ON api_usage_log(api_key_id);
35
+ CREATE INDEX IF NOT EXISTS idx_usage_log_timestamp ON api_usage_log(timestamp);
36
+
37
+ -- Function to increment API calls (atomic)
38
+ CREATE OR REPLACE FUNCTION increment_api_calls(key_id UUID)
39
+ RETURNS void AS $$
40
+ BEGIN
41
+ UPDATE api_keys
42
+ SET
43
+ calls_today = CASE
44
+ WHEN last_reset_at < CURRENT_DATE THEN 1
45
+ ELSE calls_today + 1
46
+ END,
47
+ calls_total = calls_total + 1,
48
+ last_used_at = NOW(),
49
+ last_reset_at = CURRENT_DATE
50
+ WHERE id = key_id;
51
+ END;
52
+ $$ LANGUAGE plpgsql;
53
+
54
+ -- RLS Policies
55
+ ALTER TABLE api_keys ENABLE ROW LEVEL SECURITY;
56
+ ALTER TABLE api_usage_log ENABLE ROW LEVEL SECURITY;
57
+
58
+ -- Users can only see their own API keys
59
+ CREATE POLICY "Users can view own api_keys" ON api_keys
60
+ FOR SELECT USING (auth.uid() = user_id);
61
+
62
+ CREATE POLICY "Users can insert own api_keys" ON api_keys
63
+ FOR INSERT WITH CHECK (auth.uid() = user_id);
64
+
65
+ CREATE POLICY "Users can update own api_keys" ON api_keys
66
+ FOR UPDATE USING (auth.uid() = user_id);
67
+
68
+ CREATE POLICY "Users can delete own api_keys" ON api_keys
69
+ FOR DELETE USING (auth.uid() = user_id);
70
+
71
+ -- Users can view their own usage
72
+ CREATE POLICY "Users can view own usage" ON api_usage_log
73
+ FOR SELECT USING (
74
+ api_key_id IN (SELECT id FROM api_keys WHERE user_id = auth.uid())
75
+ );
76
+
77
+ -- Service role can do everything (for the MCP server)
78
+ CREATE POLICY "Service role full access api_keys" ON api_keys
79
+ FOR ALL USING (auth.role() = 'service_role');
80
+
81
+ CREATE POLICY "Service role full access usage" ON api_usage_log
82
+ FOR ALL USING (auth.role() = 'service_role');
83
+
84
+ -- Tier configurations (for reference)
85
+ COMMENT ON TABLE api_keys IS 'Tier limits:
86
+ - free: 10 calls/day
87
+ - pro: 100 calls/day
88
+ - enterprise: unlimited (null daily_limit)
89
+ ';
package/setup.js ADDED
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server Setup Helper
5
+ * Helps users configure Claude Desktop to use the AI Crypto Risk MCP server
6
+ */
7
+
8
+ import fs from "fs";
9
+ import path from "path";
10
+ import { fileURLToPath } from "url";
11
+ import readline from "readline";
12
+
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+
15
+ const rl = readline.createInterface({
16
+ input: process.stdin,
17
+ output: process.stdout,
18
+ });
19
+
20
+ function prompt(question) {
21
+ return new Promise((resolve) => {
22
+ rl.question(question, resolve);
23
+ });
24
+ }
25
+
26
+ // Detect platform and config path
27
+ function getClaudeConfigPath() {
28
+ const platform = process.platform;
29
+ const home = process.env.HOME || process.env.USERPROFILE;
30
+
31
+ if (platform === "darwin") {
32
+ return path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
33
+ } else if (platform === "win32") {
34
+ return path.join(process.env.APPDATA, "Claude", "claude_desktop_config.json");
35
+ } else {
36
+ return path.join(home, ".config", "claude", "claude_desktop_config.json");
37
+ }
38
+ }
39
+
40
+ async function main() {
41
+ console.log("\nšŸ” AI Crypto Risk MCP Server Setup\n");
42
+ console.log("This will configure Claude Desktop to use the crypto risk analysis tools.\n");
43
+
44
+ // Get API key
45
+ const apiKey = await prompt("Enter your API key (get one at https://aicryptorisk.com/api-keys): ");
46
+
47
+ if (!apiKey || !apiKey.startsWith("acr_")) {
48
+ console.log("\nāŒ Invalid API key. Keys should start with 'acr_'");
49
+ rl.close();
50
+ process.exit(1);
51
+ }
52
+
53
+ const configPath = getClaudeConfigPath();
54
+ const serverPath = path.resolve(__dirname, "index.js");
55
+
56
+ // Read existing config or create new
57
+ let config = { mcpServers: {} };
58
+ try {
59
+ if (fs.existsSync(configPath)) {
60
+ config = JSON.parse(fs.readFileSync(configPath, "utf8"));
61
+ config.mcpServers = config.mcpServers || {};
62
+ }
63
+ } catch (err) {
64
+ console.log("Creating new Claude config...");
65
+ }
66
+
67
+ // Add our server
68
+ config.mcpServers.aicryptorisk = {
69
+ command: "node",
70
+ args: [serverPath],
71
+ env: {
72
+ AICRYPTORISK_API_KEY: apiKey,
73
+ },
74
+ };
75
+
76
+ // Ensure directory exists
77
+ const configDir = path.dirname(configPath);
78
+ if (!fs.existsSync(configDir)) {
79
+ fs.mkdirSync(configDir, { recursive: true });
80
+ }
81
+
82
+ // Write config
83
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
84
+
85
+ console.log(`\nāœ… Configuration saved to: ${configPath}`);
86
+ console.log("\nšŸ“‹ Next steps:");
87
+ console.log(" 1. Restart Claude Desktop");
88
+ console.log(" 2. You can now ask Claude to analyze crypto tokens!");
89
+ console.log("\nšŸ’” Try: \"Analyze this token for scam risk: 0x...\"");
90
+
91
+ rl.close();
92
+ }
93
+
94
+ main().catch((err) => {
95
+ console.error("Setup failed:", err.message);
96
+ rl.close();
97
+ process.exit(1);
98
+ });