@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.
- package/README.md +5 -1
- package/dist/index.js +129 -20
- 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 `
|
|
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
|
|
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 =
|
|
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:
|
|
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 =
|
|
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: "
|
|
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
|
-
|
|
297
|
-
volume: {
|
|
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("
|
|
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
|
|
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
|
|
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
|
-
|
|
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);
|