@dritan/mcp 0.2.7 → 0.3.1
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 +582 -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,124 @@ 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
|
+
// QuickChart defaults to Chart.js v2. Our config uses v3+/v4 scale syntax (`options.scales.{id}`),
|
|
166
|
+
// so pinning `v=4` prevents runtime render errors like "Cannot read properties of undefined (reading 'options')".
|
|
167
|
+
return `https://quickchart.io/chart?w=${width}&h=${height}&f=png&v=4&c=${encoded}`;
|
|
168
|
+
}
|
|
38
169
|
function getPlatformInstallHint(binary) {
|
|
39
170
|
switch (process.platform) {
|
|
40
171
|
case "darwin":
|
|
@@ -152,11 +283,61 @@ const marketSnapshotSchema = z.object({
|
|
|
152
283
|
const tokenMintSchema = z.object({
|
|
153
284
|
mint: z.string().min(1),
|
|
154
285
|
});
|
|
286
|
+
const tokenSearchSchema = z.object({
|
|
287
|
+
query: z.string().min(1),
|
|
288
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
289
|
+
cursor: z.string().min(1).optional(),
|
|
290
|
+
page: z.number().int().min(1).optional(),
|
|
291
|
+
});
|
|
155
292
|
const marketStreamSampleSchema = z.object({
|
|
156
|
-
dex: z.enum(
|
|
293
|
+
dex: z.enum(STREAM_DEXES),
|
|
157
294
|
durationMs: z.number().int().min(500).max(60_000).default(10_000),
|
|
158
295
|
maxEvents: z.number().int().min(1).max(250).default(20),
|
|
159
296
|
});
|
|
297
|
+
const tokenOhlcvSchema = z.object({
|
|
298
|
+
mint: z.string().min(1),
|
|
299
|
+
timeframe: z.string().min(1),
|
|
300
|
+
timeTo: z.number().int().positive().optional(),
|
|
301
|
+
});
|
|
302
|
+
const tokenOhlcvChartSchema = tokenOhlcvSchema.extend({
|
|
303
|
+
includeActive: z.boolean().default(true),
|
|
304
|
+
maxPoints: z.number().int().min(10).max(500).default(120),
|
|
305
|
+
width: z.number().int().min(300).max(2000).default(1200),
|
|
306
|
+
height: z.number().int().min(200).max(1200).default(600),
|
|
307
|
+
});
|
|
308
|
+
const walletAddressSchema = z.object({
|
|
309
|
+
wallet: z.string().min(1),
|
|
310
|
+
});
|
|
311
|
+
const walletPerformanceSchema = walletAddressSchema.extend({
|
|
312
|
+
showHistoricPnL: z.boolean().optional(),
|
|
313
|
+
holdingCheck: z.boolean().optional(),
|
|
314
|
+
hideDetails: z.boolean().optional(),
|
|
315
|
+
});
|
|
316
|
+
const walletTokenPerformanceSchema = walletAddressSchema.extend({
|
|
317
|
+
tokenMint: z.string().min(1),
|
|
318
|
+
});
|
|
319
|
+
const walletPortfolioChartSchema = walletAddressSchema.extend({
|
|
320
|
+
days: z.number().int().min(1).max(3650).optional(),
|
|
321
|
+
});
|
|
322
|
+
const walletTradeHistorySchema = walletAddressSchema.extend({
|
|
323
|
+
cursor: z.string().min(1).optional(),
|
|
324
|
+
});
|
|
325
|
+
const walletHoldingsPageSchema = walletAddressSchema.extend({
|
|
326
|
+
page: z.number().int().min(1),
|
|
327
|
+
});
|
|
328
|
+
const walletStreamSampleSchema = z.object({
|
|
329
|
+
wallets: z.array(z.string().min(1)).min(1).max(100),
|
|
330
|
+
durationMs: z.number().int().min(500).max(60_000).default(10_000),
|
|
331
|
+
maxEvents: z.number().int().min(1).max(250).default(20),
|
|
332
|
+
});
|
|
333
|
+
const thsScoreSchema = z.object({
|
|
334
|
+
wallet: z.string().min(1),
|
|
335
|
+
debug: z.boolean().optional(),
|
|
336
|
+
breakdown: z.boolean().optional(),
|
|
337
|
+
});
|
|
338
|
+
const thsScoreTokensSchema = thsScoreSchema.extend({
|
|
339
|
+
tokenMints: z.array(z.string().min(1)).min(1).max(200),
|
|
340
|
+
});
|
|
160
341
|
const swapBuildSchema = z.object({
|
|
161
342
|
walletPath: z.string().min(1).optional(),
|
|
162
343
|
userPublicKey: z.string().min(1).optional(),
|
|
@@ -216,6 +397,14 @@ const tools = [
|
|
|
216
397
|
},
|
|
217
398
|
},
|
|
218
399
|
},
|
|
400
|
+
{
|
|
401
|
+
name: "dritan_health",
|
|
402
|
+
description: "Check data plane health endpoint via Dritan SDK.",
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: {},
|
|
406
|
+
},
|
|
407
|
+
},
|
|
219
408
|
{
|
|
220
409
|
name: "market_get_snapshot",
|
|
221
410
|
description: "Fetch token market snapshot via Dritan SDK (price/metadata/risk/first-buyers/aggregated).",
|
|
@@ -231,6 +420,20 @@ const tools = [
|
|
|
231
420
|
},
|
|
232
421
|
},
|
|
233
422
|
},
|
|
423
|
+
{
|
|
424
|
+
name: "token_search",
|
|
425
|
+
description: "Search tokens by ticker/name and resolve mint addresses (first step for ticker-based chart requests).",
|
|
426
|
+
inputSchema: {
|
|
427
|
+
type: "object",
|
|
428
|
+
required: ["query"],
|
|
429
|
+
properties: {
|
|
430
|
+
query: { type: "string", description: "Ticker or token name, e.g. WIF or $WIF" },
|
|
431
|
+
limit: { type: "number", description: "Optional result limit (1-50)" },
|
|
432
|
+
cursor: { type: "string", description: "Optional cursor for pagination" },
|
|
433
|
+
page: { type: "number", description: "Optional page (used when cursor is absent)" },
|
|
434
|
+
},
|
|
435
|
+
},
|
|
436
|
+
},
|
|
234
437
|
{
|
|
235
438
|
name: "token_get_price",
|
|
236
439
|
description: "Fetch token price via Dritan (same as market_get_snapshot mode=price).",
|
|
@@ -242,6 +445,17 @@ const tools = [
|
|
|
242
445
|
},
|
|
243
446
|
},
|
|
244
447
|
},
|
|
448
|
+
{
|
|
449
|
+
name: "token_get_metadata",
|
|
450
|
+
description: "Fetch token metadata via Dritan.",
|
|
451
|
+
inputSchema: {
|
|
452
|
+
type: "object",
|
|
453
|
+
required: ["mint"],
|
|
454
|
+
properties: {
|
|
455
|
+
mint: { type: "string" },
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
},
|
|
245
459
|
{
|
|
246
460
|
name: "token_get_risk",
|
|
247
461
|
description: "Fetch token risk via Dritan (same as market_get_snapshot mode=risk).",
|
|
@@ -253,6 +467,17 @@ const tools = [
|
|
|
253
467
|
},
|
|
254
468
|
},
|
|
255
469
|
},
|
|
470
|
+
{
|
|
471
|
+
name: "token_get_first_buyers",
|
|
472
|
+
description: "Fetch first buyers via Dritan.",
|
|
473
|
+
inputSchema: {
|
|
474
|
+
type: "object",
|
|
475
|
+
required: ["mint"],
|
|
476
|
+
properties: {
|
|
477
|
+
mint: { type: "string" },
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
256
481
|
{
|
|
257
482
|
name: "token_get_aggregated",
|
|
258
483
|
description: "Fetch aggregated token data via Dritan (same as market_get_snapshot mode=aggregated).",
|
|
@@ -264,6 +489,131 @@ const tools = [
|
|
|
264
489
|
},
|
|
265
490
|
},
|
|
266
491
|
},
|
|
492
|
+
{
|
|
493
|
+
name: "token_get_deployer_stats",
|
|
494
|
+
description: "Fetch token deployer stats via Dritan.",
|
|
495
|
+
inputSchema: {
|
|
496
|
+
type: "object",
|
|
497
|
+
required: ["mint"],
|
|
498
|
+
properties: {
|
|
499
|
+
mint: { type: "string" },
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: "token_get_ohlcv",
|
|
505
|
+
description: "Fetch OHLCV candles for a token and timeframe.",
|
|
506
|
+
inputSchema: {
|
|
507
|
+
type: "object",
|
|
508
|
+
required: ["mint", "timeframe"],
|
|
509
|
+
properties: {
|
|
510
|
+
mint: { type: "string" },
|
|
511
|
+
timeframe: { type: "string", description: "e.g. 1m, 5m, 1h, 1d" },
|
|
512
|
+
timeTo: { type: "number" },
|
|
513
|
+
},
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
name: "token_get_ohlcv_chart",
|
|
518
|
+
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).",
|
|
519
|
+
inputSchema: {
|
|
520
|
+
type: "object",
|
|
521
|
+
required: ["mint", "timeframe"],
|
|
522
|
+
properties: {
|
|
523
|
+
mint: { type: "string" },
|
|
524
|
+
timeframe: { type: "string", description: "e.g. 1m, 5m, 1h, 1d" },
|
|
525
|
+
timeTo: { type: "number" },
|
|
526
|
+
includeActive: { type: "boolean" },
|
|
527
|
+
maxPoints: { type: "number" },
|
|
528
|
+
width: { type: "number" },
|
|
529
|
+
height: { type: "number" },
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: "wallet_get_performance",
|
|
535
|
+
description: "Fetch wallet performance via Dritan.",
|
|
536
|
+
inputSchema: {
|
|
537
|
+
type: "object",
|
|
538
|
+
required: ["wallet"],
|
|
539
|
+
properties: {
|
|
540
|
+
wallet: { type: "string" },
|
|
541
|
+
showHistoricPnL: { type: "boolean" },
|
|
542
|
+
holdingCheck: { type: "boolean" },
|
|
543
|
+
hideDetails: { type: "boolean" },
|
|
544
|
+
},
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: "wallet_get_token_performance",
|
|
549
|
+
description: "Fetch wallet performance for a specific token mint via Dritan.",
|
|
550
|
+
inputSchema: {
|
|
551
|
+
type: "object",
|
|
552
|
+
required: ["wallet", "tokenMint"],
|
|
553
|
+
properties: {
|
|
554
|
+
wallet: { type: "string" },
|
|
555
|
+
tokenMint: { type: "string" },
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
name: "wallet_get_portfolio_chart",
|
|
561
|
+
description: "Fetch wallet portfolio chart series via Dritan.",
|
|
562
|
+
inputSchema: {
|
|
563
|
+
type: "object",
|
|
564
|
+
required: ["wallet"],
|
|
565
|
+
properties: {
|
|
566
|
+
wallet: { type: "string" },
|
|
567
|
+
days: { type: "number" },
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
name: "wallet_get_summary",
|
|
573
|
+
description: "Fetch basic wallet summary via Dritan.",
|
|
574
|
+
inputSchema: {
|
|
575
|
+
type: "object",
|
|
576
|
+
required: ["wallet"],
|
|
577
|
+
properties: {
|
|
578
|
+
wallet: { type: "string" },
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
name: "wallet_get_trade_history",
|
|
584
|
+
description: "Fetch wallet trade history via Dritan.",
|
|
585
|
+
inputSchema: {
|
|
586
|
+
type: "object",
|
|
587
|
+
required: ["wallet"],
|
|
588
|
+
properties: {
|
|
589
|
+
wallet: { type: "string" },
|
|
590
|
+
cursor: { type: "string" },
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: "wallet_get_holdings",
|
|
596
|
+
description: "Fetch wallet holdings via Dritan.",
|
|
597
|
+
inputSchema: {
|
|
598
|
+
type: "object",
|
|
599
|
+
required: ["wallet"],
|
|
600
|
+
properties: {
|
|
601
|
+
wallet: { type: "string" },
|
|
602
|
+
},
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
name: "wallet_get_holdings_page",
|
|
607
|
+
description: "Fetch paginated wallet holdings via Dritan.",
|
|
608
|
+
inputSchema: {
|
|
609
|
+
type: "object",
|
|
610
|
+
required: ["wallet", "page"],
|
|
611
|
+
properties: {
|
|
612
|
+
wallet: { type: "string" },
|
|
613
|
+
page: { type: "number" },
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
},
|
|
267
617
|
{
|
|
268
618
|
name: "market_stream_sample",
|
|
269
619
|
description: "Open a DEX websocket stream and collect events for a short duration.",
|
|
@@ -273,13 +623,75 @@ const tools = [
|
|
|
273
623
|
properties: {
|
|
274
624
|
dex: {
|
|
275
625
|
type: "string",
|
|
276
|
-
enum:
|
|
626
|
+
enum: STREAM_DEXES,
|
|
277
627
|
},
|
|
278
628
|
durationMs: { type: "number" },
|
|
279
629
|
maxEvents: { type: "number" },
|
|
280
630
|
},
|
|
281
631
|
},
|
|
282
632
|
},
|
|
633
|
+
{
|
|
634
|
+
name: "wallet_stream_sample",
|
|
635
|
+
description: "Open the wallet websocket stream and collect events for selected wallets.",
|
|
636
|
+
inputSchema: {
|
|
637
|
+
type: "object",
|
|
638
|
+
required: ["wallets"],
|
|
639
|
+
properties: {
|
|
640
|
+
wallets: { type: "array", items: { type: "string" } },
|
|
641
|
+
durationMs: { type: "number" },
|
|
642
|
+
maxEvents: { type: "number" },
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
name: "ths_health",
|
|
648
|
+
description: "Check Meteora THS service health.",
|
|
649
|
+
inputSchema: {
|
|
650
|
+
type: "object",
|
|
651
|
+
properties: {},
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
name: "ths_get_score",
|
|
656
|
+
description: "Fetch THS score for a wallet.",
|
|
657
|
+
inputSchema: {
|
|
658
|
+
type: "object",
|
|
659
|
+
required: ["wallet"],
|
|
660
|
+
properties: {
|
|
661
|
+
wallet: { type: "string" },
|
|
662
|
+
debug: { type: "boolean" },
|
|
663
|
+
breakdown: { type: "boolean" },
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
name: "ths_get_score_tokens_get",
|
|
669
|
+
description: "Fetch THS score for selected token mints using GET.",
|
|
670
|
+
inputSchema: {
|
|
671
|
+
type: "object",
|
|
672
|
+
required: ["wallet", "tokenMints"],
|
|
673
|
+
properties: {
|
|
674
|
+
wallet: { type: "string" },
|
|
675
|
+
tokenMints: { type: "array", items: { type: "string" } },
|
|
676
|
+
debug: { type: "boolean" },
|
|
677
|
+
breakdown: { type: "boolean" },
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
name: "ths_get_score_tokens_post",
|
|
683
|
+
description: "Fetch THS score for selected token mints using POST.",
|
|
684
|
+
inputSchema: {
|
|
685
|
+
type: "object",
|
|
686
|
+
required: ["wallet", "tokenMints"],
|
|
687
|
+
properties: {
|
|
688
|
+
wallet: { type: "string" },
|
|
689
|
+
tokenMints: { type: "array", items: { type: "string" } },
|
|
690
|
+
debug: { type: "boolean" },
|
|
691
|
+
breakdown: { type: "boolean" },
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
},
|
|
283
695
|
{
|
|
284
696
|
name: "swap_build",
|
|
285
697
|
description: "Build an unsigned swap transaction with Dritan.",
|
|
@@ -359,6 +771,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
359
771
|
: "Environment ready.",
|
|
360
772
|
});
|
|
361
773
|
}
|
|
774
|
+
case "dritan_health": {
|
|
775
|
+
return ok(await checkDritanHealth());
|
|
776
|
+
}
|
|
362
777
|
case "wallet_create_local": {
|
|
363
778
|
const input = walletCreateSchema.parse(args);
|
|
364
779
|
const walletPath = toWalletPath(input.name, input.walletDir);
|
|
@@ -402,16 +817,106 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
402
817
|
const client = getDritanClient();
|
|
403
818
|
return ok(await client.getTokenPrice(input.mint));
|
|
404
819
|
}
|
|
820
|
+
case "token_search": {
|
|
821
|
+
const input = tokenSearchSchema.parse(args);
|
|
822
|
+
const client = getDritanClient();
|
|
823
|
+
return ok(await searchTokens(client, input.query, {
|
|
824
|
+
limit: input.limit,
|
|
825
|
+
cursor: input.cursor,
|
|
826
|
+
page: input.page,
|
|
827
|
+
}));
|
|
828
|
+
}
|
|
829
|
+
case "token_get_metadata": {
|
|
830
|
+
const input = tokenMintSchema.parse(args);
|
|
831
|
+
const client = getDritanClient();
|
|
832
|
+
return ok(await client.getTokenMetadata(input.mint));
|
|
833
|
+
}
|
|
405
834
|
case "token_get_risk": {
|
|
406
835
|
const input = tokenMintSchema.parse(args);
|
|
407
836
|
const client = getDritanClient();
|
|
408
837
|
return ok(await client.getTokenRisk(input.mint));
|
|
409
838
|
}
|
|
839
|
+
case "token_get_first_buyers": {
|
|
840
|
+
const input = tokenMintSchema.parse(args);
|
|
841
|
+
const client = getDritanClient();
|
|
842
|
+
return ok(await client.getFirstBuyers(input.mint));
|
|
843
|
+
}
|
|
410
844
|
case "token_get_aggregated": {
|
|
411
845
|
const input = tokenMintSchema.parse(args);
|
|
412
846
|
const client = getDritanClient();
|
|
413
847
|
return ok(await client.getTokenAggregated(input.mint));
|
|
414
848
|
}
|
|
849
|
+
case "token_get_deployer_stats": {
|
|
850
|
+
const input = tokenMintSchema.parse(args);
|
|
851
|
+
const client = getDritanClient();
|
|
852
|
+
return ok(await client.getDeployerStats(input.mint));
|
|
853
|
+
}
|
|
854
|
+
case "token_get_ohlcv": {
|
|
855
|
+
const input = tokenOhlcvSchema.parse(args);
|
|
856
|
+
const client = getDritanClient();
|
|
857
|
+
return ok(await client.getTokenOhlcv(input.mint, input.timeframe, { timeTo: input.timeTo }));
|
|
858
|
+
}
|
|
859
|
+
case "token_get_ohlcv_chart": {
|
|
860
|
+
const input = tokenOhlcvChartSchema.parse(args);
|
|
861
|
+
const client = getDritanClient();
|
|
862
|
+
const ohlcv = await client.getTokenOhlcv(input.mint, input.timeframe, { timeTo: input.timeTo });
|
|
863
|
+
const bars = [...(ohlcv.closed ?? [])];
|
|
864
|
+
if (input.includeActive && ohlcv.active) {
|
|
865
|
+
bars.push(ohlcv.active);
|
|
866
|
+
}
|
|
867
|
+
const trimmedBars = bars.slice(-input.maxPoints);
|
|
868
|
+
if (trimmedBars.length === 0) {
|
|
869
|
+
throw new Error(`No OHLCV data available for ${input.mint} (${input.timeframe})`);
|
|
870
|
+
}
|
|
871
|
+
const chartUrl = buildOhlcvChartUrl(input.mint, input.timeframe, trimmedBars, input.width, input.height);
|
|
872
|
+
return ok({
|
|
873
|
+
mint: input.mint,
|
|
874
|
+
timeframe: input.timeframe,
|
|
875
|
+
points: trimmedBars.length,
|
|
876
|
+
chartUrl,
|
|
877
|
+
markdown: ``,
|
|
878
|
+
lastBar: trimmedBars[trimmedBars.length - 1],
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
case "wallet_get_performance": {
|
|
882
|
+
const input = walletPerformanceSchema.parse(args);
|
|
883
|
+
const client = getDritanClient();
|
|
884
|
+
return ok(await client.getWalletPerformance(input.wallet, {
|
|
885
|
+
showHistoricPnL: input.showHistoricPnL,
|
|
886
|
+
holdingCheck: input.holdingCheck,
|
|
887
|
+
hideDetails: input.hideDetails,
|
|
888
|
+
}));
|
|
889
|
+
}
|
|
890
|
+
case "wallet_get_token_performance": {
|
|
891
|
+
const input = walletTokenPerformanceSchema.parse(args);
|
|
892
|
+
const client = getDritanClient();
|
|
893
|
+
return ok(await client.getWalletTokenPerformance(input.wallet, input.tokenMint));
|
|
894
|
+
}
|
|
895
|
+
case "wallet_get_portfolio_chart": {
|
|
896
|
+
const input = walletPortfolioChartSchema.parse(args);
|
|
897
|
+
const client = getDritanClient();
|
|
898
|
+
return ok(await client.getWalletPortfolioChart(input.wallet, { days: input.days }));
|
|
899
|
+
}
|
|
900
|
+
case "wallet_get_summary": {
|
|
901
|
+
const input = walletAddressSchema.parse(args);
|
|
902
|
+
const client = getDritanClient();
|
|
903
|
+
return ok(await client.getBasicWalletInformation(input.wallet));
|
|
904
|
+
}
|
|
905
|
+
case "wallet_get_trade_history": {
|
|
906
|
+
const input = walletTradeHistorySchema.parse(args);
|
|
907
|
+
const client = getDritanClient();
|
|
908
|
+
return ok(await client.getWalletTradeHistory(input.wallet, { cursor: input.cursor }));
|
|
909
|
+
}
|
|
910
|
+
case "wallet_get_holdings": {
|
|
911
|
+
const input = walletAddressSchema.parse(args);
|
|
912
|
+
const client = getDritanClient();
|
|
913
|
+
return ok(await client.getWalletHoldings(input.wallet));
|
|
914
|
+
}
|
|
915
|
+
case "wallet_get_holdings_page": {
|
|
916
|
+
const input = walletHoldingsPageSchema.parse(args);
|
|
917
|
+
const client = getDritanClient();
|
|
918
|
+
return ok(await client.getWalletHoldingsPage(input.wallet, input.page));
|
|
919
|
+
}
|
|
415
920
|
case "market_stream_sample": {
|
|
416
921
|
const input = marketStreamSampleSchema.parse(args);
|
|
417
922
|
const client = getDritanClient();
|
|
@@ -443,6 +948,80 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
443
948
|
await done;
|
|
444
949
|
return ok({ dex: input.dex, opened, closed, eventsCaptured: events.length, sample: events });
|
|
445
950
|
}
|
|
951
|
+
case "wallet_stream_sample": {
|
|
952
|
+
const input = walletStreamSampleSchema.parse(args);
|
|
953
|
+
const client = getDritanClient();
|
|
954
|
+
const events = [];
|
|
955
|
+
let opened = false;
|
|
956
|
+
let closed = false;
|
|
957
|
+
const done = new Promise((resolvePromise) => {
|
|
958
|
+
const handle = client.streamDex("wallet-stream", {
|
|
959
|
+
onOpen: () => {
|
|
960
|
+
opened = true;
|
|
961
|
+
try {
|
|
962
|
+
handle.socket.send(JSON.stringify({
|
|
963
|
+
method: "subscribeWallets",
|
|
964
|
+
wallets: input.wallets,
|
|
965
|
+
}));
|
|
966
|
+
}
|
|
967
|
+
catch {
|
|
968
|
+
// no-op
|
|
969
|
+
}
|
|
970
|
+
},
|
|
971
|
+
onClose: () => {
|
|
972
|
+
closed = true;
|
|
973
|
+
resolvePromise();
|
|
974
|
+
},
|
|
975
|
+
onMessage: (event) => {
|
|
976
|
+
events.push(event);
|
|
977
|
+
if (events.length >= input.maxEvents) {
|
|
978
|
+
handle.close();
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
});
|
|
982
|
+
setTimeout(() => {
|
|
983
|
+
if (!closed) {
|
|
984
|
+
handle.close();
|
|
985
|
+
}
|
|
986
|
+
}, input.durationMs);
|
|
987
|
+
});
|
|
988
|
+
await done;
|
|
989
|
+
return ok({
|
|
990
|
+
wallets: input.wallets,
|
|
991
|
+
opened,
|
|
992
|
+
closed,
|
|
993
|
+
eventsCaptured: events.length,
|
|
994
|
+
sample: events,
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
case "ths_health": {
|
|
998
|
+
const ths = getThsClient();
|
|
999
|
+
return ok({ ok: await ths.health() });
|
|
1000
|
+
}
|
|
1001
|
+
case "ths_get_score": {
|
|
1002
|
+
const input = thsScoreSchema.parse(args);
|
|
1003
|
+
const ths = getThsClient();
|
|
1004
|
+
return ok(await ths.getThsScore(input.wallet, {
|
|
1005
|
+
debug: input.debug,
|
|
1006
|
+
breakdown: input.breakdown,
|
|
1007
|
+
}));
|
|
1008
|
+
}
|
|
1009
|
+
case "ths_get_score_tokens_get": {
|
|
1010
|
+
const input = thsScoreTokensSchema.parse(args);
|
|
1011
|
+
const ths = getThsClient();
|
|
1012
|
+
return ok(await ths.getThsScoreForTokens(input.wallet, input.tokenMints, {
|
|
1013
|
+
debug: input.debug,
|
|
1014
|
+
breakdown: input.breakdown,
|
|
1015
|
+
}));
|
|
1016
|
+
}
|
|
1017
|
+
case "ths_get_score_tokens_post": {
|
|
1018
|
+
const input = thsScoreTokensSchema.parse(args);
|
|
1019
|
+
const ths = getThsClient();
|
|
1020
|
+
return ok(await ths.postThsScoreForTokens(input.wallet, input.tokenMints, {
|
|
1021
|
+
debug: input.debug,
|
|
1022
|
+
breakdown: input.breakdown,
|
|
1023
|
+
}));
|
|
1024
|
+
}
|
|
446
1025
|
case "swap_build": {
|
|
447
1026
|
const input = swapBuildSchema.parse(args);
|
|
448
1027
|
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.1",
|
|
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": {
|