@dritan/mcp 0.5.1 → 0.6.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.
Files changed (3) hide show
  1. package/README.md +5 -1
  2. package/dist/index.js +129 -20
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -39,6 +39,8 @@ npm run build && npm start
39
39
  ## Tools
40
40
 
41
41
  - `system_check_prereqs`
42
+ - `auth_status`
43
+ - `auth_set_api_key`
42
44
  - `wallet_create_local`
43
45
  - `wallet_get_address`
44
46
  - `wallet_get_balance`
@@ -79,11 +81,13 @@ npm run build && npm start
79
81
  - Wallets default to `~/.config/dritan-mcp/wallets`.
80
82
  - Private keys never leave local files; only public address/signature are returned.
81
83
  - `swap_sign_and_broadcast` signs locally, then broadcasts via Dritan.
84
+ - `auth_set_api_key` activates a key for the running MCP process without restart.
82
85
  - Agent onboarding without `DRITAN_API_KEY` should present two options:
83
86
  - Option 1: paid x402 flow (`x402_get_pricing` -> `x402_create_api_key_quote` -> user funds agent wallet -> `wallet_transfer_sol` -> `x402_create_api_key`).
84
87
  - Option 2: user gets a free key at `https://dritan.dev`.
88
+ - `x402_create_api_key` auto-activates returned keys for the current MCP session.
85
89
  - `token_get_ohlcv_chart` returns a shareable chart URL plus a ready-to-send markdown image snippet.
86
- - `token_get_ohlcv_chart` supports `chartType: "line-volume" | "candlestick"` (default is `line-volume`).
90
+ - `token_get_ohlcv_chart` supports `chartType: "line-volume" | "candlestick"` (default is `candlestick`).
87
91
  - Ticker workflow for chart requests: `token_search` -> extract mint -> `token_get_ohlcv` or `token_get_ohlcv_chart`.
88
92
  - If users ask for `$WIF` style symbols, always resolve mint with `token_search` first.
89
93
  - If Solana CLI is missing, run `system_check_prereqs` and follow returned install steps.
