dexe-mcp 0.7.2 → 0.8.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/README.md +19 -1
  3. package/dist/cli/doctor.d.ts +11 -0
  4. package/dist/cli/doctor.d.ts.map +1 -0
  5. package/dist/cli/doctor.js +44 -0
  6. package/dist/cli/doctor.js.map +1 -0
  7. package/dist/cli/init.d.ts +2 -0
  8. package/dist/cli/init.d.ts.map +1 -0
  9. package/dist/cli/init.js +306 -0
  10. package/dist/cli/init.js.map +1 -0
  11. package/dist/diag/checks.d.ts +25 -0
  12. package/dist/diag/checks.d.ts.map +1 -0
  13. package/dist/diag/checks.js +387 -0
  14. package/dist/diag/checks.js.map +1 -0
  15. package/dist/env/loader.d.ts +43 -0
  16. package/dist/env/loader.d.ts.map +1 -0
  17. package/dist/env/loader.js +121 -0
  18. package/dist/env/loader.js.map +1 -0
  19. package/dist/env/parse.d.ts +23 -0
  20. package/dist/env/parse.d.ts.map +1 -0
  21. package/dist/env/parse.js +41 -0
  22. package/dist/env/parse.js.map +1 -0
  23. package/dist/env/schema.d.ts +257 -0
  24. package/dist/env/schema.d.ts.map +1 -0
  25. package/dist/env/schema.js +240 -0
  26. package/dist/env/schema.js.map +1 -0
  27. package/dist/index.js +31 -11
  28. package/dist/index.js.map +1 -1
  29. package/dist/lib/requireEnv.d.ts +30 -0
  30. package/dist/lib/requireEnv.d.ts.map +1 -0
  31. package/dist/lib/requireEnv.js +48 -0
  32. package/dist/lib/requireEnv.js.map +1 -0
  33. package/dist/lib/signer.d.ts +8 -0
  34. package/dist/lib/signer.d.ts.map +1 -1
  35. package/dist/lib/signer.js +18 -0
  36. package/dist/lib/signer.js.map +1 -1
  37. package/dist/rpc.d.ts +8 -0
  38. package/dist/rpc.d.ts.map +1 -1
  39. package/dist/rpc.js +18 -0
  40. package/dist/rpc.js.map +1 -1
  41. package/dist/tools/doctor.d.ts +11 -0
  42. package/dist/tools/doctor.d.ts.map +1 -0
  43. package/dist/tools/doctor.js +75 -0
  44. package/dist/tools/doctor.js.map +1 -0
  45. package/dist/tools/index.d.ts.map +1 -1
  46. package/dist/tools/index.js +3 -0
  47. package/dist/tools/index.js.map +1 -1
  48. package/dist/tools/read.js +40 -10
  49. package/dist/tools/read.js.map +1 -1
  50. package/dist/tools/txSend.d.ts.map +1 -1
  51. package/dist/tools/txSend.js +18 -1
  52. package/dist/tools/txSend.js.map +1 -1
  53. package/package.json +5 -2
