@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 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,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(["pumpamm", "pumpfun", "launchlab", "dlmm", "damm2", "damm1", "dbc"]),
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: ["pumpamm", "pumpfun", "launchlab", "dlmm", "damm2", "damm1", "dbc"],
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: `![${input.mint} ${input.timeframe} chart](${chartUrl})`,
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.2.7",
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.1.3",
37
+ "dritan-sdk": "^0.6.0",
38
38
  "zod": "^3.24.1"
39
39
  },
40
40
  "devDependencies": {