omnitrade-mcp 0.3.2 → 0.4.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.
Files changed (3) hide show
  1. package/dist/cli.js +238 -189
  2. package/dist/index.js +1 -1
  3. package/package.json +2 -1
package/dist/cli.js CHANGED
@@ -5,255 +5,310 @@ import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
5
5
  import { homedir } from "os";
6
6
  import { join } from "path";
7
7
  import * as readline from "readline";
8
- var VERSION = "0.3.2";
8
+ var VERSION = "0.4.1";
9
9
  var CONFIG_PATH = join(homedir(), ".omnitrade", "config.json");
10
- var COLORS = {
10
+ var c = {
11
11
  reset: "\x1B[0m",
12
- bright: "\x1B[1m",
12
+ bold: "\x1B[1m",
13
13
  dim: "\x1B[2m",
14
- cyan: "\x1B[36m",
15
- green: "\x1B[32m",
16
- yellow: "\x1B[33m",
17
- red: "\x1B[31m",
18
- magenta: "\x1B[35m",
19
- blue: "\x1B[34m",
20
- white: "\x1B[37m",
21
- bgBlue: "\x1B[44m",
22
- bgMagenta: "\x1B[45m"
14
+ white: "\x1B[97m",
15
+ gray: "\x1B[90m",
16
+ blue: "\x1B[38;5;39m",
17
+ cyan: "\x1B[38;5;51m",
18
+ green: "\x1B[38;5;46m",
19
+ yellow: "\x1B[38;5;226m",
20
+ purple: "\x1B[38;5;165m",
21
+ orange: "\x1B[38;5;208m",
22
+ red: "\x1B[38;5;196m"
23
23
  };
24
- var c = COLORS;
25
24
  function printLogo() {
26
25
  console.log(`
27
- ${c.cyan}${c.bright}
28
- \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
29
- \u2551 \u2551
30
- \u2551 ${c.white}\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557${c.cyan} \u2551
31
- \u2551 ${c.white}\u2588\u2588\u2554\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551\u255A\u2550\u2588\u2588\u2554\u2550\u255D\u2588\u2588\u2554\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u255D${c.cyan} \u2551
32
- \u2551 ${c.white}\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2557${c.cyan} \u2551
33
- \u2551 ${c.white}\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D${c.cyan} \u2551
34
- \u2551 ${c.white}\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557${c.cyan} \u2551
35
- \u2551 ${c.white}\u255A\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u255D${c.cyan} \u2551
36
- \u2551 \u2551
37
- \u2551 ${c.magenta}\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557${c.cyan} \u2551
38
- \u2551 ${c.magenta}\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557${c.cyan} ${c.dim}One AI. 107 Exchanges.${c.cyan} \u2551
39
- \u2551 ${c.magenta}\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D${c.cyan} ${c.dim}Natural Language Trading.${c.cyan} \u2551
40
- \u2551 ${c.magenta}\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2550\u255D${c.cyan} \u2551
41
- \u2551 ${c.magenta}\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551${c.cyan} ${c.dim}v${VERSION}${c.cyan} \u2551
42
- \u2551 ${c.magenta}\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D${c.cyan} ${c.dim}by Connectry Labs${c.cyan} \u2551
43
- \u2551 \u2551
44
- \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
26
+ ${c.cyan}
27
+ \u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584
28
+ \u2588${c.reset} ${c.cyan}\u2588
29
+ \u2588${c.reset} ${c.white}${c.bold} \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588\u2588 \u2588\u2588 \u2588\u2588${c.purple}\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588${c.white}\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 ${c.reset}${c.cyan} \u2588
30
+ \u2588${c.reset} ${c.white}${c.bold}\u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588${c.purple} \u2588\u2588 ${c.white}\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ${c.reset}${c.cyan} \u2588
31
+ \u2588${c.reset} ${c.white}${c.bold}\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588${c.purple} \u2588\u2588 ${c.white}\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588 ${c.reset}${c.cyan} \u2588
32
+ \u2588${c.reset} ${c.white}${c.bold}\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588${c.purple} \u2588\u2588 ${c.white}\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 ${c.reset}${c.cyan} \u2588
33
+ \u2588${c.reset} ${c.white}${c.bold} \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588${c.purple} \u2588\u2588 ${c.white}\u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588\u2588\u2588 ${c.reset}${c.cyan} \u2588
34
+ \u2588${c.reset} ${c.cyan}\u2588
35
+ \u2588${c.gray} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.cyan}\u2588
36
+ \u2588${c.reset} ${c.cyan}\u2588
37
+ \u2588${c.reset} ${c.white}One AI. ${c.cyan}107 Exchanges. ${c.purple}Natural Language Trading.${c.reset} ${c.cyan}\u2588
38
+ \u2588${c.reset} ${c.cyan}\u2588
39
+ \u2588${c.reset} ${c.gray}v${VERSION}${c.reset} ${c.gray}by Connectry Labs${c.reset} ${c.cyan}\u2588
40
+ \u2588${c.reset} ${c.cyan}\u2588
41
+ \u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580\u2580
45
42
  ${c.reset}`);
46
43
  }
44
+ function printCompactLogo() {
45
+ console.log(`
46
+ ${c.cyan}\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${c.reset}
47
+ ${c.white}${c.bold}OMNITRADE${c.purple} MCP${c.reset} ${c.gray}\u2022 One AI. 107 Exchanges.${c.reset}
48
+ ${c.cyan}\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501${c.reset}
49
+ `);
50
+ }
47
51
  function printHelp() {
48
52
  printLogo();
49
53
  console.log(`
50
- ${c.bright}${c.white}USAGE${c.reset}
51
- ${c.cyan}omnitrade-mcp${c.reset} ${c.dim}<command>${c.reset}
54
+ ${c.white}${c.bold}COMMANDS${c.reset}
55
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
52
56
 
53
- ${c.bright}${c.white}COMMANDS${c.reset}
54
- ${c.green}start${c.reset} Start the MCP server (default)
55
- ${c.green}init${c.reset} Interactive setup wizard
56
- ${c.green}config${c.reset} Show current configuration
57
- ${c.green}test${c.reset} Test exchange connections
58
- ${c.green}exchanges${c.reset} List all 107 supported exchanges
59
- ${c.green}help${c.reset} Show this help message
60
- ${c.green}version${c.reset} Show version
57
+ ${c.green}${c.bold}setup${c.reset} Guided setup wizard ${c.dim}(start here!)${c.reset}
58
+ ${c.cyan}start${c.reset} Start MCP server for Claude Desktop
59
+ ${c.cyan}test${c.reset} Test your exchange connections
60
+ ${c.cyan}config${c.reset} View current configuration
61
+ ${c.cyan}exchanges${c.reset} List all 107 supported exchanges
62
+ ${c.cyan}help${c.reset} Show this help
61
63
 
62
- ${c.bright}${c.white}EXAMPLES${c.reset}
63
- ${c.dim}# First time setup${c.reset}
64
- ${c.cyan}omnitrade-mcp init${c.reset}
64
+ ${c.white}${c.bold}GET STARTED${c.reset}
65
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
65
66
 
66
- ${c.dim}# Start the server (for Claude Desktop)${c.reset}
67
- ${c.cyan}omnitrade-mcp start${c.reset}
67
+ ${c.yellow}$${c.reset} ${c.green}omnitrade setup${c.reset}
68
68
 
69
- ${c.dim}# Test your connections${c.reset}
70
- ${c.cyan}omnitrade-mcp test${c.reset}
69
+ ${c.white}${c.bold}DOCUMENTATION${c.reset}
70
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
71
71
 
72
- ${c.bright}${c.white}CONFIGURATION${c.reset}
73
- Config file: ${c.yellow}~/.omnitrade/config.json${c.reset}
74
-
75
- ${c.bright}${c.white}DOCUMENTATION${c.reset}
76
72
  ${c.blue}https://github.com/Connectry-io/omnitrade-mcp${c.reset}
77
73
 
78
- ${c.bright}${c.white}SUPPORT${c.reset}
79
- ${c.blue}https://github.com/Connectry-io/omnitrade-mcp/issues${c.reset}
80
74
  `);
81
75
  }
82
- function printVersion() {
83
- console.log(`${c.cyan}omnitrade-mcp${c.reset} v${VERSION}`);
84
- }
85
- async function showExchanges() {
76
+ async function runSetupWizard() {
86
77
  printLogo();
87
- const ccxt = await import("ccxt");
88
- const exchanges = ccxt.default.exchanges;
89
- console.log(`${c.bright}${c.white}SUPPORTED EXCHANGES (${exchanges.length})${c.reset}
78
+ const rl = readline.createInterface({
79
+ input: process.stdin,
80
+ output: process.stdout
81
+ });
82
+ const question = (q) => new Promise((resolve) => rl.question(q, resolve));
83
+ console.log(`
84
+ ${c.white}${c.bold}WELCOME TO OMNITRADE${c.reset}
85
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
86
+
87
+ Let's connect your first crypto exchange to Claude.
88
+ This takes about ${c.green}2 minutes${c.reset}.
89
+
90
+ ${c.white}${c.bold}WHAT YOU NEED${c.reset}
91
+
92
+ ${c.cyan}1.${c.reset} A crypto exchange account ${c.dim}(Binance, Coinbase, etc.)${c.reset}
93
+ ${c.cyan}2.${c.reset} API keys from that exchange
94
+ ${c.cyan}3.${c.reset} Claude Desktop installed
95
+
90
96
  `);
91
- const tier1 = ["binance", "bybit", "okx", "gate", "kucoin", "bitget", "htx", "mexc", "cryptocom", "bitmex", "woo", "coinex", "bitmart", "bingx"];
92
- console.log(`${c.green}${c.bright}\u2605 TIER 1 - CERTIFIED${c.reset}`);
93
- console.log(` ${tier1.filter((e) => exchanges.includes(e)).join(", ")}`);
94
- const tier2 = ["coinbase", "kraken", "bitstamp", "gemini", "bitfinex", "poloniex", "deribit", "upbit", "bithumb", "bitvavo", "phemex", "ascendex", "lbank"];
97
+ await question(` ${c.dim}Press Enter to continue...${c.reset}`);
95
98
  console.log(`
96
- ${c.yellow}${c.bright}\u2605 TIER 2 - MAJOR${c.reset}`);
97
- console.log(` ${tier2.filter((e) => exchanges.includes(e)).join(", ")}`);
98
- const others = exchanges.filter((e) => !tier1.includes(e) && !tier2.includes(e));
99
+ ${c.white}${c.bold}STEP 1/4 \u2014 CHOOSE EXCHANGE${c.reset}
100
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
101
+
102
+ ${c.cyan}[1]${c.reset} Binance ${c.dim}Largest global exchange${c.reset}
103
+ ${c.cyan}[2]${c.reset} Coinbase ${c.dim}US-based, beginner friendly${c.reset}
104
+ ${c.cyan}[3]${c.reset} Kraken ${c.dim}Security focused${c.reset}
105
+ ${c.cyan}[4]${c.reset} Bybit ${c.dim}Derivatives trading${c.reset}
106
+ ${c.cyan}[5]${c.reset} OKX ${c.dim}Full-featured${c.reset}
107
+ ${c.cyan}[6]${c.reset} KuCoin ${c.dim}Altcoin variety${c.reset}
108
+ ${c.cyan}[7]${c.reset} Other ${c.dim}Enter name manually${c.reset}
109
+
110
+ `);
111
+ const exchangeChoice = await question(` ${c.yellow}?${c.reset} Select [1-7]: `);
112
+ const exchangeMap = {
113
+ "1": "binance",
114
+ "2": "coinbase",
115
+ "3": "kraken",
116
+ "4": "bybit",
117
+ "5": "okx",
118
+ "6": "kucoin"
119
+ };
120
+ let exchange = exchangeMap[exchangeChoice.trim()];
121
+ if (!exchange) {
122
+ exchange = await question(` ${c.yellow}?${c.reset} Exchange name: `);
123
+ }
124
+ exchange = exchange.toLowerCase().trim();
99
125
  console.log(`
100
- ${c.dim}\u2605 ALL OTHERS (${others.length})${c.reset}`);
101
- const cols = 6;
102
- for (let i = 0; i < others.length; i += cols) {
103
- const row = others.slice(i, i + cols);
104
- console.log(` ${c.dim}${row.map((e) => e.padEnd(15)).join("")}${c.reset}`);
126
+ ${c.white}${c.bold}STEP 2/4 \u2014 GET API KEYS${c.reset}
127
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
128
+
129
+ Create API keys on ${c.white}${c.bold}${exchange.toUpperCase()}${c.reset}:
130
+ `);
131
+ if (exchange === "binance") {
132
+ console.log(`
133
+ ${c.cyan}1.${c.reset} Go to ${c.blue}https://testnet.binance.vision${c.reset} ${c.dim}(testnet)${c.reset}
134
+ ${c.cyan}2.${c.reset} Click ${c.white}"Generate HMAC_SHA256 Key"${c.reset}
135
+ ${c.cyan}3.${c.reset} Permissions: ${c.green}\u2713 Read${c.reset} ${c.green}\u2713 Trade${c.reset} ${c.red}\u2717 Withdraw${c.reset}
136
+ ${c.cyan}4.${c.reset} Copy ${c.white}API Key${c.reset} and ${c.white}Secret Key${c.reset}
137
+ `);
138
+ } else if (exchange === "coinbase") {
139
+ console.log(`
140
+ ${c.cyan}1.${c.reset} Go to ${c.blue}https://portal.cdp.coinbase.com${c.reset}
141
+ ${c.cyan}2.${c.reset} Create a new project
142
+ ${c.cyan}3.${c.reset} Generate API credentials
143
+ ${c.cyan}4.${c.reset} Copy ${c.white}API Key${c.reset}, ${c.white}Secret${c.reset}, and ${c.white}Passphrase${c.reset}
144
+ `);
145
+ } else {
146
+ console.log(`
147
+ ${c.cyan}1.${c.reset} Log into ${c.white}${exchange}${c.reset}
148
+ ${c.cyan}2.${c.reset} Go to API settings
149
+ ${c.cyan}3.${c.reset} Create new API key
150
+ ${c.cyan}4.${c.reset} Enable: ${c.green}\u2713 Read${c.reset} ${c.green}\u2713 Trade${c.reset} ${c.red}\u2717 Withdraw${c.reset}
151
+ `);
105
152
  }
153
+ console.log(` ${c.orange}\u26A0 Never enable withdrawal permissions!${c.reset}
154
+ `);
155
+ await question(` ${c.dim}Press Enter when you have your keys...${c.reset}`);
106
156
  console.log(`
107
- ${c.bright}Total: ${c.cyan}${exchanges.length}${c.reset} exchanges supported
157
+ ${c.white}${c.bold}STEP 3/4 \u2014 ENTER API KEYS${c.reset}
158
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
159
+
160
+ ${c.dim}Keys are stored locally at ~/.omnitrade/config.json${c.reset}
161
+ ${c.dim}They never leave your machine.${c.reset}
162
+
163
+ `);
164
+ const apiKey = await question(` ${c.yellow}?${c.reset} API Key: `);
165
+ const secret = await question(` ${c.yellow}?${c.reset} Secret: `);
166
+ let password = "";
167
+ if (["coinbase", "kucoin", "okx"].includes(exchange)) {
168
+ password = await question(` ${c.yellow}?${c.reset} Passphrase: `);
169
+ }
170
+ const testnetAnswer = await question(` ${c.yellow}?${c.reset} Use testnet? ${c.dim}(Y/n)${c.reset}: `);
171
+ const testnet = testnetAnswer.toLowerCase() !== "n";
172
+ rl.close();
173
+ const config = {
174
+ exchanges: {
175
+ [exchange]: {
176
+ apiKey: apiKey.trim(),
177
+ secret: secret.trim(),
178
+ ...password.trim() ? { password: password.trim() } : {},
179
+ testnet
180
+ }
181
+ },
182
+ security: {
183
+ maxOrderSize: 100,
184
+ confirmTrades: true
185
+ }
186
+ };
187
+ const configDir = join(homedir(), ".omnitrade");
188
+ if (!existsSync(configDir)) {
189
+ mkdirSync(configDir, { recursive: true });
190
+ }
191
+ writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
192
+ try {
193
+ const { chmodSync } = await import("fs");
194
+ chmodSync(CONFIG_PATH, 384);
195
+ } catch {
196
+ }
197
+ console.log(`
198
+ ${c.green}${c.bold}\u2713 SAVED${c.reset}
199
+
200
+ ${c.white}${c.bold}STEP 4/4 \u2014 CONNECT TO CLAUDE${c.reset}
201
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
202
+
203
+ ${c.cyan}1.${c.reset} Open Claude Desktop config:
204
+
205
+ ${c.dim}macOS:${c.reset} ${c.blue}~/Library/Application Support/Claude/claude_desktop_config.json${c.reset}
206
+ ${c.dim}Windows:${c.reset} ${c.blue}%APPDATA%\\Claude\\claude_desktop_config.json${c.reset}
207
+
208
+ ${c.cyan}2.${c.reset} Add this:
209
+
210
+ ${c.gray}{
211
+ "mcpServers": {
212
+ "omnitrade": {
213
+ "command": "omnitrade",
214
+ "args": ["start"]
215
+ }
216
+ }
217
+ }${c.reset}
218
+
219
+ ${c.cyan}3.${c.reset} ${c.white}Restart Claude Desktop${c.reset}
220
+
221
+ ${c.cyan}4.${c.reset} Try asking Claude:
222
+ ${c.dim}"What's my balance on ${exchange}?"${c.reset}
223
+
224
+ ${c.gray}\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}
225
+
226
+ ${c.white}${c.bold}USEFUL COMMANDS${c.reset}
227
+
228
+ ${c.cyan}omnitrade test${c.reset} Test your connection
229
+ ${c.cyan}omnitrade config${c.reset} View configuration
230
+ ${c.cyan}omnitrade setup${c.reset} Add another exchange
231
+
232
+ ${c.green}${c.bold}\u2713 Setup complete!${c.reset}
233
+
108
234
  `);
109
235
  }
110
- async function showConfig() {
111
- printLogo();
112
- console.log(`${c.bright}${c.white}CONFIGURATION${c.reset}
236
+ async function showExchanges() {
237
+ printCompactLogo();
238
+ const ccxt = await import("ccxt");
239
+ const exchanges = ccxt.default.exchanges;
240
+ console.log(` ${c.white}${c.bold}SUPPORTED EXCHANGES${c.reset} ${c.dim}(${exchanges.length})${c.reset}
113
241
  `);
114
- console.log(`${c.dim}Location:${c.reset} ${c.yellow}${CONFIG_PATH}${c.reset}
242
+ const tier1 = ["binance", "bybit", "okx", "gate", "kucoin", "bitget", "htx", "mexc", "cryptocom", "bitmex"];
243
+ const tier2 = ["coinbase", "kraken", "bitstamp", "gemini", "bitfinex", "poloniex", "deribit", "upbit", "bithumb", "bitvavo"];
244
+ console.log(` ${c.green}\u2605 TIER 1${c.reset} ${c.dim}${tier1.join(", ")}${c.reset}`);
245
+ console.log(` ${c.yellow}\u2605 TIER 2${c.reset} ${c.dim}${tier2.join(", ")}${c.reset}`);
246
+ const others = exchanges.filter((e) => !tier1.includes(e) && !tier2.includes(e));
247
+ console.log(` ${c.gray}+ ${others.length} more...${c.reset}
115
248
  `);
249
+ }
250
+ async function showConfig() {
251
+ printCompactLogo();
116
252
  if (!existsSync(CONFIG_PATH)) {
117
- console.log(`${c.red}\u2717 Config file not found${c.reset}`);
118
- console.log(`
119
- Run ${c.cyan}omnitrade-mcp init${c.reset} to create one.
253
+ console.log(` ${c.red}\u2717 No configuration${c.reset}
254
+ Run ${c.cyan}omnitrade setup${c.reset}
120
255
  `);
121
256
  return;
122
257
  }
123
258
  try {
124
259
  const config = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
125
- const exchanges = Object.keys(config.exchanges || {});
126
- console.log(`${c.green}\u2713 Config loaded${c.reset}
260
+ console.log(` ${c.green}\u2713 Config loaded${c.reset} ${c.dim}${CONFIG_PATH}${c.reset}
127
261
  `);
128
- console.log(`${c.bright}Exchanges configured:${c.reset}`);
129
- for (const ex of exchanges) {
130
- const cfg = config.exchanges[ex];
131
- const mode = cfg.testnet ? `${c.yellow}testnet${c.reset}` : `${c.green}production${c.reset}`;
262
+ for (const [ex, cfg] of Object.entries(config.exchanges || {})) {
263
+ const mode = cfg.testnet ? `${c.yellow}testnet${c.reset}` : `${c.green}live${c.reset}`;
132
264
  console.log(` ${c.cyan}\u2022${c.reset} ${ex} (${mode})`);
133
265
  }
134
- if (config.security) {
135
- console.log(`
136
- ${c.bright}Security settings:${c.reset}`);
137
- if (config.security.maxOrderSize) {
138
- console.log(` ${c.cyan}\u2022${c.reset} Max order size: $${config.security.maxOrderSize}`);
139
- }
140
- if (config.security.allowedPairs) {
141
- console.log(` ${c.cyan}\u2022${c.reset} Allowed pairs: ${config.security.allowedPairs.join(", ")}`);
142
- }
143
- if (config.security.testnetOnly) {
144
- console.log(` ${c.cyan}\u2022${c.reset} Testnet only: ${c.yellow}enabled${c.reset}`);
145
- }
146
- }
147
266
  console.log("");
148
267
  } catch (error) {
149
- console.log(`${c.red}\u2717 Error reading config:${c.reset} ${error.message}
268
+ console.log(` ${c.red}\u2717 Error:${c.reset} ${error.message}
150
269
  `);
151
270
  }
152
271
  }
153
272
  async function testConnections() {
154
- printLogo();
155
- console.log(`${c.bright}${c.white}TESTING CONNECTIONS${c.reset}
156
- `);
273
+ printCompactLogo();
157
274
  if (!existsSync(CONFIG_PATH)) {
158
- console.log(`${c.red}\u2717 Config file not found${c.reset}`);
159
- console.log(`
160
- Run ${c.cyan}omnitrade-mcp init${c.reset} to create one.
275
+ console.log(` ${c.red}\u2717 No configuration${c.reset}
276
+ Run ${c.cyan}omnitrade setup${c.reset}
161
277
  `);
162
278
  return;
163
279
  }
280
+ console.log(` ${c.white}${c.bold}TESTING${c.reset}
281
+ `);
164
282
  const ccxt = await import("ccxt");
165
283
  const config = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
166
- const exchanges = Object.entries(config.exchanges || {});
167
- for (const [name, cfg] of exchanges) {
168
- process.stdout.write(` Testing ${c.cyan}${name}${c.reset}... `);
284
+ for (const [name, cfg] of Object.entries(config.exchanges || {})) {
285
+ process.stdout.write(` ${c.cyan}${name}${c.reset} ... `);
169
286
  try {
170
287
  const ExchangeClass = ccxt.default[name];
171
- if (!ExchangeClass) {
172
- console.log(`${c.red}\u2717 Unknown exchange${c.reset}`);
173
- continue;
174
- }
175
288
  const ex = new ExchangeClass({
176
289
  apiKey: cfg.apiKey,
177
290
  secret: cfg.secret,
178
291
  password: cfg.password,
179
292
  enableRateLimit: true
180
293
  });
181
- if (cfg.testnet) {
182
- ex.setSandboxMode(true);
183
- }
294
+ if (cfg.testnet) ex.setSandboxMode(true);
184
295
  const balance = await ex.fetchBalance();
185
- const assets = Object.entries(balance.total).filter(([_, v]) => v > 0).slice(0, 3).map(([k, v]) => `${k}: ${v}`).join(", ");
186
- const mode = cfg.testnet ? `${c.yellow}testnet${c.reset}` : `${c.green}live${c.reset}`;
187
- console.log(`${c.green}\u2713${c.reset} Connected (${mode})`);
188
- if (assets) {
189
- console.log(` ${c.dim}Balances: ${assets}${c.reset}`);
190
- }
296
+ const assets = Object.entries(balance.total).filter(([_, v]) => v > 0).slice(0, 3).map(([k, v]) => `${k}:${v}`).join(" ");
297
+ console.log(`${c.green}\u2713${c.reset} ${c.dim}${assets || "connected"}${c.reset}`);
191
298
  } catch (error) {
192
- console.log(`${c.red}\u2717 ${error.message.substring(0, 50)}${c.reset}`);
299
+ console.log(`${c.red}\u2717${c.reset} ${c.dim}${error.message.slice(0, 40)}${c.reset}`);
193
300
  }
194
301
  }
195
302
  console.log("");
196
303
  }
197
- async function initConfig() {
198
- printLogo();
199
- console.log(`${c.bright}${c.white}SETUP WIZARD${c.reset}
200
- `);
201
- const rl = readline.createInterface({
202
- input: process.stdin,
203
- output: process.stdout
204
- });
205
- const question = (q) => new Promise((resolve) => rl.question(q, resolve));
206
- console.log(`${c.dim}Let's set up your first exchange connection.${c.reset}
207
- `);
208
- const exchange = await question(`${c.cyan}?${c.reset} Exchange name (e.g., binance, coinbase, kraken): `);
209
- if (!exchange.trim()) {
210
- console.log(`${c.red}\u2717 Exchange name required${c.reset}`);
211
- rl.close();
212
- return;
213
- }
214
- const apiKey = await question(`${c.cyan}?${c.reset} API Key: `);
215
- const secret = await question(`${c.cyan}?${c.reset} Secret Key: `);
216
- const password = await question(`${c.cyan}?${c.reset} Passphrase (press Enter if none): `);
217
- const testnetAnswer = await question(`${c.cyan}?${c.reset} Use testnet/sandbox? (Y/n): `);
218
- const testnet = testnetAnswer.toLowerCase() !== "n";
219
- rl.close();
220
- const config = {
221
- exchanges: {
222
- [exchange.toLowerCase().trim()]: {
223
- apiKey: apiKey.trim(),
224
- secret: secret.trim(),
225
- ...password.trim() ? { password: password.trim() } : {},
226
- testnet
227
- }
228
- },
229
- security: {
230
- maxOrderSize: 100,
231
- confirmTrades: true
232
- }
233
- };
234
- const configDir = join(homedir(), ".omnitrade");
235
- if (!existsSync(configDir)) {
236
- mkdirSync(configDir, { recursive: true });
237
- }
238
- writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
239
- try {
240
- const { chmodSync } = await import("fs");
241
- chmodSync(CONFIG_PATH, 384);
242
- } catch {
243
- }
244
- console.log(`
245
- ${c.green}\u2713 Configuration saved to ${CONFIG_PATH}${c.reset}`);
246
- console.log(`
247
- ${c.bright}Next steps:${c.reset}`);
248
- console.log(` 1. Test connection: ${c.cyan}omnitrade-mcp test${c.reset}`);
249
- console.log(` 2. Add to Claude: ${c.cyan}Add "omnitrade-mcp" to Claude Desktop config${c.reset}`);
250
- console.log(` 3. Start trading: ${c.cyan}Ask Claude "What's my balance?"${c.reset}
251
- `);
252
- }
253
304
  async function main() {
254
305
  const args = process.argv.slice(2);
255
306
  const command = args[0] || "help";
256
307
  switch (command) {
308
+ case "setup":
309
+ case "init":
310
+ await runSetupWizard();
311
+ break;
257
312
  case "help":
258
313
  case "--help":
259
314
  case "-h":
@@ -262,31 +317,25 @@ async function main() {
262
317
  case "version":
263
318
  case "--version":
264
319
  case "-v":
265
- printVersion();
320
+ console.log(`omnitrade v${VERSION}`);
266
321
  break;
267
322
  case "exchanges":
268
323
  case "list":
269
324
  await showExchanges();
270
325
  break;
271
326
  case "config":
272
- case "status":
273
327
  await showConfig();
274
328
  break;
275
329
  case "test":
276
330
  await testConnections();
277
331
  break;
278
- case "init":
279
- case "setup":
280
- await initConfig();
281
- break;
282
332
  case "start":
283
333
  case "serve":
284
- case "run":
285
334
  await import("./index.js");
286
335
  break;
287
336
  default:
288
- console.log(`${c.red}Unknown command: ${command}${c.reset}`);
289
- console.log(`Run ${c.cyan}omnitrade-mcp help${c.reset} for usage.
337
+ console.log(`${c.red}Unknown: ${command}${c.reset}
338
+ Run ${c.cyan}omnitrade help${c.reset}
290
339
  `);
291
340
  process.exit(1);
292
341
  }
package/dist/index.js CHANGED
@@ -911,7 +911,7 @@ function registerArbitrageTools(server, exchangeManager) {
911
911
  }
912
912
 
913
913
  // src/index.ts
914
- var VERSION = "0.3.2";
914
+ var VERSION = "0.4.1";
915
915
  function showBanner() {
916
916
  console.error(`
917
917
  \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "omnitrade-mcp",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "description": "Multi-exchange AI trading via MCP. 107 exchanges. One AI.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
+ "omnitrade": "dist/cli.js",
8
9
  "omnitrade-mcp": "dist/cli.js"
9
10
  },
10
11
  "scripts": {