@@ -0,0 +1,257 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Single source of truth for every DEXE_* env var the MCP recognizes.
4
+ *
5
+ * Consumed by:
6
+ * - `src/env/parse.ts` — validation at startup, accumulates issues
7
+ * - `src/diag/checks.ts` — `dexe_doctor` walks ENV_SPEC for presence checks
8
+ * - `src/lib/requireEnv.ts` — fail-soft tool guards build remediation hints
9
+ *
10
+ * To add a new env var: add an entry here, update docs/ENVIRONMENT.md, and
11
+ * the parity test in tests/env/schema.test.ts will require .env.example to
12
+ * mention it. Categories drive the .env.example layout and doctor output
13
+ * grouping.
14
+ */
15
+ export type EnvCategory = "core" | "rpc" | "signer" | "ipfs" | "subgraph" | "walletconnect" | "safe" | "backend" | "dev";
16
+ /**
17
+ * Feature surfaces that depend on a given env var. Used by hintFor() to tell
18
+ * the user which tools light up when the var is set.
19
+ */
20
+ export type EnvFlow = "read" | "broadcast" | "ipfs-upload" | "ipfs-read" | "subgraph-read" | "dao-deploy" | "safe-tx" | "walletconnect-sign" | "backend-offchain";
21
+ export interface EnvEntry {
22
+ schema: z.ZodType<unknown>;
23
+ category: EnvCategory;
24
+ /** True only for vars without which the MCP cannot start. None today. */
25
+ required: boolean;
26
+ /** Placeholder shown in `.env.example`. */
27
+ example: string;
28
+ /** One-line description mirrored in docs/ENVIRONMENT.md. */
29
+ doc: string;
30
+ /** Which user-visible flows this var unlocks. Doctor + hintFor() use this. */
31
+ enablesFlows?: readonly EnvFlow[];
32
+ /** Mask the value when echoing it (banner, doctor, logs). */
33
+ secret?: boolean;
34
+ }
35
+ export declare const ENV_SPEC: {
36
+ readonly DEXE_PROTOCOL_PATH: {
37
+ readonly schema: z.ZodOptional<z.ZodString>;
38
+ readonly category: "core";
39
+ readonly required: false;
40
+ readonly example: "";
41
+ readonly doc: "Override the DeXe-Protocol checkout path. Auto-managed when unset.";
42
+ };
43
+ readonly DEXE_RPC_URL: {
44
+ readonly schema: z.ZodOptional<z.ZodString>;
45
+ readonly category: "rpc";
46
+ readonly required: false;
47
+ readonly example: "https://bsc-dataseed.binance.org";
48
+ readonly doc: "Legacy single-chain RPC. Prefer DEXE_RPC_URL_TESTNET / _MAINNET / _<chainId>.";
49
+ readonly enablesFlows: readonly ["read", "broadcast"];
50
+ };
51
+ readonly DEXE_CHAIN_ID: {
52
+ readonly schema: z.ZodOptional<z.ZodString>;
53
+ readonly category: "rpc";
54
+ readonly required: false;
55
+ readonly example: "56";
56
+ readonly doc: "Chain id for legacy DEXE_RPC_URL. Inferred from hostname when unset.";
57
+ };
58
+ readonly DEXE_RPC_URL_TESTNET: {
59
+ readonly schema: z.ZodOptional<z.ZodString>;
60
+ readonly category: "rpc";
61
+ readonly required: false;
62
+ readonly example: "https://data-seed-prebsc-1-s1.bnbchain.org:8545";
63
+ readonly doc: "RPC URL for BSC testnet (chain 97).";
64
+ readonly enablesFlows: readonly ["read", "broadcast"];
65
+ };
66
+ readonly DEXE_RPC_URL_MAINNET: {
67
+ readonly schema: z.ZodOptional<z.ZodString>;
68
+ readonly category: "rpc";
69
+ readonly required: false;
70
+ readonly example: "https://bsc-dataseed.bnbchain.org";
71
+ readonly doc: "RPC URL for BSC mainnet (chain 56).";
72
+ readonly enablesFlows: readonly ["read", "broadcast"];
73
+ };
74
+ readonly DEXE_DEFAULT_CHAIN_ID: {
75
+ readonly schema: z.ZodOptional<z.ZodString>;
76
+ readonly category: "rpc";
77
+ readonly required: false;
78
+ readonly example: "97";
79
+ readonly doc: "Default chain id when a tool omits chainId. Must match a configured RPC.";
80
+ };
81
+ readonly DEXE_CONTRACTS_REGISTRY: {
82
+ readonly schema: z.ZodOptional<z.ZodString>;
83
+ readonly category: "rpc";
84
+ readonly required: false;
85
+ readonly example: "";
86
+ readonly doc: "Override ContractsRegistry address for the default chain. Rarely needed.";
87
+ };
88
+ readonly DEXE_PRIVATE_KEY: {
89
+ readonly schema: z.ZodOptional<z.ZodString>;
90
+ readonly category: "signer";
91
+ readonly required: false;
92
+ readonly example: "0x<64-hex>";
93
+ readonly doc: "Hot EOA key for dexe_tx_send broadcast. Requires an RPC.";
94
+ readonly enablesFlows: readonly ["broadcast"];
95
+ readonly secret: true;
96
+ };
97
+ readonly DEXE_SIGNER_ALLOWLIST: {
98
+ readonly schema: z.ZodOptional<z.ZodString>;
99
+ readonly category: "signer";
100
+ readonly required: false;
101
+ readonly example: "";
102
+ readonly doc: "Comma-separated destination address allowlist for dexe_tx_send (B6 guard).";
103
+ };
104
+ readonly DEXE_SIGNER_MAX_VALUE_WEI: {
105
+ readonly schema: z.ZodOptional<z.ZodString>;
106
+ readonly category: "signer";
107
+ readonly required: false;
108
+ readonly example: "";
109
+ readonly doc: "Max wei value per broadcast (B7 guard).";
110
+ };
111
+ readonly DEXE_SIGNER_MAX_BROADCASTS_PER_MIN: {
112
+ readonly schema: z.ZodOptional<z.ZodString>;
113
+ readonly category: "signer";
114
+ readonly required: false;
115
+ readonly example: "";
116
+ readonly doc: "Rate limit broadcasts per rolling minute (B10 guard).";
117
+ };
118
+ readonly DEXE_PINATA_JWT: {
119
+ readonly schema: z.ZodOptional<z.ZodString>;
120
+ readonly category: "ipfs";
121
+ readonly required: false;
122
+ readonly example: "eyJhbGciOiJIUzI1NiIs...";
123
+ readonly doc: "Pinata pinning JWT. Required for IPFS uploads; reads work without it.";
124
+ readonly enablesFlows: readonly ["ipfs-upload", "dao-deploy"];
125
+ readonly secret: true;
126
+ };
127
+ readonly DEXE_IPFS_GATEWAY: {
128
+ readonly schema: z.ZodOptional<z.ZodString>;
129
+ readonly category: "ipfs";
130
+ readonly required: false;
131
+ readonly example: "https://<subdomain>.mypinata.cloud";
132
+ readonly doc: "Primary IPFS gateway base URL for reads.";
133
+ readonly enablesFlows: readonly ["ipfs-read"];
134
+ };
135
+ readonly DEXE_IPFS_GATEWAYS_FALLBACK: {
136
+ readonly schema: z.ZodOptional<z.ZodString>;
137
+ readonly category: "ipfs";
138
+ readonly required: false;
139
+ readonly example: "";
140
+ readonly doc: "Comma-separated public IPFS gateway fallbacks for reads.";
141
+ };
142
+ readonly DEXE_PINATA_GATEWAY_TOKEN: {
143
+ readonly schema: z.ZodOptional<z.ZodString>;
144
+ readonly category: "ipfs";
145
+ readonly required: false;
146
+ readonly example: "";
147
+ readonly doc: "Auth token for a private Pinata dedicated gateway (optional).";
148
+ readonly secret: true;
149
+ };
150
+ readonly DEXE_IPFS_AVATAR_GATEWAY: {
151
+ readonly schema: z.ZodOptional<z.ZodString>;
152
+ readonly category: "ipfs";
153
+ readonly required: false;
154
+ readonly example: "";
155
+ readonly doc: "Optional override for the avatar fetch gateway.";
156
+ };
157
+ readonly DEXE_SUBGRAPH_POOLS_URL: {
158
+ readonly schema: z.ZodOptional<z.ZodString>;
159
+ readonly category: "subgraph";
160
+ readonly required: false;
161
+ readonly example: "https://gateway.thegraph.com/api/subgraphs/id/<id>";
162
+ readonly doc: "Pools subgraph endpoint.";
163
+ readonly enablesFlows: readonly ["subgraph-read"];
164
+ };
165
+ readonly DEXE_SUBGRAPH_VALIDATORS_URL: {
166
+ readonly schema: z.ZodOptional<z.ZodString>;
167
+ readonly category: "subgraph";
168
+ readonly required: false;
169
+ readonly example: "https://gateway.thegraph.com/api/subgraphs/id/<id>";
170
+ readonly doc: "Validators subgraph endpoint.";
171
+ readonly enablesFlows: readonly ["subgraph-read"];
172
+ };
173
+ readonly DEXE_SUBGRAPH_INTERACTIONS_URL: {
174
+ readonly schema: z.ZodOptional<z.ZodString>;
175
+ readonly category: "subgraph";
176
+ readonly required: false;
177
+ readonly example: "https://gateway.thegraph.com/api/subgraphs/id/<id>";
178
+ readonly doc: "Interactions subgraph endpoint.";
179
+ readonly enablesFlows: readonly ["subgraph-read"];
180
+ };
181
+ readonly DEXE_GRAPH_API_KEY: {
182
+ readonly schema: z.ZodOptional<z.ZodString>;
183
+ readonly category: "subgraph";
184
+ readonly required: false;
185
+ readonly example: "";
186
+ readonly doc: "The Graph API key for bearer auth (required by decentralized gateway).";
187
+ readonly secret: true;
188
+ };
189
+ readonly DEXE_WALLETCONNECT_PROJECT_ID: {
190
+ readonly schema: z.ZodOptional<z.ZodString>;
191
+ readonly category: "walletconnect";
192
+ readonly required: false;
193
+ readonly example: "";
194
+ readonly doc: "Reown/WalletConnect project id. Enables phone-approval signer when DEXE_PRIVATE_KEY is unset.";
195
+ readonly enablesFlows: readonly ["walletconnect-sign"];
196
+ };
197
+ readonly DEXE_WALLETCONNECT_RELAY_URL: {
198
+ readonly schema: z.ZodOptional<z.ZodString>;
199
+ readonly category: "walletconnect";
200
+ readonly required: false;
201
+ readonly example: "wss://relay.walletconnect.com";
202
+ readonly doc: "Override WC relay websocket URL.";
203
+ };
204
+ readonly DEXE_WALLETCONNECT_APPROVAL_TIMEOUT_MS: {
205
+ readonly schema: z.ZodOptional<z.ZodString>;
206
+ readonly category: "walletconnect";
207
+ readonly required: false;
208
+ readonly example: "120000";
209
+ readonly doc: "Per-tx phone-approval timeout in milliseconds.";
210
+ };
211
+ readonly DEXE_SAFE_TX_SERVICE_URL: {
212
+ readonly schema: z.ZodOptional<z.ZodString>;
213
+ readonly category: "safe";
214
+ readonly required: false;
215
+ readonly example: "https://api.safe.global/tx-service/bnb/api/v2";
216
+ readonly doc: "Safe Transaction Service base. Required for chains without a hosted service.";
217
+ readonly enablesFlows: readonly ["safe-tx"];
218
+ };
219
+ readonly DEXE_SAFE_API_KEY: {
220
+ readonly schema: z.ZodOptional<z.ZodString>;
221
+ readonly category: "safe";
222
+ readonly required: false;
223
+ readonly example: "";
224
+ readonly doc: "Bearer token for Safe Transaction Service.";
225
+ readonly secret: true;
226
+ };
227
+ readonly DEXE_BACKEND_API_URL: {
228
+ readonly schema: z.ZodOptional<z.ZodString>;
229
+ readonly category: "backend";
230
+ readonly required: false;
231
+ readonly example: "https://api.dexe.io";
232
+ readonly doc: "DeXe backend API root for off-chain proposal flows.";
233
+ readonly enablesFlows: readonly ["backend-offchain"];
234
+ };
235
+ readonly DEXE_FORK_BLOCK: {
236
+ readonly schema: z.ZodOptional<z.ZodString>;
237
+ readonly category: "dev";
238
+ readonly required: false;
239
+ readonly example: "";
240
+ readonly doc: "Optional fork block pin (Phase B).";
241
+ };
242
+ };
243
+ /**
244
+ * Re-export with the structural interface so consumers see optional fields
245
+ * (`secret`, `enablesFlows`). `as const` narrows literals but hides any field
246
+ * not present on every entry, which trips `.secret`/`.enablesFlows` reads.
247
+ */
248
+ export declare const ENV_REGISTRY: Record<EnvKey, EnvEntry>;
249
+ export type EnvKey = keyof typeof ENV_SPEC;
250
+ export declare function isKnownEnvKey(k: string): k is EnvKey;
251
+ export declare function envKeys(): EnvKey[];
252
+ /**
253
+ * Pattern for dynamic per-chain RPC keys (DEXE_RPC_URL_1, DEXE_RPC_URL_10…).
254
+ * Not in ENV_SPEC because the suffix is open-ended.
255
+ */
256
+ export declare const DYNAMIC_PER_CHAIN_RPC_RE: RegExp;
257
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/env/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,KAAK,GACL,QAAQ,GACR,MAAM,GACN,UAAU,GACV,eAAe,GACf,MAAM,GACN,SAAS,GACT,KAAK,CAAC;AAEV;;;GAGG;AACH,MAAM,MAAM,OAAO,GACf,MAAM,GACN,WAAW,GACX,aAAa,GACb,WAAW,GACX,eAAe,GACf,YAAY,GACZ,SAAS,GACT,oBAAoB,GACpB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3B,QAAQ,EAAE,WAAW,CAAC;IACtB,yEAAyE;IACzE,QAAQ,EAAE,OAAO,CAAC;IAClB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,8EAA8E;IAC9E,YAAY,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAOD,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgOwB,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAY,CAAC;AAE/D,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,QAAQ,CAAC;AAE3C,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,MAAM,CAEpD;AAED,wBAAgB,OAAO,IAAI,MAAM,EAAE,CAElC;AAED;;;GAGG;AACH,eAAO,MAAM,wBAAwB,QAAyB,CAAC"}
@@ -0,0 +1,240 @@
1
+ import { z } from "zod";
2
+ const hex64 = /^0x[0-9a-fA-F]{64}$/;
3
+ const hex40 = /^0x[0-9a-fA-F]{40}$/;
4
+ const intStr = z.string().regex(/^\d+$/);
5
+ const optionalUrl = z.string().url().optional();
6
+ export const ENV_SPEC = {
7
+ // ─── core / dev ───────────────────────────────────────────────────────────
8
+ DEXE_PROTOCOL_PATH: {
9
+ schema: z.string().optional(),
10
+ category: "core",
11
+ required: false,
12
+ example: "",
13
+ doc: "Override the DeXe-Protocol checkout path. Auto-managed when unset.",
14
+ },
15
+ // ─── rpc ──────────────────────────────────────────────────────────────────
16
+ DEXE_RPC_URL: {
17
+ schema: optionalUrl,
18
+ category: "rpc",
19
+ required: false,
20
+ example: "https://bsc-dataseed.binance.org",
21
+ doc: "Legacy single-chain RPC. Prefer DEXE_RPC_URL_TESTNET / _MAINNET / _<chainId>.",
22
+ enablesFlows: ["read", "broadcast"],
23
+ },
24
+ DEXE_CHAIN_ID: {
25
+ schema: intStr.optional(),
26
+ category: "rpc",
27
+ required: false,
28
+ example: "56",
29
+ doc: "Chain id for legacy DEXE_RPC_URL. Inferred from hostname when unset.",
30
+ },
31
+ DEXE_RPC_URL_TESTNET: {
32
+ schema: optionalUrl,
33
+ category: "rpc",
34
+ required: false,
35
+ example: "https://data-seed-prebsc-1-s1.bnbchain.org:8545",
36
+ doc: "RPC URL for BSC testnet (chain 97).",
37
+ enablesFlows: ["read", "broadcast"],
38
+ },
39
+ DEXE_RPC_URL_MAINNET: {
40
+ schema: optionalUrl,
41
+ category: "rpc",
42
+ required: false,
43
+ example: "https://bsc-dataseed.bnbchain.org",
44
+ doc: "RPC URL for BSC mainnet (chain 56).",
45
+ enablesFlows: ["read", "broadcast"],
46
+ },
47
+ DEXE_DEFAULT_CHAIN_ID: {
48
+ schema: intStr.optional(),
49
+ category: "rpc",
50
+ required: false,
51
+ example: "97",
52
+ doc: "Default chain id when a tool omits chainId. Must match a configured RPC.",
53
+ },
54
+ DEXE_CONTRACTS_REGISTRY: {
55
+ schema: z.string().regex(hex40).optional(),
56
+ category: "rpc",
57
+ required: false,
58
+ example: "",
59
+ doc: "Override ContractsRegistry address for the default chain. Rarely needed.",
60
+ },
61
+ // ─── signer ──────────────────────────────────────────────────────────────
62
+ DEXE_PRIVATE_KEY: {
63
+ schema: z.string().regex(hex64).optional(),
64
+ category: "signer",
65
+ required: false,
66
+ example: "0x<64-hex>",
67
+ doc: "Hot EOA key for dexe_tx_send broadcast. Requires an RPC.",
68
+ enablesFlows: ["broadcast"],
69
+ secret: true,
70
+ },
71
+ DEXE_SIGNER_ALLOWLIST: {
72
+ schema: z.string().optional(),
73
+ category: "signer",
74
+ required: false,
75
+ example: "",
76
+ doc: "Comma-separated destination address allowlist for dexe_tx_send (B6 guard).",
77
+ },
78
+ DEXE_SIGNER_MAX_VALUE_WEI: {
79
+ schema: intStr.optional(),
80
+ category: "signer",
81
+ required: false,
82
+ example: "",
83
+ doc: "Max wei value per broadcast (B7 guard).",
84
+ },
85
+ DEXE_SIGNER_MAX_BROADCASTS_PER_MIN: {
86
+ schema: intStr.optional(),
87
+ category: "signer",
88
+ required: false,
89
+ example: "",
90
+ doc: "Rate limit broadcasts per rolling minute (B10 guard).",
91
+ },
92
+ // ─── ipfs ────────────────────────────────────────────────────────────────
93
+ DEXE_PINATA_JWT: {
94
+ schema: z.string().optional(),
95
+ category: "ipfs",
96
+ required: false,
97
+ example: "eyJhbGciOiJIUzI1NiIs...",
98
+ doc: "Pinata pinning JWT. Required for IPFS uploads; reads work without it.",
99
+ enablesFlows: ["ipfs-upload", "dao-deploy"],
100
+ secret: true,
101
+ },
102
+ DEXE_IPFS_GATEWAY: {
103
+ schema: optionalUrl,
104
+ category: "ipfs",
105
+ required: false,
106
+ example: "https://<subdomain>.mypinata.cloud",
107
+ doc: "Primary IPFS gateway base URL for reads.",
108
+ enablesFlows: ["ipfs-read"],
109
+ },
110
+ DEXE_IPFS_GATEWAYS_FALLBACK: {
111
+ schema: z.string().optional(),
112
+ category: "ipfs",
113
+ required: false,
114
+ example: "",
115
+ doc: "Comma-separated public IPFS gateway fallbacks for reads.",
116
+ },
117
+ DEXE_PINATA_GATEWAY_TOKEN: {
118
+ schema: z.string().optional(),
119
+ category: "ipfs",
120
+ required: false,
121
+ example: "",
122
+ doc: "Auth token for a private Pinata dedicated gateway (optional).",
123
+ secret: true,
124
+ },
125
+ DEXE_IPFS_AVATAR_GATEWAY: {
126
+ schema: optionalUrl,
127
+ category: "ipfs",
128
+ required: false,
129
+ example: "",
130
+ doc: "Optional override for the avatar fetch gateway.",
131
+ },
132
+ // ─── subgraph ────────────────────────────────────────────────────────────
133
+ DEXE_SUBGRAPH_POOLS_URL: {
134
+ schema: optionalUrl,
135
+ category: "subgraph",
136
+ required: false,
137
+ example: "https://gateway.thegraph.com/api/subgraphs/id/<id>",
138
+ doc: "Pools subgraph endpoint.",
139
+ enablesFlows: ["subgraph-read"],
140
+ },
141
+ DEXE_SUBGRAPH_VALIDATORS_URL: {
142
+ schema: optionalUrl,
143
+ category: "subgraph",
144
+ required: false,
145
+ example: "https://gateway.thegraph.com/api/subgraphs/id/<id>",
146
+ doc: "Validators subgraph endpoint.",
147
+ enablesFlows: ["subgraph-read"],
148
+ },
149
+ DEXE_SUBGRAPH_INTERACTIONS_URL: {
150
+ schema: optionalUrl,
151
+ category: "subgraph",
152
+ required: false,
153
+ example: "https://gateway.thegraph.com/api/subgraphs/id/<id>",
154
+ doc: "Interactions subgraph endpoint.",
155
+ enablesFlows: ["subgraph-read"],
156
+ },
157
+ DEXE_GRAPH_API_KEY: {
158
+ schema: z.string().optional(),
159
+ category: "subgraph",
160
+ required: false,
161
+ example: "",
162
+ doc: "The Graph API key for bearer auth (required by decentralized gateway).",
163
+ secret: true,
164
+ },
165
+ // ─── walletconnect ───────────────────────────────────────────────────────
166
+ DEXE_WALLETCONNECT_PROJECT_ID: {
167
+ schema: z.string().optional(),
168
+ category: "walletconnect",
169
+ required: false,
170
+ example: "",
171
+ doc: "Reown/WalletConnect project id. Enables phone-approval signer when DEXE_PRIVATE_KEY is unset.",
172
+ enablesFlows: ["walletconnect-sign"],
173
+ },
174
+ DEXE_WALLETCONNECT_RELAY_URL: {
175
+ schema: optionalUrl,
176
+ category: "walletconnect",
177
+ required: false,
178
+ example: "wss://relay.walletconnect.com",
179
+ doc: "Override WC relay websocket URL.",
180
+ },
181
+ DEXE_WALLETCONNECT_APPROVAL_TIMEOUT_MS: {
182
+ schema: intStr.optional(),
183
+ category: "walletconnect",
184
+ required: false,
185
+ example: "120000",
186
+ doc: "Per-tx phone-approval timeout in milliseconds.",
187
+ },
188
+ // ─── safe ────────────────────────────────────────────────────────────────
189
+ DEXE_SAFE_TX_SERVICE_URL: {
190
+ schema: optionalUrl,
191
+ category: "safe",
192
+ required: false,
193
+ example: "https://api.safe.global/tx-service/bnb/api/v2",
194
+ doc: "Safe Transaction Service base. Required for chains without a hosted service.",
195
+ enablesFlows: ["safe-tx"],
196
+ },
197
+ DEXE_SAFE_API_KEY: {
198
+ schema: z.string().optional(),
199
+ category: "safe",
200
+ required: false,
201
+ example: "",
202
+ doc: "Bearer token for Safe Transaction Service.",
203
+ secret: true,
204
+ },
205
+ // ─── backend ─────────────────────────────────────────────────────────────
206
+ DEXE_BACKEND_API_URL: {
207
+ schema: optionalUrl,
208
+ category: "backend",
209
+ required: false,
210
+ example: "https://api.dexe.io",
211
+ doc: "DeXe backend API root for off-chain proposal flows.",
212
+ enablesFlows: ["backend-offchain"],
213
+ },
214
+ // ─── dev ─────────────────────────────────────────────────────────────────
215
+ DEXE_FORK_BLOCK: {
216
+ schema: intStr.optional(),
217
+ category: "dev",
218
+ required: false,
219
+ example: "",
220
+ doc: "Optional fork block pin (Phase B).",
221
+ },
222
+ };
223
+ /**
224
+ * Re-export with the structural interface so consumers see optional fields
225
+ * (`secret`, `enablesFlows`). `as const` narrows literals but hides any field
226
+ * not present on every entry, which trips `.secret`/`.enablesFlows` reads.
227
+ */
228
+ export const ENV_REGISTRY = ENV_SPEC;
229
+ export function isKnownEnvKey(k) {
230
+ return Object.prototype.hasOwnProperty.call(ENV_SPEC, k);
231
+ }
232
+ export function envKeys() {
233
+ return Object.keys(ENV_SPEC);
234
+ }
235
+ /**
236
+ * Pattern for dynamic per-chain RPC keys (DEXE_RPC_URL_1, DEXE_RPC_URL_10…).
237
+ * Not in ENV_SPEC because the suffix is open-ended.
238
+ */
239
+ export const DYNAMIC_PER_CHAIN_RPC_RE = /^DEXE_RPC_URL_(\d+)$/;
240
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/env/schema.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,23 +1,43 @@
1
1
  #!/usr/bin/env node
