@zerodust/mcp-server 0.2.1 → 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/dist/signer.js ADDED
@@ -0,0 +1,270 @@
1
+ /**
2
+ * @fileoverview Signer resolution for the ZeroDust MCP server
3
+ *
4
+ * Sweeping requires a key that can produce an EIP-712 signature and two
5
+ * EIP-7702 authorizations. The original server accepted exactly one form of
6
+ * that key: a raw hex private key in an environment variable, which in practice
7
+ * means pasting a hot key into an MCP client's JSON config. Operators running
8
+ * agents with real balances decline to do that, which is precisely the
9
+ * population with the most stranded gas.
10
+ *
11
+ * So there are four ways in, in descending order of precedence:
12
+ *
13
+ * ZERODUST_SIGNER_MODULE a module that returns a viem LocalAccount
14
+ * ZERODUST_KEYSTORE_FILE an encrypted V3 keystore + password
15
+ * ZERODUST_PRIVATE_KEY_FILE a file containing a hex key
16
+ * ZERODUST_PRIVATE_KEY a hex key inline (unchanged, still supported)
17
+ *
18
+ * The module hook is the important one. Rather than depending on Turnkey,
19
+ * Privy, AWS KMS and every future custody vendor, this server loads any module
20
+ * that hands back a viem `LocalAccount` — which every one of those vendors
21
+ * already knows how to produce. ZeroDust stays out of the custody business and
22
+ * the integration surface stops growing.
23
+ */
24
+ import { readFile } from "node:fs/promises";
25
+ import { createDecipheriv, pbkdf2 as pbkdf2Cb, scrypt as scryptCb, timingSafeEqual, } from "node:crypto";
26
+ import { pathToFileURL } from "node:url";
27
+ import { isAbsolute, resolve as resolvePath } from "node:path";
28
+ import { keccak256 } from "viem";
29
+ // Hand-rolled rather than promisify()'d: both functions are overloaded, and
30
+ // promisify resolves to the shortest overload, which drops the options argument
31
+ // scrypt needs for its memory limit.
32
+ function scrypt(secret, salt, keylen, options) {
33
+ return new Promise((resolve, reject) => {
34
+ scryptCb(secret, salt, keylen, options, (error, key) => error ? reject(error) : resolve(key));
35
+ });
36
+ }
37
+ function pbkdf2(secret, salt, iterations, keylen, digest) {
38
+ return new Promise((resolve, reject) => {
39
+ pbkdf2Cb(secret, salt, iterations, keylen, digest, (error, key) => error ? reject(error) : resolve(key));
40
+ });
41
+ }
42
+ const PRIVATE_KEY_RE = /^0x[a-fA-F0-9]{64}$/;
43
+ /** Env var names that supply a key, in precedence order. */
44
+ const SIGNER_ENV_VARS = [
45
+ "ZERODUST_SIGNER_MODULE",
46
+ "ZERODUST_KEYSTORE_FILE",
47
+ "ZERODUST_PRIVATE_KEY_FILE",
48
+ "ZERODUST_PRIVATE_KEY",
49
+ ];
50
+ /** True when any signer variable is present, used to detect a partial setup. */
51
+ export function hasSignerEnv(env) {
52
+ return SIGNER_ENV_VARS.some((name) => Boolean(env[name]?.trim()));
53
+ }
54
+ /**
55
+ * Picks the signer source from the environment.
56
+ *
57
+ * @returns the source, or null when no signer variable is set
58
+ * @throws when more than one is set, or when the chosen one is incomplete
59
+ */
60
+ export function resolveSignerSource(env = process.env) {
61
+ const present = SIGNER_ENV_VARS.filter((name) => Boolean(env[name]?.trim()));
62
+ if (present.length === 0)
63
+ return null;
64
+ if (present.length > 1) {
65
+ throw new Error(`Multiple signing keys configured (${present.join(", ")}). ` +
66
+ "Set exactly one so it is unambiguous which key signs.");
67
+ }
68
+ const [chosen] = present;
69
+ switch (chosen) {
70
+ case "ZERODUST_SIGNER_MODULE":
71
+ return moduleSource(env.ZERODUST_SIGNER_MODULE.trim());
72
+ case "ZERODUST_KEYSTORE_FILE":
73
+ return keystoreSource(env);
74
+ case "ZERODUST_PRIVATE_KEY_FILE":
75
+ return keyFileSource(env.ZERODUST_PRIVATE_KEY_FILE.trim());
76
+ default:
77
+ return keyEnvSource(env.ZERODUST_PRIVATE_KEY.trim());
78
+ }
79
+ }
80
+ // ============ Sources ============
81
+ /**
82
+ * Loads a caller-supplied module and uses whatever account it returns.
83
+ *
84
+ * Accepted shapes, in order: a default export that is a function, a named
85
+ * `createAccount` function, or a default export that is already an account.
86
+ * Functions may be async, which is what lets a remote signer do a round trip
87
+ * (fetch a Turnkey/Privy session, resolve a KMS key) before returning.
88
+ */
89
+ function moduleSource(specifier) {
90
+ return {
91
+ kind: "module",
92
+ description: `signer module ${specifier}`,
93
+ load: async () => {
94
+ // Relative paths resolve against cwd, not against this file, because the
95
+ // operator writes them relative to where they launch the server.
96
+ const target = specifier.startsWith(".") || isAbsolute(specifier)
97
+ ? pathToFileURL(resolvePath(process.cwd(), specifier)).href
98
+ : specifier;
99
+ let mod;
100
+ try {
101
+ mod = (await import(target));
102
+ }
103
+ catch (error) {
104
+ throw new Error(`ZERODUST_SIGNER_MODULE could not be loaded (${specifier}): ` +
105
+ `${error instanceof Error ? error.message : String(error)}`);
106
+ }
107
+ const factory = mod.default ?? mod.createAccount;
108
+ const candidate = typeof factory === "function" ? await factory() : factory;
109
+ return assertLocalAccount(candidate, specifier);
110
+ },
111
+ };
112
+ }
113
+ /**
114
+ * Decrypts a Web3 Secret Storage (V3) keystore — the format produced by
115
+ * `cast wallet import`, geth, and most key management tooling. The password can
116
+ * come from a variable or, better, from a file that never enters the MCP config.
117
+ */
118
+ function keystoreSource(env) {
119
+ const file = env.ZERODUST_KEYSTORE_FILE.trim();
120
+ const inlinePassword = env.ZERODUST_KEYSTORE_PASSWORD;
121
+ const passwordFile = env.ZERODUST_KEYSTORE_PASSWORD_FILE?.trim();
122
+ if (inlinePassword === undefined && !passwordFile) {
123
+ throw new Error("ZERODUST_KEYSTORE_FILE is set but no password was provided. " +
124
+ "Set ZERODUST_KEYSTORE_PASSWORD_FILE (preferred) or ZERODUST_KEYSTORE_PASSWORD.");
125
+ }
126
+ if (inlinePassword !== undefined && passwordFile) {
127
+ throw new Error("Both ZERODUST_KEYSTORE_PASSWORD and ZERODUST_KEYSTORE_PASSWORD_FILE are set. Choose one.");
128
+ }
129
+ return {
130
+ kind: "keystore",
131
+ description: `keystore ${file}`,
132
+ load: async () => {
133
+ const password = passwordFile
134
+ ? // Trailing newlines are near-universal in password files and are
135
+ // essentially never part of the password.
136
+ (await readFileText(passwordFile, "ZERODUST_KEYSTORE_PASSWORD_FILE")).replace(/\r?\n$/, "")
137
+ : inlinePassword;
138
+ const raw = await readFileText(file, "ZERODUST_KEYSTORE_FILE");
139
+ const privateKey = await decryptV3Keystore(raw, password);
140
+ return toAccount(privateKey);
141
+ },
142
+ };
143
+ }
144
+ /** Reads a hex private key from a file, so it never appears in a config file. */
145
+ function keyFileSource(file) {
146
+ return {
147
+ kind: "key-file",
148
+ description: `key file ${file}`,
149
+ load: async () => {
150
+ const contents = (await readFileText(file, "ZERODUST_PRIVATE_KEY_FILE")).trim();
151
+ return toAccount(normalizeKey(contents, `the contents of ${file}`));
152
+ },
153
+ };
154
+ }
155
+ function keyEnvSource(value) {
156
+ return {
157
+ kind: "key-env",
158
+ description: "ZERODUST_PRIVATE_KEY (inline)",
159
+ load: async () => toAccount(normalizeKey(value, "ZERODUST_PRIVATE_KEY")),
160
+ };
161
+ }
162
+ // ============ Helpers ============
163
+ async function readFileText(file, label) {
164
+ try {
165
+ return await readFile(file, "utf8");
166
+ }
167
+ catch (error) {
168
+ throw new Error(`${label} could not be read (${file}): ` +
169
+ `${error instanceof Error ? error.message : String(error)}`);
170
+ }
171
+ }
172
+ function normalizeKey(value, label) {
173
+ const candidate = (value.startsWith("0x") ? value : `0x${value}`);
174
+ if (!PRIVATE_KEY_RE.test(candidate)) {
175
+ throw new Error(`${label} must be a 32-byte hex private key.`);
176
+ }
177
+ return candidate;
178
+ }
179
+ async function toAccount(privateKey) {
180
+ const { privateKeyToAccount } = await import("viem/accounts");
181
+ return privateKeyToAccount(privateKey);
182
+ }
183
+ /**
184
+ * Verifies a module returned something that can actually sign a sweep.
185
+ *
186
+ * `signAuthorization` is the load-bearing one: without it the EIP-7702
187
+ * delegation cannot be produced, and a signer missing it would otherwise fail
188
+ * deep inside a sweep instead of at startup.
189
+ */
190
+ function assertLocalAccount(candidate, specifier) {
191
+ const account = candidate;
192
+ if (!account || typeof account !== "object") {
193
+ throw new Error(`ZERODUST_SIGNER_MODULE (${specifier}) did not return an account. ` +
194
+ "Export a default function returning a viem LocalAccount.");
195
+ }
196
+ const missing = ["address", "signTypedData", "signAuthorization"].filter((key) => {
197
+ const value = account[key];
198
+ return key === "address" ? typeof value !== "string" : typeof value !== "function";
199
+ });
200
+ if (missing.length > 0) {
201
+ throw new Error(`ZERODUST_SIGNER_MODULE (${specifier}) returned an object missing ${missing.join(", ")}. ` +
202
+ "ZeroDust needs a viem LocalAccount that can sign EIP-712 typed data and " +
203
+ "EIP-7702 authorizations.");
204
+ }
205
+ return account;
206
+ }
207
+ /**
208
+ * Decrypts a V3 keystore to its private key.
209
+ *
210
+ * Implemented directly on node:crypto rather than pulling in a keystore
211
+ * library, because the whole format is one KDF, one AES-CTR decryption, and one
212
+ * keccak MAC check, and this server's dependency surface is worth keeping thin.
213
+ */
214
+ async function decryptV3Keystore(raw, password) {
215
+ let parsed;
216
+ try {
217
+ parsed = JSON.parse(raw);
218
+ }
219
+ catch {
220
+ throw new Error("Keystore file is not valid JSON.");
221
+ }
222
+ // Older tooling capitalises the key.
223
+ const crypto = parsed.crypto ?? parsed.Crypto;
224
+ if (!crypto)
225
+ throw new Error("Keystore file has no \"crypto\" section.");
226
+ if (parsed.version !== 3) {
227
+ throw new Error(`Unsupported keystore version ${parsed.version}. Only V3 is supported.`);
228
+ }
229
+ if (crypto.cipher !== "aes-128-ctr") {
230
+ throw new Error(`Unsupported keystore cipher "${crypto.cipher}". Expected aes-128-ctr.`);
231
+ }
232
+ const derived = await deriveKey(crypto, password);
233
+ const ciphertext = Buffer.from(crypto.ciphertext, "hex");
234
+ // MAC covers the second half of the derived key plus the ciphertext. A
235
+ // mismatch means the password is wrong (or the file was tampered with).
236
+ const mac = keccak256(Buffer.concat([derived.subarray(16, 32), ciphertext])).slice(2);
237
+ const expected = Buffer.from(crypto.mac.replace(/^0x/, ""), "hex");
238
+ const actual = Buffer.from(mac, "hex");
239
+ if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) {
240
+ throw new Error("Keystore MAC mismatch - the password is incorrect.");
241
+ }
242
+ const decipher = createDecipheriv("aes-128-ctr", derived.subarray(0, 16), Buffer.from(crypto.cipherparams.iv, "hex"));
243
+ const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
244
+ return normalizeKey(plaintext.toString("hex"), "The decrypted keystore key");
245
+ }
246
+ async function deriveKey(crypto, password) {
247
+ const params = crypto.kdfparams;
248
+ const salt = Buffer.from(String(params.salt), "hex");
249
+ const dklen = Number(params.dklen ?? 32);
250
+ const secret = Buffer.from(password, "utf8");
251
+ if (crypto.kdf === "scrypt") {
252
+ const N = Number(params.n);
253
+ const r = Number(params.r);
254
+ const p = Number(params.p);
255
+ // Node caps scrypt memory at 32MB by default, which the standard geth
256
+ // parameters (N=262144, r=8) blow straight through. Size it from the
257
+ // parameters instead of failing with an opaque error.
258
+ const maxmem = 256 * N * r;
259
+ return scrypt(secret, salt, dklen, { N, r, p, maxmem });
260
+ }
261
+ if (crypto.kdf === "pbkdf2") {
262
+ const prf = String(params.prf ?? "hmac-sha256");
263
+ if (prf !== "hmac-sha256") {
264
+ throw new Error(`Unsupported keystore PRF "${prf}". Expected hmac-sha256.`);
265
+ }
266
+ return pbkdf2(secret, salt, Number(params.c), dklen, "sha256");
267
+ }
268
+ throw new Error(`Unsupported keystore KDF "${crypto.kdf}". Expected scrypt or pbkdf2.`);
269
+ }
270
+ //# sourceMappingURL=signer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signer.js","sourceRoot":"","sources":["../src/signer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zerodust/mcp-server",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "mcpName": "io.github.andresdefi/zerodust",
5
5
  "description": "MCP server that lets an AI agent sweep its own wallet to exactly zero on 25+ EVM chains, with no gas left stranded.",
6
6
  "type": "module",
@@ -18,7 +18,9 @@
18
18
  "dev": "tsc --watch",
19
19
  "start": "node dist/index.js",
20
20
  "clean": "rm -rf dist",
21
- "prepublishOnly": "npm run clean && npm run build"
21
+ "prepublishOnly": "npm run clean && npm run build",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest"
22
24
  },
23
25
  "keywords": [
24
26
  "zerodust",
@@ -58,12 +60,13 @@
58
60
  },
59
61
  "dependencies": {
60
62
  "@modelcontextprotocol/sdk": "^1.2.0",
61
- "@zerodust/sdk": "^0.1.0",
63
+ "@zerodust/sdk": "^0.2.0",
62
64
  "viem": "^2.21.0",
63
65
  "zod": "^3.22.0"
64
66
  },
65
67
  "devDependencies": {
66
68
  "@types/node": "^20.11.0",
67
- "typescript": "^5.3.3"
69
+ "typescript": "^5.3.3",
70
+ "vitest": "^1.6.1"
68
71
  }
69
72
  }