@dimcool/mcp 0.1.0 → 0.1.2

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 +49 -7
  2. package/dist/index.js +164 -27
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -21,7 +21,7 @@ Add to your MCP config (`claude_desktop_config.json` or `.cursor/mcp.json`):
21
21
  "command": "npx",
22
22
  "args": ["@dimcool/mcp"],
23
23
  "env": {
24
- "DIM_WALLET_PRIVATE_KEY": "your-base58-solana-private-key",
24
+ "DIM_WALLET_STORE_PATH": "/absolute/path/to/mcp-wallet.json",
25
25
  "DIM_API_URL": "https://api.dim.cool"
26
26
  }
27
27
  }
@@ -29,20 +29,62 @@ Add to your MCP config (`claude_desktop_config.json` or `.cursor/mcp.json`):
29
29
  }
30
30
  ```
31
31
 
32
+ ### Bootstrap a wallet locally (recommended)
33
+
34
+ Create a local non-custodial wallet file (private key never leaves your machine):
35
+
36
+ ```bash
37
+ npx @dimcool/mcp init-wallet
38
+ ```
39
+
40
+ This prints:
41
+
42
+ - your public Solana address
43
+ - the local wallet store path
44
+ - the env snippet to add to your MCP config
45
+
46
+ ### Bring your own key (advanced)
47
+
48
+ If you already have a wallet:
49
+
50
+ ```bash
51
+ DIM_WALLET_PRIVATE_KEY=<base58-key> npx @dimcool/mcp
52
+ ```
53
+
32
54
  ### Environment Variables
33
55
 
34
- | Variable | Required | Description |
35
- | ------------------------ | -------- | ---------------------------------------------- |
36
- | `DIM_WALLET_PRIVATE_KEY` | Yes | Base58-encoded Solana private key |
37
- | `DIM_API_URL` | No | API base URL (default: `https://api.dim.cool`) |
38
- | `DIM_REFERRAL_CODE` | No | Referral code to use on first signup |
56
+ | Variable | Required | Description |
57
+ | ------------------------ | -------- | ------------------------------------------------------------------- |
58
+ | `DIM_WALLET_PRIVATE_KEY` | No\* | Base58-encoded Solana private key |
59
+ | `DIM_WALLET_STORE_PATH` | No | Local path to wallet store file (default: `~/.dim/mcp-wallet.json`) |
60
+ | `DIM_WALLET_AUTO_CREATE` | No | If `true`, auto-creates/stores a wallet when key/store is missing |
61
+ | `DIM_API_URL` | No | API base URL (default: `https://api.dim.cool`) |
62
+ | `DIM_REFERRAL_CODE` | No | Referral code to use on first signup |
63
+
64
+ \*Required if you do not use wallet store or auto-create.
39
65
 
40
66
  ### Wallet Auth Configuration
41
67
 
42
- - `@dimcool/mcp` uses `DIM_WALLET_PRIVATE_KEY` directly for wallet-based login and signing.
68
+ - `@dimcool/mcp` supports three wallet startup modes:
69
+ 1. direct key (`DIM_WALLET_PRIVATE_KEY`)
70
+ 2. local wallet store (`DIM_WALLET_STORE_PATH`)
71
+ 3. auto-create (`DIM_WALLET_AUTO_CREATE=true`)
43
72
  - Call `dim_login` before any wallet, games, chat, social, referrals, support, or market tools.
73
+ - Call `dim_get_balance` after login and before paid actions.
44
74
  - To generate/export a Base58 key programmatically, use [@dimcool/wallet](https://docs.dim.cool/guides/wallet-package).
45
75
 
76
+ ### Funding your wallet
77
+
78
+ - Send **USDC on Solana** to the wallet public address shown during bootstrap/import.
79
+ - Most paid actions (transfers, game bets, market buys) require USDC balance.
80
+ - If balance is low, agents should ask users to fund first, then retry.
81
+ - You can still start referral growth without pre-funding: onboarding referred users who play can earn rewards.
82
+
83
+ ### Skill templates for agent platforms
84
+
85
+ - OpenClaw starter skill: `skills/openclaw-dim/SKILL.md`
86
+ - Hermes starter skill: `skills/hermes-dim/SKILL.md`
87
+
46
88
  ## Available Tools
47
89
 
48
90
  ### Authentication
package/dist/index.js CHANGED
@@ -7,7 +7,19 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
7
7
  });