2
2
  import { resolve, dirname } from "node:path";
3
- import { existsSync } from "node:fs";
4
3
  import { fileURLToPath } from "node:url";
5
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
6
  import { loadConfig } from "./config.js";
8
7
  import { registerAll } from "./tools/index.js";
9
- // Load .env from project root if present — ensures env vars reach the process
10
- // regardless of how the MCP host launches us (Claude Code drops most env vars
11
- // from its settings.json "env" block on Windows).
8
+ import { loadEnvFile, writeStartupBanner } from "./env/loader.js";
9
+ import { envKeys } from "./env/schema.js";
10
+ // Snapshot DEXE_* schema keys already in process.env BEFORE we load .env.
11
+ // Anything found here was injected by the MCP host (Claude Code's
12
+ // .claude.json `env` block) and will SHADOW the .env file —
13
+ // `process.loadEnvFile()` does NOT override pre-set keys. The startup banner
14
+ // (and dexe_doctor) surface the collision so users don't chase a phantom
15
+ // "I edited .env and nothing changed" bug.
16
+ //
17
+ // This must run BEFORE the CLI subcommand dispatch below: `npx dexe-mcp
18
+ // doctor` invoked directly from a shell needs the same env as the MCP
19
+ // startup path, otherwise the diagnostic sees an empty config.
12
20
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
21
  const dotenvPath = resolve(__dirname, "..", ".env");