package/dist/index.js CHANGED
@@ -11,6 +11,41 @@ import { Connection, Keypair, PublicKey, SystemProgram, Transaction, VersionedTr
11
11
  import { z } from "zod";
12
12
  const DEFAULT_WALLET_DIR = join(homedir(), ".config", "dritan-mcp", "wallets");
13
13
  const LAMPORTS_PER_SOL = 1_000_000_000;
14
+ function normalizeApiKey(value) {
15
+ const trimmed = value?.trim();
16
+ return trimmed ? trimmed : null;
17
+ }
18
+ function apiKeyPreview(apiKey) {
19
+ if (!apiKey)
20
+ return null;
21
+ if (apiKey.length <= 12)
22
+ return `${apiKey.slice(0, 4)}...`;
23
+ return `${apiKey.slice(0, 8)}...${apiKey.slice(-4)}`;
24
+ }
25
+ let runtimeApiKey = normalizeApiKey(process.env.DRITAN_API_KEY);
26
+ let runtimeApiKeySource = runtimeApiKey ? "env" : "none";
27
+ function setRuntimeApiKey(apiKey, source) {
28
+ const normalized = normalizeApiKey(apiKey);
29
+ if (!normalized) {
30
+ throw new Error("apiKey is required");
31
+ }
32
+ runtimeApiKey = normalized;
33
+ runtimeApiKeySource = source;
34
+ process.env.DRITAN_API_KEY = normalized;
35
+ return normalized;
36
+ }
37
+ function getActiveApiKey() {
38
+ if (runtimeApiKey)
39
+ return runtimeApiKey;
40
+ const fromEnv = normalizeApiKey(process.env.DRITAN_API_KEY);
41
+ if (fromEnv) {
42
+ runtimeApiKey = fromEnv;
43
+ runtimeApiKeySource = "env";
44
+ return fromEnv;
45
+ }
46
+ runtimeApiKeySource = "none";
47
+ return null;
48
+ }
14
49
  const STREAM_DEXES = [
15
50
  "pumpamm",
16
51
  "pumpfun",
@@ -35,7 +70,7 @@ const server = new Server({
35
70
  "This server supports two API-key onboarding options when DRITAN_API_KEY is missing:",
36
71
  "1) x402 pay-per-use key flow: create wallet, receive SOL, create quote, forward payment, claim key.",
37
72
  "2) Free key flow: user creates a free API key at https://dritan.dev.",
38
- "After key is obtained, set DRITAN_API_KEY and continue with market/swap tools.",
73
+ "After key is obtained, set it with auth_set_api_key (no restart needed), or restart with DRITAN_API_KEY configured.",
39
74
  "Suggested setup command:",
40
75
  " claude mcp add dritan-mcp -e DRITAN_API_KEY=<your-key> -- npx @dritan/mcp@latest",
41
76
  ].join("\n"),
@@ -48,10 +83,11 @@ function missingApiKeyError() {
48
83
  "Missing DRITAN_API_KEY in environment.",
49
84
  "Option 1 (paid): use x402 tools (x402_get_pricing, x402_create_api_key_quote, x402_create_api_key) and wallet tools.",
50
85
  "Option 2 (free): create a free key at https://dritan.dev and set DRITAN_API_KEY.",
86
+ "You can activate a key immediately with auth_set_api_key without restarting MCP.",
51
87
  ].join(" "));
52
88
  }
53
89
  function getDritanClient() {
54
- const apiKey = process.env.DRITAN_API_KEY;
90
+ const apiKey = getActiveApiKey();
55
91
  if (!apiKey) {
56
92
  throw missingApiKeyError();
57
93
  }
@@ -65,7 +101,7 @@ function getDritanClient() {
65
101
  function getX402Client() {
66
102
  return new DritanClient({
67
103
  // x402 endpoints are public; SDK constructor still needs a string.
68
- apiKey: process.env.DRITAN_API_KEY ?? "x402_public_endpoints",
104
+ apiKey: getActiveApiKey() ?? "x402_public_endpoints",
69
105
  baseUrl: process.env.DRITAN_BASE_URL,
70
106
  controlBaseUrl: getControlBaseUrl(),
71
107
  wsBaseUrl: process.env.DRITAN_WS_BASE_URL,
@@ -82,7 +118,7 @@ async function searchTokens(client, query, options) {
82
118
  return await sdkSearch.call(client, query, options);
83
119
  }
84
120
  // Backward-compatible fallback for environments where dritan-sdk hasn't been upgraded yet.
85
- const apiKey = process.env.DRITAN_API_KEY;
121
+ const apiKey = getActiveApiKey();
86
122
  if (!apiKey) {
87
123
  throw missingApiKeyError();
88
124
  }
@@ -260,27 +296,35 @@ function buildCandlestickOhlcvChartUrl(mint, timeframe, bars, width, height) {
260
296
  const volumeSeries = bars.map((bar) => Number(bar.volume.toFixed(12)));
261
297
  const volumeColors = bars.map((bar) => bar.close >= bar.open ? "rgba(16,185,129,0.25)" : "rgba(239,68,68,0.25)");
262
298
  const config = {
263
- type: "candlestick",
299
+ type: "bar",
264
300
  data: {
265
301
  labels,
266
302
  datasets: [
267
303
  {
304
+ type: "bar",
305
+ label: "Volume",
306
+ data: volumeSeries,
307
+ backgroundColor: volumeColors,
308
+ borderWidth: 0,
309
+ yAxisID: "volume",
310
+ barPercentage: 0.9,
311
+ categoryPercentage: 1,
312
+ maxBarThickness: 14,
313
+ order: 1,
314
+ },
315
+ {
316
+ type: "candlestick",
268
317
  label: "OHLC",
269
318
  data: candles,
319
+ yAxisID: "price",
320
+ parsing: false,
321
+ order: 2,
270
322
  color: {
271
323
  up: "#10b981",
272
324
  down: "#ef4444",
273
325
  unchanged: "#94a3b8",
274
326
  },
275
327
  },
276
- {
277
- type: "bar",
278
- label: "Volume",
279
- data: volumeSeries,
280
- backgroundColor: volumeColors,
281
- borderWidth: 0,
282
- yAxisID: "volume",
283
- },
284
328
  ],
285
329
  },
286
330
  options: {
@@ -293,8 +337,13 @@ function buildCandlestickOhlcvChartUrl(mint, timeframe, bars, width, height) {
293
337
  },
294
338
  scales: {
295
339
  x: { type: "category" },
296
- y: { type: "linear", position: "left" },
297
- volume: { type: "linear", position: "right", grid: { drawOnChartArea: false } },
340
+ price: { type: "linear", position: "left" },
341
+ volume: {
342
+ type: "linear",
343
+ position: "right",
344
+ beginAtZero: true,
345
+ grid: { drawOnChartArea: false },
346
+ },
298
347
  },
299
348
  },
300
349
  };
@@ -412,6 +461,9 @@ const walletCreateSchema = z.object({
412
461
  name: z.string().min(1).default("agent-wallet"),
413
462
  walletDir: z.string().min(1).optional(),
414
463
  });
464
+ const authSetApiKeySchema = z.object({
465
+ apiKey: z.string().min(8),
466
+ });
415
467
  const walletPathSchema = z.object({
416
468
  walletPath: z.string().min(1),
417
469
  });
@@ -467,7 +519,7 @@ const tokenOhlcvSchema = z.object({
467
519
  timeTo: z.number().int().positive().optional(),
468
520
  });
469
521
  const tokenOhlcvChartSchema = tokenOhlcvSchema.extend({
470
- chartType: z.enum(["line-volume", "candlestick"]).default("line-volume"),
522
+ chartType: z.enum(["line-volume", "candlestick"]).default("candlestick"),
471
523
  includeActive: z.boolean().default(true),
472
524
  maxPoints: z.number().int().min(10).max(500).default(120),
473
525
  width: z.number().int().min(300).max(2000).default(1200),
@@ -531,6 +583,25 @@ const tools = [
531
583
  properties: {},
532
584
  },
533
585
  },
586
+ {
587
+ name: "auth_status",
588
+ description: "Show current API key status for this MCP session (active source, preview, and onboarding options).",
589
+ inputSchema: {
590
+ type: "object",
591
+ properties: {},
592
+ },
593
+ },
594
+ {
595
+ name: "auth_set_api_key",
596
+ description: "Set the active Dritan API key for this running MCP process without restart (runtime session scope).",
597
+ inputSchema: {
598
+ type: "object",
599
+ required: ["apiKey"],
600
+ properties: {
601
+ apiKey: { type: "string" },
602
+ },
603
+ },
604
+ },
534
605
  {
535
606
  name: "wallet_create_local",
536
607
  description: "Create a local Solana wallet using solana-keygen and return path + public address.",
@@ -746,7 +817,7 @@ const tools = [
746
817
  chartType: {
747
818
  type: "string",
748
819
  enum: ["line-volume", "candlestick"],
749
- description: "Chart style. Default line-volume.",
820
+ description: "Chart style. Default candlestick.",
750
821
  },
751
822
  includeActive: { type: "boolean" },
752
823
  maxPoints: { type: "number" },
@@ -976,7 +1047,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
976
1047
  switch (request.params.name) {
977
1048
  case "system_check_prereqs": {
978
1049
  const solanaCli = checkSolanaCli();
979
- const apiKeySet = !!process.env.DRITAN_API_KEY;
1050
+ const activeApiKey = getActiveApiKey();
1051
+ const apiKeySet = !!activeApiKey;
980
1052
  return ok({
981
1053
  ready: solanaCli.ok && apiKeySet,
982
1054
  readyForX402Onboarding: solanaCli.ok,
@@ -985,6 +1057,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
985
1057
  {
986
1058
  ok: apiKeySet,
987
1059
  name: "DRITAN_API_KEY",
1060
+ source: runtimeApiKeySource,
1061
+ preview: apiKeyPreview(activeApiKey),
988
1062
  hint: apiKeySet
989
1063
  ? "API key is configured."
990
1064
  : "Missing DRITAN_API_KEY. You can either use x402 onboarding tools or get a free key at https://dritan.dev.",
@@ -997,6 +1071,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
997
1071
  : "Environment ready.",
998
1072
  });
999
1073
  }
1074
+ case "auth_status": {
1075
+ const activeApiKey = getActiveApiKey();
1076
+ return ok({
1077
+ apiKeyConfigured: !!activeApiKey,
1078
+ source: runtimeApiKeySource,
1079
+ preview: apiKeyPreview(activeApiKey),
1080
+ controlBaseUrl: getControlBaseUrl(),
1081
+ onboardingOptions: [
1082
+ "Option 1 (paid x402): create wallet -> receive user SOL -> x402 quote -> transfer -> claim key.",
1083
+ "Option 2 (free): create key at https://dritan.dev and set it with auth_set_api_key.",
1084
+ ],
1085
+ });
1086
+ }
1087
+ case "auth_set_api_key": {
1088
+ const input = authSetApiKeySchema.parse(args);
1089
+ const activated = setRuntimeApiKey(input.apiKey, "runtime");
1090
+ return ok({
1091
+ ok: true,
1092
+ message: "API key activated for this MCP session without restart.",
1093
+ source: runtimeApiKeySource,
1094
+ preview: apiKeyPreview(activated),
1095
+ });
1096
+ }
1000
1097
  case "dritan_health": {
1001
1098
  return ok(await checkDritanHealth());
1002
1099
  }
@@ -1090,7 +1187,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1090
1187
  "If user picked paid flow, ensure agent has a local wallet (wallet_create_local + wallet_get_address).",
1091
1188
  "User funds the agent wallet.",
1092
1189
  "Transfer quoted SOL amount to receiver wallet using wallet_transfer_sol.",
1093
- "Claim key with x402_create_api_key using returned tx signature.",
1190
+ "Claim key with x402_create_api_key using returned tx signature (MCP auto-activates returned apiKey).",
1094
1191
  ],
1095
1192
  });
1096
1193
  }
@@ -1098,7 +1195,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1098
1195
  const input = x402CreateApiKeySchema.parse(args);
1099
1196
  const client = getX402Client();
1100
1197
  const created = await x402CreateApiKey(client, input);
1101
- return ok(created);
1198
+ const payload = typeof created === "object" && created !== null
1199
+ ? { ...created }
1200
+ : { value: created };
1201
+ if (typeof payload.apiKey === "string") {
1202
+ const activated = setRuntimeApiKey(payload.apiKey, "x402");
1203
+ payload.mcpAuth = {
1204
+ activated: true,
1205
+ source: runtimeApiKeySource,
1206
+ preview: apiKeyPreview(activated),
1207
+ message: "x402-created API key is active for this MCP session (no restart needed).",
1208
+ };
1209
+ }
1210
+ return ok(payload);
1102
1211
  }
1103
1212
  case "market_get_snapshot": {
1104
1213
  const input = marketSnapshotSchema.parse(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dritan/mcp",
3
- "version": "0.5.1",
3
+ "version": "0.6.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",