8
8
 
9
9
  // src/index.ts
10
+ import {
11
+ chmod,
12
+ mkdir,
13
+ readFile,
14
+ rename,
15
+ stat,
16
+ writeFile
17
+ } from "fs/promises";
18
+ import os from "os";
19
+ import path from "path";
10
20
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
+ import { Keypair as Keypair3 } from "@solana/web3.js";
22
+ import bs582 from "bs58";
11
23
 
12
24
  // src/server.ts
13
25
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -6489,14 +6501,14 @@ var require_url_state_machine = __commonJS({
6489
6501
  return url2.replace(/\u0009|\u000A|\u000D/g, "");
6490
6502
  }
6491
6503
  function shortenPath(url2) {
6492
- const path = url2.path;
6493
- if (path.length === 0) {
6504
+ const path2 = url2.path;
6505
+ if (path2.length === 0) {
6494
6506
  return;
6495
6507
  }
6496
- if (url2.scheme === "file" && path.length === 1 && isNormalizedWindowsDriveLetter(path[0])) {
6508
+ if (url2.scheme === "file" && path2.length === 1 && isNormalizedWindowsDriveLetter(path2[0])) {
6497
6509
  return;
6498
6510
  }
6499
- path.pop();
6511
+ path2.pop();
6500
6512
  }
6501
6513
  function includesCredentials(url2) {
6502
6514
  return url2.username !== "" || url2.password !== "";
@@ -7543,15 +7555,15 @@ var require_node_gyp_build = __commonJS({
7543
7555
  "../../node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
7544
7556
  "use strict";
7545
7557
  var fs = __require2("fs");
7546
- var path = __require2("path");
7547
- var os = __require2("os");
7558
+ var path2 = __require2("path");
7559
+ var os2 = __require2("os");
7548
7560
  var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require2;
7549
7561
  var vars = process.config && process.config.variables || {};
7550
7562
  var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
7551
7563
  var abi = process.versions.modules;
7552
7564
  var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
7553
- var arch = process.env.npm_config_arch || os.arch();
7554
- var platform = process.env.npm_config_platform || os.platform();
7565
+ var arch = process.env.npm_config_arch || os2.arch();
7566
+ var platform = process.env.npm_config_platform || os2.platform();
7555
7567
  var libc = process.env.LIBC || (isAlpine(platform) ? "musl" : "glibc");
7556
7568
  var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
7557
7569
  var uv = (process.versions.uv || "").split(".")[0];
@@ -7560,21 +7572,21 @@ var require_node_gyp_build = __commonJS({
7560
7572
  return runtimeRequire(load.resolve(dir));
7561
7573
  }
7562
7574
  load.resolve = load.path = function(dir) {
7563
- dir = path.resolve(dir || ".");
7575
+ dir = path2.resolve(dir || ".");
7564
7576
  try {
7565
- var name = runtimeRequire(path.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
7577
+ var name = runtimeRequire(path2.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
7566
7578
  if (process.env[name + "_PREBUILD"]) dir = process.env[name + "_PREBUILD"];
7567
7579
  } catch (err) {
7568
7580
  }
7569
7581
  if (!prebuildsOnly) {
7570
- var release = getFirst(path.join(dir, "build/Release"), matchBuild);
7582
+ var release = getFirst(path2.join(dir, "build/Release"), matchBuild);
7571
7583
  if (release) return release;
7572
- var debug = getFirst(path.join(dir, "build/Debug"), matchBuild);
7584
+ var debug = getFirst(path2.join(dir, "build/Debug"), matchBuild);
7573
7585
  if (debug) return debug;
7574
7586
  }
7575
7587
  var prebuild = resolve(dir);
7576
7588
  if (prebuild) return prebuild;
7577
- var nearby = resolve(path.dirname(process.execPath));
7589
+ var nearby = resolve(path2.dirname(process.execPath));
7578
7590
  if (nearby) return nearby;
7579
7591
  var target = [
7580
7592
  "platform=" + platform,
@@ -7591,14 +7603,14 @@ var require_node_gyp_build = __commonJS({
7591
7603
  ].filter(Boolean).join(" ");
7592
7604
  throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
7593
7605
  function resolve(dir2) {
7594
- var tuples = readdirSync(path.join(dir2, "prebuilds")).map(parseTuple);
7606
+ var tuples = readdirSync(path2.join(dir2, "prebuilds")).map(parseTuple);
7595
7607
  var tuple2 = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
7596
7608
  if (!tuple2) return;
7597
- var prebuilds = path.join(dir2, "prebuilds", tuple2.name);
7609
+ var prebuilds = path2.join(dir2, "prebuilds", tuple2.name);
7598
7610
  var parsed = readdirSync(prebuilds).map(parseTags);
7599
7611
  var candidates = parsed.filter(matchTags(runtime, abi));
7600
7612
  var winner = candidates.sort(compareTags(runtime))[0];
7601
- if (winner) return path.join(prebuilds, winner.file);
7613
+ if (winner) return path2.join(prebuilds, winner.file);
7602
7614
  }
7603
7615
  };
7604
7616
  function readdirSync(dir) {
@@ -7610,7 +7622,7 @@ var require_node_gyp_build = __commonJS({
7610
7622
  }
7611
7623
  function getFirst(dir, filter) {
7612
7624
  var files = readdirSync(dir).filter(filter);
7613
- return files[0] && path.join(dir, files[0]);
7625
+ return files[0] && path2.join(dir, files[0]);
7614
7626
  }
7615
7627
  function matchBuild(name) {
7616
7628
  return /\.node$/.test(name);
@@ -15332,8 +15344,8 @@ var StructError = class extends TypeError {
15332
15344
  constructor(failure, failures) {
15333
15345
  let cached;
15334
15346
  const { message, explanation, ...rest } = failure;
15335
- const { path } = failure;
15336
- const msg = path.length === 0 ? message : `At path: ${path.join(".")} -- ${message}`;
15347
+ const { path: path2 } = failure;
15348
+ const msg = path2.length === 0 ? message : `At path: ${path2.join(".")} -- ${message}`;
15337
15349
  super(explanation ?? msg);
15338
15350
  if (explanation != null)
15339
15351
  this.cause = msg;
@@ -15371,15 +15383,15 @@ function toFailure(result, context, struct2, value) {
15371
15383
  } else if (typeof result === "string") {
15372
15384
  result = { message: result };
15373
15385
  }
15374
- const { path, branch } = context;
15386
+ const { path: path2, branch } = context;
15375
15387
  const { type: type2 } = struct2;
15376
15388
  const { refinement, message = `Expected a value of type \`${type2}\`${refinement ? ` with refinement \`${refinement}\`` : ""}, but received: \`${print(value)}\`` } = result;
15377
15389
  return {
15378
15390
  value,
15379
15391
  type: type2,
15380
15392
  refinement,
15381
- key: path[path.length - 1],
15382
- path,
15393
+ key: path2[path2.length - 1],
15394
+ path: path2,
15383
15395
  branch,
15384
15396
  ...result,
15385
15397
  message
@@ -15397,8 +15409,8 @@ function* toFailures(result, context, struct2, value) {
15397
15409
  }
15398
15410
  }
15399
15411
  function* run(value, struct2, options = {}) {
15400
- const { path = [], branch = [value], coerce: coerce2 = false, mask: mask2 = false } = options;
15401
- const ctx = { path, branch, mask: mask2 };
15412
+ const { path: path2 = [], branch = [value], coerce: coerce2 = false, mask: mask2 = false } = options;
15413
+ const ctx = { path: path2, branch, mask: mask2 };
15402
15414
  if (coerce2) {
15403
15415
  value = struct2.coercer(value, ctx);
15404
15416
  }
@@ -15410,7 +15422,7 @@ function* run(value, struct2, options = {}) {
15410
15422
  }
15411
15423
  for (let [k, v, s] of struct2.entries(value, ctx)) {
15412
15424
  const ts = run(v, s, {
15413
- path: k === void 0 ? path : [...path, k],
15425
+ path: k === void 0 ? path2 : [...path2, k],
15414
15426
  branch: k === void 0 ? branch : [...branch, v],
15415
15427
  coerce: coerce2,
15416
15428
  mask: mask2,
@@ -26838,10 +26850,135 @@ function createDimMcpServer(config) {
26838
26850
  }
26839
26851
 
26840
26852
  // src/index.ts
26841
- var walletPrivateKey = process.env.DIM_WALLET_PRIVATE_KEY;
26853
+ var DEFAULT_WALLET_STORE_PATH = path.join(
26854
+ os.homedir(),
26855
+ ".dim",
26856
+ "mcp-wallet.json"
26857
+ );
26858
+ function parseBooleanFlag(value) {
26859
+ if (!value) return false;
26860
+ return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
26861
+ }
26862
+ function resolveWalletStorePath(cliArgs2) {
26863
+ const storeFlagIndex = cliArgs2.indexOf("--store");
26864
+ if (storeFlagIndex !== -1) {
26865
+ const value = cliArgs2[storeFlagIndex + 1];
26866
+ if (!value || value.startsWith("--")) {
26867
+ throw new Error(
26868
+ "Missing value for --store. Example: npx @dimcool/mcp init-wallet --store ~/.dim/mcp-wallet.json"
26869
+ );
26870
+ }
26871
+ if (value.startsWith("~/")) {
26872
+ return path.join(os.homedir(), value.slice(2));
26873
+ }
26874
+ return path.resolve(value);
26875
+ }
26876
+ const envPath = process.env.DIM_WALLET_STORE_PATH?.trim();
26877
+ if (envPath) {
26878
+ if (envPath.startsWith("~/")) {
26879
+ return path.join(os.homedir(), envPath.slice(2));
26880
+ }
26881
+ return path.resolve(envPath);
26882
+ }
26883
+ return DEFAULT_WALLET_STORE_PATH;
26884
+ }
26885
+ async function writeWalletStoreFile(storePath, record2) {
26886
+ await mkdir(path.dirname(storePath), { recursive: true });
26887
+ const tmpPath = `${storePath}.tmp`;
26888
+ await writeFile(tmpPath, `${JSON.stringify(record2, null, 2)}
26889
+ `, {
26890
+ encoding: "utf8",
26891
+ mode: 384
26892
+ });
26893
+ await chmod(tmpPath, 384);
26894
+ await rename(tmpPath, storePath);
26895
+ }
26896
+ async function readWalletStoreFile(storePath) {
26897
+ try {
26898
+ const raw = await readFile(storePath, "utf8");
26899
+ const parsed = JSON.parse(raw);
26900
+ if (parsed.walletPrivateKey && parsed.walletAddress) {
26901
+ return {
26902
+ version: 1,
26903
+ walletAddress: parsed.walletAddress,
26904
+ walletPrivateKey: parsed.walletPrivateKey,
26905
+ createdAt: parsed.createdAt || (/* @__PURE__ */ new Date()).toISOString()
26906
+ };
26907
+ }
26908
+ return null;
26909
+ } catch {
26910
+ return null;
26911
+ }
26912
+ }
26913
+ async function createWalletRecord() {
26914
+ const keypair = Keypair3.generate();
26915
+ return {
26916
+ version: 1,
26917
+ walletAddress: keypair.publicKey.toBase58(),
26918
+ walletPrivateKey: bs582.encode(keypair.secretKey),
26919
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
26920
+ };
26921
+ }
26922
+ async function walletStoreExists(storePath) {
26923
+ try {
26924
+ await stat(storePath);
26925
+ return true;
26926
+ } catch {
26927
+ return false;
26928
+ }
26929
+ }
26930
+ async function handleInitWallet(cliArgs2) {
26931
+ const force = cliArgs2.includes("--force");
26932
+ const storePath = resolveWalletStorePath(cliArgs2);
26933
+ const exists = await walletStoreExists(storePath);
26934
+ if (exists && !force) {
26935
+ console.error(
26936
+ `Wallet store already exists at ${storePath}.
26937
+ Use --force to overwrite, or remove the file first.`
26938
+ );
26939
+ process.exit(1);
26940
+ }
26941
+ const record2 = await createWalletRecord();
26942
+ await writeWalletStoreFile(storePath, record2);
26943
+ console.log("DIM wallet created and stored locally.");
26944
+ console.log(`Public address: ${record2.walletAddress}`);
26945
+ console.log(`Store path: ${storePath}`);
26946
+ console.log("");
26947
+ console.log("Add this to your MCP env config:");
26948
+ console.log(` DIM_WALLET_STORE_PATH="${storePath}"`);
26949
+ console.log(' DIM_API_URL="https://api.dim.cool"');
26950
+ console.log("");
26951
+ console.log(
26952
+ "Next step: fund this wallet with USDC on Solana, then call dim_login and dim_get_balance."
26953
+ );
26954
+ }
26955
+ async function resolveWalletPrivateKey(cliArgs2) {
26956
+ const direct = process.env.DIM_WALLET_PRIVATE_KEY?.trim();
26957
+ if (direct) return direct;
26958
+ const storePath = resolveWalletStorePath(cliArgs2);
26959
+ const record2 = await readWalletStoreFile(storePath);
26960
+ if (record2?.walletPrivateKey) return record2.walletPrivateKey;
26961
+ if (parseBooleanFlag(process.env.DIM_WALLET_AUTO_CREATE)) {
26962
+ const newRecord = await createWalletRecord();
26963
+ await writeWalletStoreFile(storePath, newRecord);
26964
+ console.error(
26965
+ `DIM wallet auto-created. Public address: ${newRecord.walletAddress}
26966
+ Stored at: ${storePath}
26967
+ Fund this wallet with USDC on Solana before paid actions.`
26968
+ );
26969
+ return newRecord.walletPrivateKey;
26970
+ }
26971
+ return null;
26972
+ }
26973
+ var cliArgs = process.argv.slice(2);
26974
+ if (cliArgs[0] === "init-wallet") {
26975
+ await handleInitWallet(cliArgs.slice(1));
26976
+ process.exit(0);
26977
+ }
26978
+ var walletPrivateKey = await resolveWalletPrivateKey(cliArgs);
26842
26979
  if (!walletPrivateKey) {
26843
26980
  console.error(
26844
- "Error: DIM_WALLET_PRIVATE_KEY environment variable is required.\nSet it to your Base58-encoded Solana private key.\n\nExample:\n DIM_WALLET_PRIVATE_KEY=your-key npx @dimcool/mcp"
26981
+ "Error: DIM wallet key is not configured.\nProvide DIM_WALLET_PRIVATE_KEY, or initialize a local wallet store.\n\nExamples:\n DIM_WALLET_PRIVATE_KEY=your-base58-key npx @dimcool/mcp\n npx @dimcool/mcp init-wallet\n DIM_WALLET_AUTO_CREATE=true npx @dimcool/mcp"
26845
26982
  );
26846
26983
  process.exit(1);
26847
26984
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dimcool/mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server for DIM — lets AI agents play games, chat, send USDC, and earn referral income on the DIM platform",
5
5
  "type": "module",
6
6
  "bin": {