@toon-protocol/client-mcp 0.31.0 → 0.31.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.
- package/README.md +48 -7
- package/dist/{chunk-QHNSETFB.js → chunk-ARFPM2PT.js} +86 -4
- package/dist/{chunk-QHNSETFB.js.map → chunk-ARFPM2PT.js.map} +1 -1
- package/dist/{chunk-U227QSQG.js → chunk-I5L5CB4N.js} +2 -2
- package/dist/{chunk-4K6S3VPM.js → chunk-PRFPAEGG.js} +158 -8
- package/dist/chunk-PRFPAEGG.js.map +1 -0
- package/dist/daemon.js +6 -3
- package/dist/daemon.js.map +1 -1
- package/dist/index.d.ts +98 -1
- package/dist/index.js +12 -4
- package/dist/mcp.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-4K6S3VPM.js.map +0 -1
- /package/dist/{chunk-U227QSQG.js.map → chunk-I5L5CB4N.js.map} +0 -0
package/README.md
CHANGED
|
@@ -56,16 +56,43 @@ The `toon-client` MCP server exposes **8 tools**:
|
|
|
56
56
|
## Install
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
|
-
pnpm add -g @toon-protocol/client-mcp #
|
|
59
|
+
pnpm add -g @toon-protocol/client-mcp # installs both bins
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
This installs two bins: `toon-clientd` and `toon-mcp`.
|
|
62
|
+
This installs two bins: `toon-clientd` and `toon-mcp`. To run a bin straight
|
|
63
|
+
from npm without a global install, name it explicitly (the package name is not a
|
|
64
|
+
bin, so plain `npx @toon-protocol/client-mcp` fails):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx -y -p @toon-protocol/client-mcp toon-mcp # MCP stdio server
|
|
68
|
+
npx -y -p @toon-protocol/client-mcp toon-clientd # daemon
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## First run (zero-config onboarding)
|
|
72
|
+
|
|
73
|
+
On the **first** `toon-clientd run`/`start` (including the auto-spawn from
|
|
74
|
+
`toon-mcp`), the daemon onboards a brand-new user with no manual setup:
|
|
75
|
+
|
|
76
|
+
- **Identity** — if no mnemonic is configured, it generates a fresh BIP-39
|
|
77
|
+
mnemonic, encrypts it to `~/.toon-client/keystore.json`, records
|
|
78
|
+
`keystorePath` in `config.json`, and prints the seed phrase + derived
|
|
79
|
+
addresses **once** (back it up). The keystore is encrypted with
|
|
80
|
+
`TOON_CLIENT_KEYSTORE_PASSWORD` when set, otherwise a default password so the
|
|
81
|
+
identity reloads on every restart with **no env var required**.
|
|
82
|
+
- **Transport scaffolding** — it writes a starter `~/.toon-client/config.json`
|
|
83
|
+
carrying the `btpUrl`/`relayUrl`/`managedAnonProxy` knobs plus a `_help`
|
|
84
|
+
block documenting direct-vs-`.anyone` selection.
|
|
85
|
+
|
|
86
|
+
The one thing you must supply is the apex you pay: set **`btpUrl`** (and usually
|
|
87
|
+
`relayUrl`) in the scaffolded config, then publish. Everything below is for
|
|
88
|
+
overriding those auto-provisioned defaults.
|
|
63
89
|
|
|
64
90
|
## Configure the daemon
|
|
65
91
|
|
|
66
92
|
The daemon reads `~/.toon-client/config.json` (override with `TOON_CLIENT_CONFIG`).
|
|
67
|
-
The **mnemonic is never stored in plaintext by default** —
|
|
68
|
-
encrypted keystore (scrypt + AES-256-GCM, mode 0600)
|
|
93
|
+
The **mnemonic is never stored in plaintext by default** — it is auto-generated
|
|
94
|
+
into an encrypted keystore (scrypt + AES-256-GCM, mode 0600) on first run, or you
|
|
95
|
+
can supply your own via env or an imported keystore.
|
|
69
96
|
|
|
70
97
|
```jsonc
|
|
71
98
|
// ~/.toon-client/config.json
|
|
@@ -96,9 +123,23 @@ Environment overrides: `TOON_CLIENT_MNEMONIC`, `TOON_CLIENT_KEYSTORE_PASSWORD`,
|
|
|
96
123
|
`TOON_CLIENT_BTP_URL`, `TOON_CLIENT_RELAY_URL`, `TOON_CLIENT_SOCKS`,
|
|
97
124
|
`TOON_CLIENT_HTTP_PORT`, `TOON_CLIENT_NETWORK`, `TOON_CLIENT_HOME`.
|
|
98
125
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
126
|
+
**Transport is inferred from the URLs — no separate "direct vs hidden-service"
|
|
127
|
+
switch.** The daemon parses the host of each URL independently:
|
|
128
|
+
|
|
129
|
+
- `btpUrl`/`relayUrl` on a normal host (`ws://1.2.3.4:3000/btp`, `wss://…`) →
|
|
130
|
+
**direct**, dialed as-is.
|
|
131
|
+
- `btpUrl` on a `.anyone` host → the ToonClient auto-starts a managed `anon`
|
|
132
|
+
SOCKS5h proxy and routes paid writes (and reads) through it.
|
|
133
|
+
- `btpUrl` **direct** but `relayUrl` on a `.anyone` host → the daemon starts its
|
|
134
|
+
own managed `anon` proxy just for the free-read subscription; paid writes stay
|
|
135
|
+
direct. (Mixed transports — a direct apex with a hidden-service relay — work
|
|
136
|
+
out of the box.)
|
|
137
|
+
|
|
138
|
+
No explicit `socksProxy` is needed for any of these. Set `socksProxy` only to
|
|
139
|
+
point at your own external proxy, or `managedAnonProxy: false` to opt out of the
|
|
140
|
+
managed proxy entirely. The first bootstrap pays the anon warm-up cost (~30–90s)
|
|
141
|
+
**once** — the detached daemon then stays up. A read-proxy failure surfaces under
|
|
142
|
+
`relay.proxyError` in `toon_status` and never blocks direct paid writes.
|
|
102
143
|
|
|
103
144
|
### Create an encrypted keystore
|
|
104
145
|
|
|
@@ -2243,6 +2243,7 @@ var base58 = /* @__PURE__ */ genBase58("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefg
|
|
|
2243
2243
|
var createBase58check = (sha2563) => /* @__PURE__ */ chain(checksum(4, (data) => sha2563(sha2563(data))), base58);
|
|
2244
2244
|
|
|
2245
2245
|
// ../../node_modules/.pnpm/@scure+bip39@1.6.0/node_modules/@scure/bip39/esm/index.js
|
|
2246
|
+
var isJapanese = (wordlist2) => wordlist2[0] === "\u3042\u3044\u3053\u304F\u3057\u3093";
|
|
2246
2247
|
function nfkd(str) {
|
|
2247
2248
|
if (typeof str !== "string")
|
|
2248
2249
|
throw new TypeError("invalid mnemonic type: " + typeof str);
|
|
@@ -2258,6 +2259,12 @@ function normalize(str) {
|
|
|
2258
2259
|
function aentropy(ent) {
|
|
2259
2260
|
abytes(ent, 16, 20, 24, 28, 32);
|
|
2260
2261
|
}
|
|
2262
|
+
function generateMnemonic(wordlist2, strength = 128) {
|
|
2263
|
+
anumber(strength);
|
|
2264
|
+
if (strength % 32 !== 0 || strength > 256)
|
|
2265
|
+
throw new TypeError("Invalid entropy");
|
|
2266
|
+
return entropyToMnemonic(randomBytes(strength / 8), wordlist2);
|
|
2267
|
+
}
|
|
2261
2268
|
var calcChecksum = (entropy) => {
|
|
2262
2269
|
const bitsLeft = 8 - entropy.length / 4;
|
|
2263
2270
|
return new Uint8Array([sha2562(entropy)[0] >> bitsLeft << bitsLeft]);
|
|
@@ -2277,6 +2284,11 @@ function mnemonicToEntropy(mnemonic, wordlist2) {
|
|
|
2277
2284
|
aentropy(entropy);
|
|
2278
2285
|
return entropy;
|
|
2279
2286
|
}
|
|
2287
|
+
function entropyToMnemonic(entropy, wordlist2) {
|
|
2288
|
+
aentropy(entropy);
|
|
2289
|
+
const words = getCoder(wordlist2).encode(entropy);
|
|
2290
|
+
return words.join(isJapanese(wordlist2) ? "\u3000" : " ");
|
|
2291
|
+
}
|
|
2280
2292
|
function validateMnemonic(mnemonic, wordlist2) {
|
|
2281
2293
|
try {
|
|
2282
2294
|
mnemonicToEntropy(mnemonic, wordlist2);
|
|
@@ -6589,6 +6601,9 @@ var ValidationError = class extends ToonClientError {
|
|
|
6589
6601
|
this.name = "ValidationError";
|
|
6590
6602
|
}
|
|
6591
6603
|
};
|
|
6604
|
+
function generateMnemonic2() {
|
|
6605
|
+
return generateMnemonic(wordlist, 128);
|
|
6606
|
+
}
|
|
6592
6607
|
function validateMnemonic2(mnemonic) {
|
|
6593
6608
|
return validateMnemonic(mnemonic, wordlist);
|
|
6594
6609
|
}
|
|
@@ -10409,6 +10424,8 @@ var SCRYPT_R = 8;
|
|
|
10409
10424
|
var SCRYPT_P = 1;
|
|
10410
10425
|
var SCRYPT_KEY_LEN = 32;
|
|
10411
10426
|
var SCRYPT_MAXMEM = SCRYPT_N * SCRYPT_R * 256 + 32 * 1024 * 1024;
|
|
10427
|
+
var SALT_LEN = 32;
|
|
10428
|
+
var IV_LEN = 12;
|
|
10412
10429
|
var AUTH_TAG_LEN = 16;
|
|
10413
10430
|
function assertNode() {
|
|
10414
10431
|
const versions = globalThis.process?.versions;
|
|
@@ -10418,6 +10435,42 @@ function assertNode() {
|
|
|
10418
10435
|
);
|
|
10419
10436
|
}
|
|
10420
10437
|
}
|
|
10438
|
+
function encryptMnemonic2(mnemonic, password) {
|
|
10439
|
+
assertNode();
|
|
10440
|
+
if (typeof mnemonic !== "string" || mnemonic.length === 0) {
|
|
10441
|
+
throw new Error("encryptMnemonic: mnemonic must be a non-empty string");
|
|
10442
|
+
}
|
|
10443
|
+
if (typeof password !== "string" || password.length === 0) {
|
|
10444
|
+
throw new Error("encryptMnemonic: password must be a non-empty string");
|
|
10445
|
+
}
|
|
10446
|
+
const salt = randomBytes2(SALT_LEN);
|
|
10447
|
+
const iv = randomBytes2(IV_LEN);
|
|
10448
|
+
const key = scryptSync(password, salt, SCRYPT_KEY_LEN, {
|
|
10449
|
+
N: SCRYPT_N,
|
|
10450
|
+
r: SCRYPT_R,
|
|
10451
|
+
p: SCRYPT_P,
|
|
10452
|
+
maxmem: SCRYPT_MAXMEM
|
|
10453
|
+
});
|
|
10454
|
+
try {
|
|
10455
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv, {
|
|
10456
|
+
authTagLength: AUTH_TAG_LEN
|
|
10457
|
+
});
|
|
10458
|
+
const ciphertext = Buffer.concat([
|
|
10459
|
+
cipher.update(mnemonic, "utf8"),
|
|
10460
|
+
cipher.final()
|
|
10461
|
+
]);
|
|
10462
|
+
const tag = cipher.getAuthTag();
|
|
10463
|
+
return {
|
|
10464
|
+
salt: salt.toString("base64"),
|
|
10465
|
+
iv: iv.toString("base64"),
|
|
10466
|
+
ciphertext: ciphertext.toString("base64"),
|
|
10467
|
+
tag: tag.toString("base64"),
|
|
10468
|
+
version: 1
|
|
10469
|
+
};
|
|
10470
|
+
} finally {
|
|
10471
|
+
key.fill(0);
|
|
10472
|
+
}
|
|
10473
|
+
}
|
|
10421
10474
|
function decryptMnemonic2(encrypted, password) {
|
|
10422
10475
|
assertNode();
|
|
10423
10476
|
if (typeof password !== "string" || password.length === 0) {
|
|
@@ -10456,6 +10509,13 @@ function decryptMnemonic2(encrypted, password) {
|
|
|
10456
10509
|
key.fill(0);
|
|
10457
10510
|
}
|
|
10458
10511
|
}
|
|
10512
|
+
function generateKeystore(path, password) {
|
|
10513
|
+
assertNode();
|
|
10514
|
+
const mnemonic = generateMnemonic2();
|
|
10515
|
+
const keystore = encryptMnemonic2(mnemonic, password);
|
|
10516
|
+
writeKeystoreFile(path, keystore);
|
|
10517
|
+
return { mnemonic, keystore };
|
|
10518
|
+
}
|
|
10459
10519
|
function loadKeystore(path, password) {
|
|
10460
10520
|
assertNode();
|
|
10461
10521
|
const raw = readFileSync2(path, "utf8");
|
|
@@ -10467,11 +10527,19 @@ function loadKeystore(path, password) {
|
|
|
10467
10527
|
}
|
|
10468
10528
|
return decryptMnemonic2(parsed, password);
|
|
10469
10529
|
}
|
|
10530
|
+
function writeKeystoreFile(path, keystore) {
|
|
10531
|
+
assertNode();
|
|
10532
|
+
writeFileSync2(path, JSON.stringify(keystore, null, 2), {
|
|
10533
|
+
encoding: "utf8",
|
|
10534
|
+
mode: 384
|
|
10535
|
+
});
|
|
10536
|
+
}
|
|
10470
10537
|
|
|
10471
10538
|
// src/daemon/config.ts
|
|
10472
10539
|
import { readFileSync as readFileSync3 } from "fs";
|
|
10473
10540
|
import { homedir } from "os";
|
|
10474
10541
|
import { join as join2 } from "path";
|
|
10542
|
+
var DEFAULT_KEYSTORE_PASSWORD = "toon-client-default";
|
|
10475
10543
|
function configDir() {
|
|
10476
10544
|
return process.env["TOON_CLIENT_HOME"] ?? join2(homedir(), ".toon-client");
|
|
10477
10545
|
}
|
|
@@ -10492,7 +10560,7 @@ function resolveMnemonic(file) {
|
|
|
10492
10560
|
const envMnemonic = process.env["TOON_CLIENT_MNEMONIC"];
|
|
10493
10561
|
if (envMnemonic) return envMnemonic.trim();
|
|
10494
10562
|
if (file.keystorePath) {
|
|
10495
|
-
const password = process.env["TOON_CLIENT_KEYSTORE_PASSWORD"];
|
|
10563
|
+
const password = process.env["TOON_CLIENT_KEYSTORE_PASSWORD"] ?? (file.keystoreAutoPassword ? DEFAULT_KEYSTORE_PASSWORD : void 0);
|
|
10496
10564
|
if (!password) {
|
|
10497
10565
|
throw new Error(
|
|
10498
10566
|
"keystorePath is set but TOON_CLIENT_KEYSTORE_PASSWORD is not provided"
|
|
@@ -10524,20 +10592,29 @@ function resolveConfig(file) {
|
|
|
10524
10592
|
const chain2 = process.env["TOON_CLIENT_CHAIN"] ?? file.chain ?? "evm";
|
|
10525
10593
|
const apex = file.apexChains?.[chain2] ?? file.apex;
|
|
10526
10594
|
const managedPort = Number(file.managedAnonSocksPort ?? 9050);
|
|
10527
|
-
const
|
|
10595
|
+
const allowManaged = file.managedAnonProxy ?? true;
|
|
10596
|
+
const btpIsAnyone = isAnyoneHost2(btpUrl);
|
|
10597
|
+
const relayIsAnyone = isAnyoneHost2(relayUrl);
|
|
10528
10598
|
let transport;
|
|
10529
10599
|
let managedAnonProxy;
|
|
10530
10600
|
let managedAnonSocksPort;
|
|
10531
10601
|
let readsSocksProxy;
|
|
10602
|
+
let manageReadProxy = false;
|
|
10532
10603
|
if (explicitSocks) {
|
|
10533
10604
|
transport = { type: "socks5", socksProxy: explicitSocks };
|
|
10534
10605
|
managedAnonProxy = false;
|
|
10535
10606
|
readsSocksProxy = explicitSocks;
|
|
10536
|
-
} else if (
|
|
10607
|
+
} else if (allowManaged && btpIsAnyone) {
|
|
10537
10608
|
transport = { type: "direct" };
|
|
10538
10609
|
managedAnonProxy = true;
|
|
10539
10610
|
managedAnonSocksPort = managedPort;
|
|
10540
10611
|
readsSocksProxy = `socks5h://127.0.0.1:${managedPort}`;
|
|
10612
|
+
} else if (allowManaged && relayIsAnyone) {
|
|
10613
|
+
transport = { type: "direct" };
|
|
10614
|
+
managedAnonProxy = false;
|
|
10615
|
+
manageReadProxy = true;
|
|
10616
|
+
managedAnonSocksPort = managedPort;
|
|
10617
|
+
readsSocksProxy = `socks5h://127.0.0.1:${managedPort}`;
|
|
10541
10618
|
} else {
|
|
10542
10619
|
transport = { type: "direct" };
|
|
10543
10620
|
managedAnonProxy = false;
|
|
@@ -10581,6 +10658,8 @@ function resolveConfig(file) {
|
|
|
10581
10658
|
httpPort,
|
|
10582
10659
|
relayUrl,
|
|
10583
10660
|
...readsSocksProxy !== void 0 ? { socksProxy: readsSocksProxy } : {},
|
|
10661
|
+
manageReadProxy,
|
|
10662
|
+
...manageReadProxy ? { readProxySocksPort: managedPort } : {},
|
|
10584
10663
|
destination,
|
|
10585
10664
|
feePerEvent,
|
|
10586
10665
|
apex,
|
|
@@ -10795,7 +10874,10 @@ export {
|
|
|
10795
10874
|
encodeEventToToon,
|
|
10796
10875
|
decodeEventFromToon,
|
|
10797
10876
|
buildIlpPrepare,
|
|
10877
|
+
deriveFullIdentity,
|
|
10798
10878
|
ToonClient,
|
|
10879
|
+
generateKeystore,
|
|
10880
|
+
DEFAULT_KEYSTORE_PASSWORD,
|
|
10799
10881
|
configDir,
|
|
10800
10882
|
defaultConfigPath,
|
|
10801
10883
|
readConfigFile,
|
|
@@ -10831,4 +10913,4 @@ export {
|
|
|
10831
10913
|
@scure/bip32/lib/esm/index.js:
|
|
10832
10914
|
(*! scure-bip32 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) *)
|
|
10833
10915
|
*/
|
|
10834
|
-
//# sourceMappingURL=chunk-
|
|
10916
|
+
//# sourceMappingURL=chunk-ARFPM2PT.js.map
|