@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 CHANGED
@@ -2,3 +2,4 @@ DRITAN_API_KEY=
2
2
  DRITAN_BASE_URL=https://us-east.dritan.dev
3
3
  DRITAN_WS_BASE_URL=wss://us-east.dritan.dev
4
4
  SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
5
+ METEORA_THS_BASE_URL=https://ths.dritan.dev
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(["pumpamm", "pumpfun", "launchlab", "dlmm", "damm2", "damm1", "dbc"]),
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: ["pumpamm", "pumpfun", "launchlab", "dlmm", "damm2", "damm1", "dbc"],
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: `![${input.mint} ${input.timeframe} chart](${chartUrl})`,
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.2.7",
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.1.3",
37
+ "dritan-sdk": "^0.6.0",
38
38
  "zod": "^3.24.1"
39
39
  },
40
40
  "devDependencies": {