14
- if (existsSync(dotenvPath)) {
15
- try {
16
- process.loadEnvFile(dotenvPath);
17
- }
18
- catch {
19
- // Node < 21.7 or other issue fall through to process.env as-is
20
- }
22
+ const prevSnapshot = new Set(envKeys().filter(k => !!process.env[k]?.trim()));
23
+ const envReport = loadEnvFile(dotenvPath, prevSnapshot);
24
+ writeStartupBanner(envReport);
25
+ // CLI subcommand dispatch. `npx dexe-mcp` (no args) → MCP server.
26
+ // `npx dexe-mcp doctor` → run diagnostics and exit.
27
+ // `npx dexe-mcp init` → run the onboarding wizard and exit.
28
+ // Keeps a single bin entry instead of shipping parallel scripts.
29
+ // Subcommands must be handled BEFORE the stdio transport opens — the MCP
30
+ // host passes no args, so any argv[2] means a human/CI invoked directly.
31
+ const subcommand = process.argv[2];
32
+ if (subcommand === "doctor") {
33
+ const mod = await import("./cli/doctor.js");
34
+ await mod.run();
35
+ process.exit(0);
36
+ }
37
+ if (subcommand === "init") {
38
+ const mod = await import("./cli/init.js");
39
+ await mod.run();
40
+ process.exit(0);
21
41
  }
22
42
  async function main() {
23
43
  const config = await loadConfig();
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,8EAA8E;AAC9E,8EAA8E;AAC9E,kDAAkD;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AACpD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;IAC3B,IAAI,CAAC;QACH,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;IACnE,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAElC,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,EACtC;QACE,YAAY,EACV,mRAAmR;KACtR,CACF,CAAC;IAEF,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,qDAAqD;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qDAAqD,MAAM,CAAC,YAAY,GACtE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EACrC,IAAI,CACL,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,GAAG,IAAI,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { type EnvKey } from "../env/schema.js";
2
+ /**
3
+ * Result of a soft env guard. `ok` carries the resolved value; the error
4
+ * branch carries both a one-line `error` and a multi-line `remediation`
5
+ * with paste-ready fixes. Tool handlers fold these into MCP `errorResult`.
6
+ *
7
+ * Modeled on the long-standing `requirePinata` pattern in
8
+ * `src/tools/ipfs.ts:129-137` so existing call sites need only a minimal
9
+ * refactor.
10
+ */
11
+ export type EnvGuardResult<T> = {
12
+ ok: T;
13
+ } | {
14
+ error: string;
15
+ remediation: string;
16
+ };
17
+ /**
18
+ * Soft env-var guard. Returns `{ ok }` only when every listed key is present
19
+ * and non-blank in `process.env`; otherwise returns a remediation hint that
20
+ * names the missing keys and the flows they unlock.
21
+ */
22
+ export declare function requireEnv<K extends EnvKey>(keys: readonly K[]): EnvGuardResult<Record<K, string>>;
23
+ /**
24
+ * Build a human-readable remediation string for a list of (possibly missing)
25
+ * env keys. Pulls per-key documentation and flow-enable metadata out of
26
+ * ENV_SPEC and tacks on the restart reminder so MCP clients don't keep
27
+ * retrying without picking up the new env.
28
+ */
29
+ export declare function hintFor(keys: readonly EnvKey[]): string;
30
+ //# sourceMappingURL=requireEnv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requireEnv.d.ts","sourceRoot":"","sources":["../../src/lib/requireEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACzC,IAAI,EAAE,SAAS,CAAC,EAAE,GACjB,cAAc,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAkBnC;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAevD"}
@@ -0,0 +1,48 @@
1
+ import { ENV_REGISTRY } from "../env/schema.js";
2
+ /**
3
+ * Soft env-var guard. Returns `{ ok }` only when every listed key is present
4
+ * and non-blank in `process.env`; otherwise returns a remediation hint that
5
+ * names the missing keys and the flows they unlock.
6
+ */
7
+ export function requireEnv(keys) {
8
+ const missing = [];
9
+ const out = {};
10
+ for (const k of keys) {
11
+ const v = process.env[k]?.trim();
12
+ if (!v) {
13
+ missing.push(k);
14
+ }
15
+ else {
16
+ out[k] = v;
17
+ }
18
+ }
19
+ if (missing.length > 0) {
20
+ return {
21
+ error: `Missing required env: ${missing.join(", ")}`,
22
+ remediation: hintFor(missing),
23
+ };
24
+ }
25
+ return { ok: out };
26
+ }
27
+ /**
28
+ * Build a human-readable remediation string for a list of (possibly missing)
29
+ * env keys. Pulls per-key documentation and flow-enable metadata out of
30
+ * ENV_SPEC and tacks on the restart reminder so MCP clients don't keep
31
+ * retrying without picking up the new env.
32
+ */
33
+ export function hintFor(keys) {
34
+ const parts = [];
35
+ for (const k of keys) {
36
+ const spec = ENV_REGISTRY[k];
37
+ if (!spec)
38
+ continue;
39
+ const flowHint = spec.enablesFlows?.length
40
+ ? ` (enables: ${spec.enablesFlows.join(", ")})`
41
+ : "";
42
+ parts.push(`Set ${k} in .env — ${spec.doc}${flowHint}`);
43
+ }
44
+ parts.push("After editing .env, restart the MCP server (Claude Code: quit + relaunch). " +
45
+ "Run dexe_doctor to verify the new values were picked up.");
46
+ return parts.join("\n");
47
+ }
48
+ //# sourceMappingURL=requireEnv.js.map