omnitrade-mcp 0.4.3 → 0.5.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/cli.js CHANGED
@@ -198,29 +198,90 @@ async function runSetupWizard() {
198
198
 
199
199
  ${c.white}${c.bold}STEP 4/4 \u2014 CONNECT TO CLAUDE${c.reset}
200
200
  ${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}
201
-
201
+ `);
202
+ const rl2 = readline.createInterface({
203
+ input: process.stdin,
204
+ output: process.stdout
205
+ });
206
+ const question2 = (q) => new Promise((resolve) => rl2.question(q, resolve));
207
+ const platform = process.platform;
208
+ let claudeConfigPath;
209
+ if (platform === "darwin") {
210
+ claudeConfigPath = join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
211
+ } else if (platform === "win32") {
212
+ claudeConfigPath = join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
213
+ } else {
214
+ claudeConfigPath = join(homedir(), ".config", "Claude", "claude_desktop_config.json");
215
+ }
216
+ const configureAuto = await question2(` ${c.yellow}?${c.reset} Auto-configure Claude Desktop? ${c.dim}(Y/n)${c.reset}: `);
217
+ if (configureAuto.toLowerCase() !== "n") {
218
+ try {
219
+ let claudeConfig = {};
220
+ const claudeConfigDir = join(claudeConfigPath, "..");
221
+ if (existsSync(claudeConfigPath)) {
222
+ claudeConfig = JSON.parse(readFileSync(claudeConfigPath, "utf-8"));
223
+ console.log(` ${c.dim}Found existing config${c.reset}`);
224
+ } else {
225
+ if (!existsSync(claudeConfigDir)) {
226
+ mkdirSync(claudeConfigDir, { recursive: true });
227
+ }
228
+ console.log(` ${c.dim}Creating new config${c.reset}`);
229
+ }
230
+ if (!claudeConfig.mcpServers) {
231
+ claudeConfig.mcpServers = {};
232
+ }
233
+ claudeConfig.mcpServers.omnitrade = {
234
+ command: "omnitrade",
235
+ args: ["start"]
236
+ };
237
+ writeFileSync(claudeConfigPath, JSON.stringify(claudeConfig, null, 2));
238
+ console.log(`
239
+ ${c.green}${c.bold}\u2713 Claude Desktop configured!${c.reset}
240
+ ${c.dim}${claudeConfigPath}${c.reset}
241
+ `);
242
+ if (platform === "darwin") {
243
+ const restart = await question2(` ${c.yellow}?${c.reset} Restart Claude Desktop now? ${c.dim}(Y/n)${c.reset}: `);
244
+ if (restart.toLowerCase() !== "n") {
245
+ const { execSync } = await import("child_process");
246
+ try {
247
+ execSync(`osascript -e 'quit app "Claude"'`, { stdio: "ignore" });
248
+ await new Promise((r) => setTimeout(r, 1e3));
249
+ execSync('open -a "Claude"', { stdio: "ignore" });
250
+ console.log(` ${c.green}\u2713 Claude Desktop restarted${c.reset}`);
251
+ } catch {
252
+ console.log(` ${c.yellow}! Please restart Claude Desktop manually${c.reset}`);
253
+ }
254
+ }
255
+ } else {
256
+ console.log(` ${c.yellow}!${c.reset} Please restart Claude Desktop to apply changes`);
257
+ }
258
+ } catch (error) {
259
+ console.log(` ${c.red}\u2717 Auto-config failed:${c.reset} ${error.message}`);
260
+ console.log(`
261
+ ${c.dim}Manual setup:${c.reset}
262
+ 1. Open: ${c.blue}${claudeConfigPath}${c.reset}
263
+ 2. Add omnitrade to mcpServers
264
+ 3. Restart Claude Desktop
265
+ `);
266
+ }
267
+ } else {
268
+ console.log(`
202
269
  ${c.cyan}1.${c.reset} Open Claude Desktop config:
270
+ ${c.blue}${claudeConfigPath}${c.reset}
203
271
 
204
- ${c.dim}macOS:${c.reset} ${c.blue}~/Library/Application Support/Claude/claude_desktop_config.json${c.reset}
205
- ${c.dim}Windows:${c.reset} ${c.blue}%APPDATA%\\Claude\\claude_desktop_config.json${c.reset}
206
-
207
- ${c.cyan}2.${c.reset} Add this:
272
+ ${c.cyan}2.${c.reset} Add this to mcpServers:
273
+ ${c.gray}"omnitrade": { "command": "omnitrade", "args": ["start"] }${c.reset}
208
274
 
209
- ${c.gray}{
210
- "mcpServers": {
211
- "omnitrade": {
212
- "command": "omnitrade",
213
- "args": ["start"]
214
- }
215
- }
216
- }${c.reset}
217
-
218
- ${c.cyan}3.${c.reset} ${c.white}Restart Claude Desktop${c.reset}
275
+ ${c.cyan}3.${c.reset} Restart Claude Desktop
276
+ `);
277
+ }
278
+ rl2.close();
279
+ console.log(`
280
+ ${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}
219
281
 
220
- ${c.cyan}4.${c.reset} Try asking Claude:
221
- ${c.dim}"What's my balance on ${exchange}?"${c.reset}
282
+ ${c.white}${c.bold}TRY IT${c.reset}
222
283
 
223
- ${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}
284
+ Ask Claude: ${c.dim}"What's my balance on ${exchange}?"${c.reset}
224
285
 
225
286
  ${c.white}${c.bold}USEFUL COMMANDS${c.reset}
226
287
 
package/dist/index.js CHANGED
@@ -292,9 +292,12 @@ function registerBalanceTools(server, exchangeManager) {
292
292
  );
293
293
  server.tool(
294
294
  "get_portfolio",
295
- "Get a unified portfolio summary across all exchanges with total values.",
296
- {},
297
- async () => {
295
+ "Get a unified portfolio summary with USD values. Shows total portfolio worth and individual holdings.",
296
+ {
297
+ minValue: z2.number().default(1).describe("Minimum USD value to display (default: $1). Set to 0 to show all."),
298
+ showAll: z2.boolean().default(false).describe("Show all assets including dust (overrides minValue)")
299
+ },
300
+ async ({ minValue, showAll }) => {
298
301
  const assetTotals = {};
299
302
  const errors = [];
300
303
  for (const [name, ex] of exchangeManager.getAll()) {
@@ -313,17 +316,59 @@ function registerBalanceTools(server, exchangeManager) {
313
316
  errors.push(`${name}: ${error.message}`);
314
317
  }
315
318
  }
316
- const assets = Object.entries(assetTotals).map(([asset, data]) => ({
317
- asset,
318
- total: data.total,
319
- distribution: data.byExchange
320
- })).sort((a, b) => b.total - a.total);
319
+ const priceCache = {};
320
+ const stablecoins = ["USDT", "USDC", "BUSD", "DAI", "USD", "TUSD", "USDP"];
321
+ const priceExchange = exchangeManager.getAll().values().next().value;
322
+ if (priceExchange) {
323
+ for (const symbol of Object.keys(assetTotals)) {
324
+ if (stablecoins.includes(symbol.toUpperCase())) {
325
+ priceCache[symbol] = 1;
326
+ continue;
327
+ }
328
+ try {
329
+ const ticker = await priceExchange.fetchTicker(`${symbol}/USDT`);
330
+ if (ticker.last) {
331
+ priceCache[symbol] = ticker.last;
332
+ }
333
+ } catch {
334
+ try {
335
+ const ticker = await priceExchange.fetchTicker(`${symbol}/USD`);
336
+ if (ticker.last) {
337
+ priceCache[symbol] = ticker.last;
338
+ }
339
+ } catch {
340
+ priceCache[symbol] = 0;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ const assets = Object.entries(assetTotals).map(([asset, data]) => {
346
+ const price = priceCache[asset] ?? 0;
347
+ const usdValue = data.total * price;
348
+ return {
349
+ asset,
350
+ quantity: data.total,
351
+ price,
352
+ usdValue: parseFloat(usdValue.toFixed(2)),
353
+ distribution: data.byExchange
354
+ };
355
+ }).filter((a) => showAll || a.usdValue >= minValue).sort((a, b) => b.usdValue - a.usdValue);
356
+ const totalUsdValue = assets.reduce((sum, a) => sum + a.usdValue, 0);
357
+ const hiddenCount = Object.keys(assetTotals).length - assets.length;
321
358
  const portfolio = {
322
359
  summary: {
360
+ totalValue: `$${totalUsdValue.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`,
323
361
  totalAssets: assets.length,
362
+ hiddenAssets: hiddenCount > 0 ? `${hiddenCount} assets below $${minValue}` : void 0,
324
363
  exchanges: exchangeManager.getNames()
325
364
  },
326
- assets,
365
+ holdings: assets.map((a) => ({
366
+ asset: a.asset,
367
+ quantity: a.quantity,
368
+ price: a.price > 0 ? `$${a.price.toLocaleString("en-US")}` : "N/A",
369
+ value: `$${a.usdValue.toLocaleString("en-US", { minimumFractionDigits: 2 })}`,
370
+ percent: totalUsdValue > 0 ? `${(a.usdValue / totalUsdValue * 100).toFixed(1)}%` : "0%"
371
+ })),
327
372
  errors: errors.length > 0 ? errors : void 0
328
373
  };
329
374
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnitrade-mcp",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "Multi-exchange AI trading via MCP. 107 exchanges. One AI.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",