@dritan/mcp 0.2.7 → 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 +22 -0
- package/dist/index.js +580 -3
- package/package.json +2 -2
package/.env.example
CHANGED
package/README.md
CHANGED
|
@@ -41,11 +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`
|
|
45
47
|
- `token_get_price`
|
|
48
|
+
- `token_get_metadata`
|
|
46
49
|
- `token_get_risk`
|
|
50
|
+
- `token_get_first_buyers`
|
|
47
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`
|
|
48
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`
|
|
49
68
|
- `swap_build`
|
|
50
69
|
- `swap_sign_and_broadcast`
|
|
51
70
|
- `swap_build_sign_and_broadcast`
|
|
@@ -55,4 +74,7 @@ npm run build && npm start
|
|
|
55
74
|
- Wallets default to `~/.config/dritan-mcp/wallets`.
|
|
56
75
|
- Private keys never leave local files; only public address/signature are returned.
|
|
57
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.
|
|
58
80
|
- If Solana CLI is missing, run `system_check_prereqs` and follow returned install steps.
|
package/dist/index.js
CHANGED
|
@@ -6,10 +6,23 @@ 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
28
|
version: "0.1.2",
|
|
@@ -35,6 +48,122 @@ function getDritanClient() {
|
|
|
35
48
|
wsBaseUrl: process.env.DRITAN_WS_BASE_URL,
|
|
36
49
|
});
|
|
37
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
|
+
}
|
|
38
167
|
function getPlatformInstallHint(binary) {
|
|
39
168
|
switch (process.platform) {
|
|
40
169
|
case "darwin":
|
|
@@ -152,11 +281,61 @@ const marketSnapshotSchema = z.object({
|
|
|
152
281
|
const tokenMintSchema = z.object({
|
|
153
282
|
mint: z.string().min(1),
|
|
154
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
|
+
});
|
|
155
290
|
const marketStreamSampleSchema = z.object({
|
|
156
|
-
dex: z.enum(
|
|
291
|
+
dex: z.enum(STREAM_DEXES),
|
|
157
292
|
durationMs: z.number().int().min(500).max(60_000).default(10_000),
|
|
158
293
|
maxEvents: z.number().int().min(1).max(250).default(20),
|
|
159
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
|
+
});
|
|
160
339
|
const swapBuildSchema = z.object({
|
|
161
340
|
walletPath: z.string().min(1).optional(),
|
|
162
341
|
userPublicKey: z.string().min(1).optional(),
|
|
@@ -216,6 +395,14 @@ const tools = [
|
|
|
216
395
|
},
|
|
217
396
|
},
|
|
218
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
|
+
},
|
|
219
406
|
{
|
|
220
407
|
name: "market_get_snapshot",
|
|
221
408
|
description: "Fetch token market snapshot via Dritan SDK (price/metadata/risk/first-buyers/aggregated).",
|
|
@@ -231,6 +418,20 @@ const tools = [
|
|
|
231
418
|
},
|
|
232
419
|
},
|
|
233
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
|
+
},
|
|
234
435
|
{
|
|
235
436
|
name: "token_get_price",
|
|
236
437
|
description: "Fetch token price via Dritan (same as market_get_snapshot mode=price).",
|
|
@@ -242,6 +443,17 @@ const tools = [
|
|
|
242
443
|
},
|
|
243
444
|
},
|
|
244
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
|
+
},
|
|
245
457
|
{
|
|
246
458
|
name: "token_get_risk",
|
|
247
459
|
description: "Fetch token risk via Dritan (same as market_get_snapshot mode=risk).",
|
|
@@ -253,6 +465,17 @@ const tools = [
|
|
|
253
465
|
},
|
|
254
466
|
},
|
|
255
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
|
+
},
|
|
256
479
|
{
|
|
257
480
|
name: "token_get_aggregated",
|
|
258
481
|
description: "Fetch aggregated token data via Dritan (same as market_get_snapshot mode=aggregated).",
|
|
@@ -264,6 +487,131 @@ const tools = [
|
|
|
264
487
|
},
|
|
265
488
|
},
|
|
266
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
|
+
},
|
|
267
615
|
{
|
|
268
616
|
name: "market_stream_sample",
|
|
269
617
|
description: "Open a DEX websocket stream and collect events for a short duration.",
|
|
@@ -273,13 +621,75 @@ const tools = [
|
|
|
273
621
|
properties: {
|
|
274
622
|
dex: {
|
|
275
623
|
type: "string",
|
|
276
|
-
enum:
|
|
624
|
+
enum: STREAM_DEXES,
|
|
277
625
|
},
|
|
278
626
|
durationMs: { type: "number" },
|
|
279
627
|
maxEvents: { type: "number" },
|
|
280
628
|
},
|
|
281
629
|
},
|
|
282
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
|
+
},
|
|
283
693
|
{
|
|
284
694
|
name: "swap_build",
|
|
285
695
|
description: "Build an unsigned swap transaction with Dritan.",
|
|
@@ -359,6 +769,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
359
769
|
: "Environment ready.",
|
|
360
770
|
});
|
|
361
771
|
}
|
|
772
|
+
case "dritan_health": {
|
|
773
|
+
return ok(await checkDritanHealth());
|
|
774
|
+
}
|
|
362
775
|
case "wallet_create_local": {
|
|
363
776
|
const input = walletCreateSchema.parse(args);
|
|
364
777
|
const walletPath = toWalletPath(input.name, input.walletDir);
|
|
@@ -402,16 +815,106 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
402
815
|
const client = getDritanClient();
|
|
403
816
|
return ok(await client.getTokenPrice(input.mint));
|
|
404
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
|
+
}
|
|
405
832
|
case "token_get_risk": {
|
|
406
833
|
const input = tokenMintSchema.parse(args);
|
|
407
834
|
const client = getDritanClient();
|
|
408
835
|
return ok(await client.getTokenRisk(input.mint));
|
|
409
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
|
+
}
|
|
410
842
|
case "token_get_aggregated": {
|
|
411
843
|
const input = tokenMintSchema.parse(args);
|
|
412
844
|
const client = getDritanClient();
|
|
413
845
|
return ok(await client.getTokenAggregated(input.mint));
|
|
414
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
|
+
}
|
|
415
918
|
case "market_stream_sample": {
|
|
416
919
|
const input = marketStreamSampleSchema.parse(args);
|
|
417
920
|
const client = getDritanClient();
|
|
@@ -443,6 +946,80 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
443
946
|
await done;
|
|
444
947
|
return ok({ dex: input.dex, opened, closed, eventsCaptured: events.length, sample: events });
|
|
445
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
|
+
}
|
|
446
1023
|
case "swap_build": {
|
|
447
1024
|
const input = swapBuildSchema.parse(args);
|
|
448
1025
|
const client = getDritanClient();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@modelcontextprotocol/sdk": "^1.17.4",
|
|
36
36
|
"@solana/web3.js": "^1.98.4",
|
|
37
|
-
"dritan-sdk": "^0.
|
|
37
|
+
"dritan-sdk": "^0.6.0",
|
|
38
38
|
"zod": "^3.24.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|