@dritan/mcp 0.1.1 → 0.3.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/.env.example +1 -0
- package/README.md +25 -0
- package/dist/index.js +655 -9
- package/package.json +14 -4
package/.env.example
CHANGED
package/README.md
CHANGED
|
@@ -41,8 +41,30 @@ npm run build && npm start
|
|
|
41
41
|
- `wallet_create_local`
|
|
42
42
|
- `wallet_get_address`
|
|
43
43
|
- `wallet_get_balance`
|
|
44
|
+
- `dritan_health`
|
|
44
45
|
- `market_get_snapshot`
|
|
46
|
+
- `token_search`
|
|
47
|
+
- `token_get_price`
|
|
48
|
+
- `token_get_metadata`
|
|
49
|
+
- `token_get_risk`
|
|
50
|
+
- `token_get_first_buyers`
|
|
51
|
+
- `token_get_aggregated`
|
|
52
|
+
- `token_get_deployer_stats`
|
|
53
|
+
- `token_get_ohlcv`
|
|
54
|
+
- `token_get_ohlcv_chart`
|
|
55
|
+
- `wallet_get_performance`
|
|
56
|
+
- `wallet_get_token_performance`
|
|
57
|
+
- `wallet_get_portfolio_chart`
|
|
58
|
+
- `wallet_get_summary`
|
|
59
|
+
- `wallet_get_trade_history`
|
|
60
|
+
- `wallet_get_holdings`
|
|
61
|
+
- `wallet_get_holdings_page`
|
|
45
62
|
- `market_stream_sample`
|
|
63
|
+
- `wallet_stream_sample`
|
|
64
|
+
- `ths_health`
|
|
65
|
+
- `ths_get_score`
|
|
66
|
+
- `ths_get_score_tokens_get`
|
|
67
|
+
- `ths_get_score_tokens_post`
|
|
46
68
|
- `swap_build`
|
|
47
69
|
- `swap_sign_and_broadcast`
|
|
48
70
|
- `swap_build_sign_and_broadcast`
|
|
@@ -52,4 +74,7 @@ npm run build && npm start
|
|
|
52
74
|
- Wallets default to `~/.config/dritan-mcp/wallets`.
|
|
53
75
|
- Private keys never leave local files; only public address/signature are returned.
|
|
54
76
|
- `swap_sign_and_broadcast` signs locally, then broadcasts via Dritan.
|
|
77
|
+
- `token_get_ohlcv_chart` returns a shareable chart URL plus a ready-to-send markdown image snippet.
|
|
78
|
+
- Ticker workflow for chart requests: `token_search` -> extract mint -> `token_get_ohlcv` or `token_get_ohlcv_chart`.
|
|
79
|
+
- If users ask for `$WIF` style symbols, always resolve mint with `token_search` first.
|
|
55
80
|
- If Solana CLI is missing, run `system_check_prereqs` and follow returned install steps.
|
package/dist/index.js
CHANGED
|
@@ -6,17 +6,36 @@ import { spawnSync } from "node:child_process";
|
|
|
6
6
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
8
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
-
import { DritanClient } from "dritan-sdk";
|
|
9
|
+
import { DritanClient, MeteoraThsClient, } from "dritan-sdk";
|
|
10
10
|
import { Connection, Keypair, VersionedTransaction, clusterApiUrl } from "@solana/web3.js";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
const DEFAULT_WALLET_DIR = join(homedir(), ".config", "dritan-mcp", "wallets");
|
|
13
|
+
const STREAM_DEXES = [
|
|
14
|
+
"pumpamm",
|
|
15
|
+
"pumpfun",
|
|
16
|
+
"launchlab",
|
|
17
|
+
"dlmm",
|
|
18
|
+
"damm2",
|
|
19
|
+
"damm1",
|
|
20
|
+
"dbc",
|
|
21
|
+
"amm",
|
|
22
|
+
"cpmm",
|
|
23
|
+
"clmm",
|
|
24
|
+
"orca",
|
|
25
|
+
];
|
|
13
26
|
const server = new Server({
|
|
14
27
|
name: "dritan-mcp",
|
|
15
|
-
version: "0.1.
|
|
28
|
+
version: "0.1.2",
|
|
16
29
|
}, {
|
|
17
30
|
capabilities: {
|
|
18
31
|
tools: {},
|
|
19
32
|
},
|
|
33
|
+
instructions: [
|
|
34
|
+
"This server requires a DRITAN_API_KEY environment variable to use market data and swap tools.",
|
|
35
|
+
"Get your API key at https://dritan.dev and configure it:",
|
|
36
|
+
" claude mcp add dritan-mcp -e DRITAN_API_KEY=<your-key> -- npx @dritan/mcp@latest",
|
|
37
|
+
"Without the key, only system_check_prereqs and wallet tools (create_local, get_address, get_balance) will work.",
|
|
38
|
+
].join("\n"),
|
|
20
39
|
});
|
|
21
40
|
function getDritanClient() {
|
|
22
41
|
const apiKey = process.env.DRITAN_API_KEY;
|
|
@@ -29,6 +48,122 @@ function getDritanClient() {
|
|
|
29
48
|
wsBaseUrl: process.env.DRITAN_WS_BASE_URL,
|
|
30
49
|
});
|
|
31
50
|
}
|
|
51
|
+
function getThsClient() {
|
|
52
|
+
return new MeteoraThsClient({
|
|
53
|
+
baseUrl: process.env.METEORA_THS_BASE_URL,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async function searchTokens(client, query, options) {
|
|
57
|
+
const sdkSearch = client.searchTokens;
|
|
58
|
+
if (typeof sdkSearch === "function") {
|
|
59
|
+
return await sdkSearch(query, options);
|
|
60
|
+
}
|
|
61
|
+
// Backward-compatible fallback for environments where dritan-sdk hasn't been upgraded yet.
|
|
62
|
+
const apiKey = process.env.DRITAN_API_KEY;
|
|
63
|
+
if (!apiKey) {
|
|
64
|
+
throw new Error("Missing DRITAN_API_KEY in environment");
|
|
65
|
+
}
|
|
66
|
+
const baseUrl = process.env.DRITAN_BASE_URL ?? "https://us-east.dritan.dev";
|
|
67
|
+
const url = new URL("/token/search", baseUrl);
|
|
68
|
+
url.searchParams.set("query", query);
|
|
69
|
+
if (options?.limit !== undefined)
|
|
70
|
+
url.searchParams.set("limit", String(options.limit));
|
|
71
|
+
if (options?.cursor) {
|
|
72
|
+
url.searchParams.set("cursor", options.cursor);
|
|
73
|
+
}
|
|
74
|
+
else if (options?.page !== undefined) {
|
|
75
|
+
url.searchParams.set("page", String(options.page));
|
|
76
|
+
}
|
|
77
|
+
const response = await fetch(url.toString(), {
|
|
78
|
+
method: "GET",
|
|
79
|
+
headers: { "x-api-key": apiKey },
|
|
80
|
+
});
|
|
81
|
+
const text = await response.text();
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`Token search failed (${response.status}): ${text}`);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(text);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { ok: true, raw: text };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function checkDritanHealth() {
|
|
93
|
+
const baseUrl = process.env.DRITAN_BASE_URL ?? "https://us-east.dritan.dev";
|
|
94
|
+
const url = new URL("/health", baseUrl).toString();
|
|
95
|
+
const apiKey = process.env.DRITAN_API_KEY;
|
|
96
|
+
const headers = {};
|
|
97
|
+
if (apiKey)
|
|
98
|
+
headers["x-api-key"] = apiKey;
|
|
99
|
+
const response = await fetch(url, {
|
|
100
|
+
method: "GET",
|
|
101
|
+
headers,
|
|
102
|
+
});
|
|
103
|
+
const body = await response.text().catch(() => null);
|
|
104
|
+
return {
|
|
105
|
+
ok: response.ok,
|
|
106
|
+
status: response.status,
|
|
107
|
+
url,
|
|
108
|
+
body,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function toEpochMs(ts) {
|
|
112
|
+
return ts > 1_000_000_000_000 ? ts : ts * 1000;
|
|
113
|
+
}
|
|
114
|
+
function formatChartLabel(ts) {
|
|
115
|
+
const date = new Date(toEpochMs(ts));
|
|
116
|
+
if (Number.isNaN(date.getTime()))
|
|
117
|
+
return String(ts);
|
|
118
|
+
return date.toISOString().replace("T", " ").slice(0, 16);
|
|
119
|
+
}
|
|
120
|
+
function buildOhlcvChartUrl(mint, timeframe, bars, width, height) {
|
|
121
|
+
const labels = bars.map((bar) => formatChartLabel(bar.time));
|
|
122
|
+
const closeSeries = bars.map((bar) => Number(bar.close.toFixed(12)));
|
|
123
|
+
const volumeSeries = bars.map((bar) => Number(bar.volume.toFixed(12)));
|
|
124
|
+
const config = {
|
|
125
|
+
type: "bar",
|
|
126
|
+
data: {
|
|
127
|
+
labels,
|
|
128
|
+
datasets: [
|
|
129
|
+
{
|
|
130
|
+
type: "line",
|
|
131
|
+
label: "Close",
|
|
132
|
+
data: closeSeries,
|
|
133
|
+
borderColor: "#2563eb",
|
|
134
|
+
backgroundColor: "rgba(37,99,235,0.2)",
|
|
135
|
+
pointRadius: 0,
|
|
136
|
+
borderWidth: 2,
|
|
137
|
+
tension: 0.2,
|
|
138
|
+
yAxisID: "price",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
type: "bar",
|
|
142
|
+
label: "Volume",
|
|
143
|
+
data: volumeSeries,
|
|
144
|
+
backgroundColor: "rgba(16,185,129,0.25)",
|
|
145
|
+
borderWidth: 0,
|
|
146
|
+
yAxisID: "volume",
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
options: {
|
|
151
|
+
plugins: {
|
|
152
|
+
legend: { display: true },
|
|
153
|
+
title: {
|
|
154
|
+
display: true,
|
|
155
|
+
text: `${mint} ${timeframe.toUpperCase()} OHLCV`,
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
scales: {
|
|
159
|
+
price: { type: "linear", position: "left" },
|
|
160
|
+
volume: { type: "linear", position: "right", grid: { drawOnChartArea: false } },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
const encoded = encodeURIComponent(JSON.stringify(config));
|
|
165
|
+
return `https://quickchart.io/chart?w=${width}&h=${height}&f=png&c=${encoded}`;
|
|
166
|
+
}
|
|
32
167
|
function getPlatformInstallHint(binary) {
|
|
33
168
|
switch (process.platform) {
|
|
34
169
|
case "darwin":
|
|
@@ -143,11 +278,64 @@ const marketSnapshotSchema = z.object({
|
|
|
143
278
|
mint: z.string().min(1),
|
|
144
279
|
mode: z.enum(["price", "metadata", "risk", "first-buyers", "aggregated"]).default("aggregated"),
|
|
145
280
|
});
|
|
281
|
+
const tokenMintSchema = z.object({
|
|
282
|
+
mint: z.string().min(1),
|
|
283
|
+
});
|
|
284
|
+
const tokenSearchSchema = z.object({
|
|
285
|
+
query: z.string().min(1),
|
|
286
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
287
|
+
cursor: z.string().min(1).optional(),
|
|
288
|
+
page: z.number().int().min(1).optional(),
|
|
289
|
+
});
|
|
146
290
|
const marketStreamSampleSchema = z.object({
|
|
147
|
-
dex: z.enum(
|
|
291
|
+
dex: z.enum(STREAM_DEXES),
|
|
148
292
|
durationMs: z.number().int().min(500).max(60_000).default(10_000),
|
|
149
293
|
maxEvents: z.number().int().min(1).max(250).default(20),
|
|
150
294
|
});
|
|
295
|
+
const tokenOhlcvSchema = z.object({
|
|
296
|
+
mint: z.string().min(1),
|
|
297
|
+
timeframe: z.string().min(1),
|
|
298
|
+
timeTo: z.number().int().positive().optional(),
|
|
299
|
+
});
|
|
300
|
+
const tokenOhlcvChartSchema = tokenOhlcvSchema.extend({
|
|
301
|
+
includeActive: z.boolean().default(true),
|
|
302
|
+
maxPoints: z.number().int().min(10).max(500).default(120),
|
|
303
|
+
width: z.number().int().min(300).max(2000).default(1200),
|
|
304
|
+
height: z.number().int().min(200).max(1200).default(600),
|
|
305
|
+
});
|
|
306
|
+
const walletAddressSchema = z.object({
|
|
307
|
+
wallet: z.string().min(1),
|
|
308
|
+
});
|
|
309
|
+
const walletPerformanceSchema = walletAddressSchema.extend({
|
|
310
|
+
showHistoricPnL: z.boolean().optional(),
|
|
311
|
+
holdingCheck: z.boolean().optional(),
|
|
312
|
+
hideDetails: z.boolean().optional(),
|
|
313
|
+
});
|
|
314
|
+
const walletTokenPerformanceSchema = walletAddressSchema.extend({
|
|
315
|
+
tokenMint: z.string().min(1),
|
|
316
|
+
});
|
|
317
|
+
const walletPortfolioChartSchema = walletAddressSchema.extend({
|
|
318
|
+
days: z.number().int().min(1).max(3650).optional(),
|
|
319
|
+
});
|
|
320
|
+
const walletTradeHistorySchema = walletAddressSchema.extend({
|
|
321
|
+
cursor: z.string().min(1).optional(),
|
|
322
|
+
});
|
|
323
|
+
const walletHoldingsPageSchema = walletAddressSchema.extend({
|
|
324
|
+
page: z.number().int().min(1),
|
|
325
|
+
});
|
|
326
|
+
const walletStreamSampleSchema = z.object({
|
|
327
|
+
wallets: z.array(z.string().min(1)).min(1).max(100),
|
|
328
|
+
durationMs: z.number().int().min(500).max(60_000).default(10_000),
|
|
329
|
+
maxEvents: z.number().int().min(1).max(250).default(20),
|
|
330
|
+
});
|
|
331
|
+
const thsScoreSchema = z.object({
|
|
332
|
+
wallet: z.string().min(1),
|
|
333
|
+
debug: z.boolean().optional(),
|
|
334
|
+
breakdown: z.boolean().optional(),
|
|
335
|
+
});
|
|
336
|
+
const thsScoreTokensSchema = thsScoreSchema.extend({
|
|
337
|
+
tokenMints: z.array(z.string().min(1)).min(1).max(200),
|
|
338
|
+
});
|
|
151
339
|
const swapBuildSchema = z.object({
|
|
152
340
|
walletPath: z.string().min(1).optional(),
|
|
153
341
|
userPublicKey: z.string().min(1).optional(),
|
|
@@ -207,6 +395,14 @@ const tools = [
|
|
|
207
395
|
},
|
|
208
396
|
},
|
|
209
397
|
},
|
|
398
|
+
{
|
|
399
|
+
name: "dritan_health",
|
|
400
|
+
description: "Check data plane health endpoint via Dritan SDK.",
|
|
401
|
+
inputSchema: {
|
|
402
|
+
type: "object",
|
|
403
|
+
properties: {},
|
|
404
|
+
},
|
|
405
|
+
},
|
|
210
406
|
{
|
|
211
407
|
name: "market_get_snapshot",
|
|
212
408
|
description: "Fetch token market snapshot via Dritan SDK (price/metadata/risk/first-buyers/aggregated).",
|
|
@@ -222,6 +418,200 @@ const tools = [
|
|
|
222
418
|
},
|
|
223
419
|
},
|
|
224
420
|
},
|
|
421
|
+
{
|
|
422
|
+
name: "token_search",
|
|
423
|
+
description: "Search tokens by ticker/name and resolve mint addresses (first step for ticker-based chart requests).",
|
|
424
|
+
inputSchema: {
|
|
425
|
+
type: "object",
|
|
426
|
+
required: ["query"],
|
|
427
|
+
properties: {
|
|
428
|
+
query: { type: "string", description: "Ticker or token name, e.g. WIF or $WIF" },
|
|
429
|
+
limit: { type: "number", description: "Optional result limit (1-50)" },
|
|
430
|
+
cursor: { type: "string", description: "Optional cursor for pagination" },
|
|
431
|
+
page: { type: "number", description: "Optional page (used when cursor is absent)" },
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: "token_get_price",
|
|
437
|
+
description: "Fetch token price via Dritan (same as market_get_snapshot mode=price).",
|
|
438
|
+
inputSchema: {
|
|
439
|
+
type: "object",
|
|
440
|
+
required: ["mint"],
|
|
441
|
+
properties: {
|
|
442
|
+
mint: { type: "string" },
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: "token_get_metadata",
|
|
448
|
+
description: "Fetch token metadata via Dritan.",
|
|
449
|
+
inputSchema: {
|
|
450
|
+
type: "object",
|
|
451
|
+
required: ["mint"],
|
|
452
|
+
properties: {
|
|
453
|
+
mint: { type: "string" },
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: "token_get_risk",
|
|
459
|
+
description: "Fetch token risk via Dritan (same as market_get_snapshot mode=risk).",
|
|
460
|
+
inputSchema: {
|
|
461
|
+
type: "object",
|
|
462
|
+
required: ["mint"],
|
|
463
|
+
properties: {
|
|
464
|
+
mint: { type: "string" },
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
name: "token_get_first_buyers",
|
|
470
|
+
description: "Fetch first buyers via Dritan.",
|
|
471
|
+
inputSchema: {
|
|
472
|
+
type: "object",
|
|
473
|
+
required: ["mint"],
|
|
474
|
+
properties: {
|
|
475
|
+
mint: { type: "string" },
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: "token_get_aggregated",
|
|
481
|
+
description: "Fetch aggregated token data via Dritan (same as market_get_snapshot mode=aggregated).",
|
|
482
|
+
inputSchema: {
|
|
483
|
+
type: "object",
|
|
484
|
+
required: ["mint"],
|
|
485
|
+
properties: {
|
|
486
|
+
mint: { type: "string" },
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
name: "token_get_deployer_stats",
|
|
492
|
+
description: "Fetch token deployer stats via Dritan.",
|
|
493
|
+
inputSchema: {
|
|
494
|
+
type: "object",
|
|
495
|
+
required: ["mint"],
|
|
496
|
+
properties: {
|
|
497
|
+
mint: { type: "string" },
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: "token_get_ohlcv",
|
|
503
|
+
description: "Fetch OHLCV candles for a token and timeframe.",
|
|
504
|
+
inputSchema: {
|
|
505
|
+
type: "object",
|
|
506
|
+
required: ["mint", "timeframe"],
|
|
507
|
+
properties: {
|
|
508
|
+
mint: { type: "string" },
|
|
509
|
+
timeframe: { type: "string", description: "e.g. 1m, 5m, 1h, 1d" },
|
|
510
|
+
timeTo: { type: "number" },
|
|
511
|
+
},
|
|
512
|
+
},
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: "token_get_ohlcv_chart",
|
|
516
|
+
description: "Build a shareable chart URL from token OHLCV candles so agents can send an actual chart in chat (resolve ticker with token_search first).",
|
|
517
|
+
inputSchema: {
|
|
518
|
+
type: "object",
|
|
519
|
+
required: ["mint", "timeframe"],
|
|
520
|
+
properties: {
|
|
521
|
+
mint: { type: "string" },
|
|
522
|
+
timeframe: { type: "string", description: "e.g. 1m, 5m, 1h, 1d" },
|
|
523
|
+
timeTo: { type: "number" },
|
|
524
|
+
includeActive: { type: "boolean" },
|
|
525
|
+
maxPoints: { type: "number" },
|
|
526
|
+
width: { type: "number" },
|
|
527
|
+
height: { type: "number" },
|
|
528
|
+
},
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
name: "wallet_get_performance",
|
|
533
|
+
description: "Fetch wallet performance via Dritan.",
|
|
534
|
+
inputSchema: {
|
|
535
|
+
type: "object",
|
|
536
|
+
required: ["wallet"],
|
|
537
|
+
properties: {
|
|
538
|
+
wallet: { type: "string" },
|
|
539
|
+
showHistoricPnL: { type: "boolean" },
|
|
540
|
+
holdingCheck: { type: "boolean" },
|
|
541
|
+
hideDetails: { type: "boolean" },
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
name: "wallet_get_token_performance",
|
|
547
|
+
description: "Fetch wallet performance for a specific token mint via Dritan.",
|
|
548
|
+
inputSchema: {
|
|
549
|
+
type: "object",
|
|
550
|
+
required: ["wallet", "tokenMint"],
|
|
551
|
+
properties: {
|
|
552
|
+
wallet: { type: "string" },
|
|
553
|
+
tokenMint: { type: "string" },
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
name: "wallet_get_portfolio_chart",
|
|
559
|
+
description: "Fetch wallet portfolio chart series via Dritan.",
|
|
560
|
+
inputSchema: {
|
|
561
|
+
type: "object",
|
|
562
|
+
required: ["wallet"],
|
|
563
|
+
properties: {
|
|
564
|
+
wallet: { type: "string" },
|
|
565
|
+
days: { type: "number" },
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: "wallet_get_summary",
|
|
571
|
+
description: "Fetch basic wallet summary via Dritan.",
|
|
572
|
+
inputSchema: {
|
|
573
|
+
type: "object",
|
|
574
|
+
required: ["wallet"],
|
|
575
|
+
properties: {
|
|
576
|
+
wallet: { type: "string" },
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
name: "wallet_get_trade_history",
|
|
582
|
+
description: "Fetch wallet trade history via Dritan.",
|
|
583
|
+
inputSchema: {
|
|
584
|
+
type: "object",
|
|
585
|
+
required: ["wallet"],
|
|
586
|
+
properties: {
|
|
587
|
+
wallet: { type: "string" },
|
|
588
|
+
cursor: { type: "string" },
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
name: "wallet_get_holdings",
|
|
594
|
+
description: "Fetch wallet holdings via Dritan.",
|
|
595
|
+
inputSchema: {
|
|
596
|
+
type: "object",
|
|
597
|
+
required: ["wallet"],
|
|
598
|
+
properties: {
|
|
599
|
+
wallet: { type: "string" },
|
|
600
|
+
},
|
|
601
|
+
},
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
name: "wallet_get_holdings_page",
|
|
605
|
+
description: "Fetch paginated wallet holdings via Dritan.",
|
|
606
|
+
inputSchema: {
|
|
607
|
+
type: "object",
|
|
608
|
+
required: ["wallet", "page"],
|
|
609
|
+
properties: {
|
|
610
|
+
wallet: { type: "string" },
|
|
611
|
+
page: { type: "number" },
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
},
|
|
225
615
|
{
|
|
226
616
|
name: "market_stream_sample",
|
|
227
617
|
description: "Open a DEX websocket stream and collect events for a short duration.",
|
|
@@ -231,13 +621,75 @@ const tools = [
|
|
|
231
621
|
properties: {
|
|
232
622
|
dex: {
|
|
233
623
|
type: "string",
|
|
234
|
-
enum:
|
|
624
|
+
enum: STREAM_DEXES,
|
|
235
625
|
},
|
|
236
626
|
durationMs: { type: "number" },
|
|
237
627
|
maxEvents: { type: "number" },
|
|
238
628
|
},
|
|
239
629
|
},
|
|
240
630
|
},
|
|
631
|
+
{
|
|
632
|
+
name: "wallet_stream_sample",
|
|
633
|
+
description: "Open the wallet websocket stream and collect events for selected wallets.",
|
|
634
|
+
inputSchema: {
|
|
635
|
+
type: "object",
|
|
636
|
+
required: ["wallets"],
|
|
637
|
+
properties: {
|
|
638
|
+
wallets: { type: "array", items: { type: "string" } },
|
|
639
|
+
durationMs: { type: "number" },
|
|
640
|
+
maxEvents: { type: "number" },
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
name: "ths_health",
|
|
646
|
+
description: "Check Meteora THS service health.",
|
|
647
|
+
inputSchema: {
|
|
648
|
+
type: "object",
|
|
649
|
+
properties: {},
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
name: "ths_get_score",
|
|
654
|
+
description: "Fetch THS score for a wallet.",
|
|
655
|
+
inputSchema: {
|
|
656
|
+
type: "object",
|
|
657
|
+
required: ["wallet"],
|
|
658
|
+
properties: {
|
|
659
|
+
wallet: { type: "string" },
|
|
660
|
+
debug: { type: "boolean" },
|
|
661
|
+
breakdown: { type: "boolean" },
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
name: "ths_get_score_tokens_get",
|
|
667
|
+
description: "Fetch THS score for selected token mints using GET.",
|
|
668
|
+
inputSchema: {
|
|
669
|
+
type: "object",
|
|
670
|
+
required: ["wallet", "tokenMints"],
|
|
671
|
+
properties: {
|
|
672
|
+
wallet: { type: "string" },
|
|
673
|
+
tokenMints: { type: "array", items: { type: "string" } },
|
|
674
|
+
debug: { type: "boolean" },
|
|
675
|
+
breakdown: { type: "boolean" },
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
name: "ths_get_score_tokens_post",
|
|
681
|
+
description: "Fetch THS score for selected token mints using POST.",
|
|
682
|
+
inputSchema: {
|
|
683
|
+
type: "object",
|
|
684
|
+
required: ["wallet", "tokenMints"],
|
|
685
|
+
properties: {
|
|
686
|
+
wallet: { type: "string" },
|
|
687
|
+
tokenMints: { type: "array", items: { type: "string" } },
|
|
688
|
+
debug: { type: "boolean" },
|
|
689
|
+
breakdown: { type: "boolean" },
|
|
690
|
+
},
|
|
691
|
+
},
|
|
692
|
+
},
|
|
241
693
|
{
|
|
242
694
|
name: "swap_build",
|
|
243
695
|
description: "Build an unsigned swap transaction with Dritan.",
|
|
@@ -297,14 +749,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
297
749
|
switch (request.params.name) {
|
|
298
750
|
case "system_check_prereqs": {
|
|
299
751
|
const solanaCli = checkSolanaCli();
|
|
752
|
+
const apiKeySet = !!process.env.DRITAN_API_KEY;
|
|
300
753
|
return ok({
|
|
301
|
-
ready: solanaCli.ok,
|
|
302
|
-
checks: [
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
754
|
+
ready: solanaCli.ok && apiKeySet,
|
|
755
|
+
checks: [
|
|
756
|
+
solanaCli,
|
|
757
|
+
{
|
|
758
|
+
ok: apiKeySet,
|
|
759
|
+
name: "DRITAN_API_KEY",
|
|
760
|
+
hint: apiKeySet
|
|
761
|
+
? "API key is configured."
|
|
762
|
+
: "Missing DRITAN_API_KEY. Get your key at https://dritan.dev and set it as an environment variable.",
|
|
763
|
+
},
|
|
764
|
+
],
|
|
765
|
+
nextAction: !apiKeySet
|
|
766
|
+
? "Set DRITAN_API_KEY environment variable. Get your key at https://dritan.dev"
|
|
767
|
+
: !solanaCli.ok
|
|
768
|
+
? "Install Solana CLI using installHint, then retry wallet_create_local."
|
|
769
|
+
: "Environment ready.",
|
|
306
770
|
});
|
|
307
771
|
}
|
|
772
|
+
case "dritan_health": {
|
|
773
|
+
return ok(await checkDritanHealth());
|
|
774
|
+
}
|
|
308
775
|
case "wallet_create_local": {
|
|
309
776
|
const input = walletCreateSchema.parse(args);
|
|
310
777
|
const walletPath = toWalletPath(input.name, input.walletDir);
|
|
@@ -343,6 +810,111 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
343
810
|
return ok(await client.getFirstBuyers(input.mint));
|
|
344
811
|
return ok(await client.getTokenAggregated(input.mint));
|
|
345
812
|
}
|
|
813
|
+
case "token_get_price": {
|
|
814
|
+
const input = tokenMintSchema.parse(args);
|
|
815
|
+
const client = getDritanClient();
|
|
816
|
+
return ok(await client.getTokenPrice(input.mint));
|
|
817
|
+
}
|
|
818
|
+
case "token_search": {
|
|
819
|
+
const input = tokenSearchSchema.parse(args);
|
|
820
|
+
const client = getDritanClient();
|
|
821
|
+
return ok(await searchTokens(client, input.query, {
|
|
822
|
+
limit: input.limit,
|
|
823
|
+
cursor: input.cursor,
|
|
824
|
+
page: input.page,
|
|
825
|
+
}));
|
|
826
|
+
}
|
|
827
|
+
case "token_get_metadata": {
|
|
828
|
+
const input = tokenMintSchema.parse(args);
|
|
829
|
+
const client = getDritanClient();
|
|
830
|
+
return ok(await client.getTokenMetadata(input.mint));
|
|
831
|
+
}
|
|
832
|
+
case "token_get_risk": {
|
|
833
|
+
const input = tokenMintSchema.parse(args);
|
|
834
|
+
const client = getDritanClient();
|
|
835
|
+
return ok(await client.getTokenRisk(input.mint));
|
|
836
|
+
}
|
|
837
|
+
case "token_get_first_buyers": {
|
|
838
|
+
const input = tokenMintSchema.parse(args);
|
|
839
|
+
const client = getDritanClient();
|
|
840
|
+
return ok(await client.getFirstBuyers(input.mint));
|
|
841
|
+
}
|
|
842
|
+
case "token_get_aggregated": {
|
|
843
|
+
const input = tokenMintSchema.parse(args);
|
|
844
|
+
const client = getDritanClient();
|
|
845
|
+
return ok(await client.getTokenAggregated(input.mint));
|
|
846
|
+
}
|
|
847
|
+
case "token_get_deployer_stats": {
|
|
848
|
+
const input = tokenMintSchema.parse(args);
|
|
849
|
+
const client = getDritanClient();
|
|
850
|
+
return ok(await client.getDeployerStats(input.mint));
|
|
851
|
+
}
|
|
852
|
+
case "token_get_ohlcv": {
|
|
853
|
+
const input = tokenOhlcvSchema.parse(args);
|
|
854
|
+
const client = getDritanClient();
|
|
855
|
+
return ok(await client.getTokenOhlcv(input.mint, input.timeframe, { timeTo: input.timeTo }));
|
|
856
|
+
}
|
|
857
|
+
case "token_get_ohlcv_chart": {
|
|
858
|
+
const input = tokenOhlcvChartSchema.parse(args);
|
|
859
|
+
const client = getDritanClient();
|
|
860
|
+
const ohlcv = await client.getTokenOhlcv(input.mint, input.timeframe, { timeTo: input.timeTo });
|
|
861
|
+
const bars = [...(ohlcv.closed ?? [])];
|
|
862
|
+
if (input.includeActive && ohlcv.active) {
|
|
863
|
+
bars.push(ohlcv.active);
|
|
864
|
+
}
|
|
865
|
+
const trimmedBars = bars.slice(-input.maxPoints);
|
|
866
|
+
if (trimmedBars.length === 0) {
|
|
867
|
+
throw new Error(`No OHLCV data available for ${input.mint} (${input.timeframe})`);
|
|
868
|
+
}
|
|
869
|
+
const chartUrl = buildOhlcvChartUrl(input.mint, input.timeframe, trimmedBars, input.width, input.height);
|
|
870
|
+
return ok({
|
|
871
|
+
mint: input.mint,
|
|
872
|
+
timeframe: input.timeframe,
|
|
873
|
+
points: trimmedBars.length,
|
|
874
|
+
chartUrl,
|
|
875
|
+
markdown: ``,
|
|
876
|
+
lastBar: trimmedBars[trimmedBars.length - 1],
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
case "wallet_get_performance": {
|
|
880
|
+
const input = walletPerformanceSchema.parse(args);
|
|
881
|
+
const client = getDritanClient();
|
|
882
|
+
return ok(await client.getWalletPerformance(input.wallet, {
|
|
883
|
+
showHistoricPnL: input.showHistoricPnL,
|
|
884
|
+
holdingCheck: input.holdingCheck,
|
|
885
|
+
hideDetails: input.hideDetails,
|
|
886
|
+
}));
|
|
887
|
+
}
|
|
888
|
+
case "wallet_get_token_performance": {
|
|
889
|
+
const input = walletTokenPerformanceSchema.parse(args);
|
|
890
|
+
const client = getDritanClient();
|
|
891
|
+
return ok(await client.getWalletTokenPerformance(input.wallet, input.tokenMint));
|
|
892
|
+
}
|
|
893
|
+
case "wallet_get_portfolio_chart": {
|
|
894
|
+
const input = walletPortfolioChartSchema.parse(args);
|
|
895
|
+
const client = getDritanClient();
|
|
896
|
+
return ok(await client.getWalletPortfolioChart(input.wallet, { days: input.days }));
|
|
897
|
+
}
|
|
898
|
+
case "wallet_get_summary": {
|
|
899
|
+
const input = walletAddressSchema.parse(args);
|
|
900
|
+
const client = getDritanClient();
|
|
901
|
+
return ok(await client.getBasicWalletInformation(input.wallet));
|
|
902
|
+
}
|
|
903
|
+
case "wallet_get_trade_history": {
|
|
904
|
+
const input = walletTradeHistorySchema.parse(args);
|
|
905
|
+
const client = getDritanClient();
|
|
906
|
+
return ok(await client.getWalletTradeHistory(input.wallet, { cursor: input.cursor }));
|
|
907
|
+
}
|
|
908
|
+
case "wallet_get_holdings": {
|
|
909
|
+
const input = walletAddressSchema.parse(args);
|
|
910
|
+
const client = getDritanClient();
|
|
911
|
+
return ok(await client.getWalletHoldings(input.wallet));
|
|
912
|
+
}
|
|
913
|
+
case "wallet_get_holdings_page": {
|
|
914
|
+
const input = walletHoldingsPageSchema.parse(args);
|
|
915
|
+
const client = getDritanClient();
|
|
916
|
+
return ok(await client.getWalletHoldingsPage(input.wallet, input.page));
|
|
917
|
+
}
|
|
346
918
|
case "market_stream_sample": {
|
|
347
919
|
const input = marketStreamSampleSchema.parse(args);
|
|
348
920
|
const client = getDritanClient();
|
|
@@ -374,6 +946,80 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
374
946
|
await done;
|
|
375
947
|
return ok({ dex: input.dex, opened, closed, eventsCaptured: events.length, sample: events });
|
|
376
948
|
}
|
|
949
|
+
case "wallet_stream_sample": {
|
|
950
|
+
const input = walletStreamSampleSchema.parse(args);
|
|
951
|
+
const client = getDritanClient();
|
|
952
|
+
const events = [];
|
|
953
|
+
let opened = false;
|
|
954
|
+
let closed = false;
|
|
955
|
+
const done = new Promise((resolvePromise) => {
|
|
956
|
+
const handle = client.streamDex("wallet-stream", {
|
|
957
|
+
onOpen: () => {
|
|
958
|
+
opened = true;
|
|
959
|
+
try {
|
|
960
|
+
handle.socket.send(JSON.stringify({
|
|
961
|
+
method: "subscribeWallets",
|
|
962
|
+
wallets: input.wallets,
|
|
963
|
+
}));
|
|
964
|
+
}
|
|
965
|
+
catch {
|
|
966
|
+
// no-op
|
|
967
|
+
}
|
|
968
|
+
},
|
|
969
|
+
onClose: () => {
|
|
970
|
+
closed = true;
|
|
971
|
+
resolvePromise();
|
|
972
|
+
},
|
|
973
|
+
onMessage: (event) => {
|
|
974
|
+
events.push(event);
|
|
975
|
+
if (events.length >= input.maxEvents) {
|
|
976
|
+
handle.close();
|
|
977
|
+
}
|
|
978
|
+
},
|
|
979
|
+
});
|
|
980
|
+
setTimeout(() => {
|
|
981
|
+
if (!closed) {
|
|
982
|
+
handle.close();
|
|
983
|
+
}
|
|
984
|
+
}, input.durationMs);
|
|
985
|
+
});
|
|
986
|
+
await done;
|
|
987
|
+
return ok({
|
|
988
|
+
wallets: input.wallets,
|
|
989
|
+
opened,
|
|
990
|
+
closed,
|
|
991
|
+
eventsCaptured: events.length,
|
|
992
|
+
sample: events,
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
case "ths_health": {
|
|
996
|
+
const ths = getThsClient();
|
|
997
|
+
return ok({ ok: await ths.health() });
|
|
998
|
+
}
|
|
999
|
+
case "ths_get_score": {
|
|
1000
|
+
const input = thsScoreSchema.parse(args);
|
|
1001
|
+
const ths = getThsClient();
|
|
1002
|
+
return ok(await ths.getThsScore(input.wallet, {
|
|
1003
|
+
debug: input.debug,
|
|
1004
|
+
breakdown: input.breakdown,
|
|
1005
|
+
}));
|
|
1006
|
+
}
|
|
1007
|
+
case "ths_get_score_tokens_get": {
|
|
1008
|
+
const input = thsScoreTokensSchema.parse(args);
|
|
1009
|
+
const ths = getThsClient();
|
|
1010
|
+
return ok(await ths.getThsScoreForTokens(input.wallet, input.tokenMints, {
|
|
1011
|
+
debug: input.debug,
|
|
1012
|
+
breakdown: input.breakdown,
|
|
1013
|
+
}));
|
|
1014
|
+
}
|
|
1015
|
+
case "ths_get_score_tokens_post": {
|
|
1016
|
+
const input = thsScoreTokensSchema.parse(args);
|
|
1017
|
+
const ths = getThsClient();
|
|
1018
|
+
return ok(await ths.postThsScoreForTokens(input.wallet, input.tokenMints, {
|
|
1019
|
+
debug: input.debug,
|
|
1020
|
+
breakdown: input.breakdown,
|
|
1021
|
+
}));
|
|
1022
|
+
}
|
|
377
1023
|
case "swap_build": {
|
|
378
1024
|
const input = swapBuildSchema.parse(args);
|
|
379
1025
|
const client = getDritanClient();
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dritan/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "MCP server for Dritan SDK market data and local Solana wallet swap execution",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/
|
|
9
|
+
"url": "https://github.com/amirdauti/dritan-mcp"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://dritan.dev",
|
|
12
12
|
"bugs": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"build": "tsc -p tsconfig.json",
|
|
26
26
|
"dev": "tsx src/index.ts",
|
|
27
27
|
"start": "node dist/index.js",
|
|
28
|
-
"lint": "tsc -p tsconfig.json --noEmit"
|
|
28
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
29
|
+
"release": "semantic-release"
|
|
29
30
|
},
|
|
30
31
|
"publishConfig": {
|
|
31
32
|
"access": "public"
|
|
@@ -33,11 +34,20 @@
|
|
|
33
34
|
"dependencies": {
|
|
34
35
|
"@modelcontextprotocol/sdk": "^1.17.4",
|
|
35
36
|
"@solana/web3.js": "^1.98.4",
|
|
36
|
-
"dritan-sdk": "^0.
|
|
37
|
+
"dritan-sdk": "^0.6.0",
|
|
37
38
|
"zod": "^3.24.1"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
41
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
42
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
43
|
+
"@semantic-release/exec": "^7.1.0",
|
|
44
|
+
"@semantic-release/git": "^10.0.1",
|
|
45
|
+
"@semantic-release/github": "^11.0.6",
|
|
46
|
+
"@semantic-release/npm": "^12.0.2",
|
|
47
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
40
48
|
"@types/node": "^22.14.0",
|
|
49
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
50
|
+
"semantic-release": "^24.2.9",
|
|
41
51
|
"tsx": "^4.19.2",
|
|
42
52
|
"typescript": "^5.8.2"
|
|
43
53
|
}
|