@noelclaw/mcp 1.2.1 → 1.4.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/server.js CHANGED
@@ -33,11 +33,10 @@ function containsSensitiveRequest(args) {
33
33
  text.includes("privatekey"));
34
34
  }
35
35
  exports.ALL_TOOLS = [
36
- ...market_js_1.MARKET_TOOLS, // 6 — market data, signals, whale alerts, recap
37
- ...news_js_1.NEWS_TOOLS, // 2 — crypto news digest, manual signal gen
36
+ ...market_js_1.MARKET_TOOLS, // 2 — market data, token data
38
37
  ...research_js_1.RESEARCH_TOOLS, // 1 — deep research
39
38
  ...insight_js_1.INSIGHT_TOOLS, // 2 — get_insight, ask_noel
40
- ...defi_js_1.DEFI_TOOLS, // 6portfolio, swap, send, deploy, claim, mint
39
+ ...defi_js_1.DEFI_TOOLS, // 3 — swap, send, claim
41
40
  ...automation_js_1.AUTOMATION_TOOLS, // 4 — create, list, pause, delete
42
41
  ...swarm_js_1.SWARM_TOOLS, // 6 — start, stop, status, memory, scores
43
42
  ...framework_js_1.FRAMEWORK_TOOLS, // 6 — task packets, playbooks, sentinel, ledger
@@ -46,7 +45,7 @@ exports.ALL_TOOLS = [
46
45
  ...twitter_js_1.TWITTER_TOOLS, // 1 — post tweet
47
46
  ...miroshark_js_1.MIROSHARK_TOOLS, // 2 — simulate, status
48
47
  ...humanizer_js_1.HUMANIZER_TOOLS, // 1 — humanize_text
49
- // total: 46
48
+ // total: 37
50
49
  ];
51
50
  exports.server = new index_js_1.Server({ name: "noelclaw", version: "2.1.0" }, { capabilities: { tools: {} } });
52
51
  exports.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({ tools: exports.ALL_TOOLS }));
@@ -6,11 +6,6 @@ const zod_1 = require("zod");
6
6
  const convex_js_1 = require("../convex.js");
7
7
  const wallet_js_1 = require("../wallet.js");
8
8
  exports.DEFI_TOOLS = [
9
- {
10
- name: "get_portfolio",
11
- description: "Get your Base wallet address and full token portfolio including all token balances with USD values. Auto-creates a secure encrypted wallet on first use.",
12
- inputSchema: { type: "object", properties: {}, required: [] },
13
- },
14
9
  {
15
10
  name: "swap_tokens",
16
11
  description: "Swap tokens on Base mainnet via 0x Permit2. Supported: ETH, USDC, USDT, DAI, WETH. Amount is human-readable. Signed and broadcast locally from your wallet.",
@@ -37,24 +32,6 @@ exports.DEFI_TOOLS = [
37
32
  required: ["token", "toAddress", "amount"],
38
33
  },
39
34
  },
40
- {
41
- name: "deploy_token",
42
- description: "Launch a new memecoin on Base via Flaunch. Deploys with fair launch period, sets your revenue share (default 80% of swap fees), and returns a Memestream NFT that earns ETH from every swap forever. Image must be a publicly accessible URL.",
43
- inputSchema: {
44
- type: "object",
45
- properties: {
46
- name: { type: "string", description: "Token name e.g. 'Pepe Noel'" },
47
- symbol: { type: "string", description: "Ticker 3-6 chars e.g. 'PNOEL'" },
48
- imageUrl: { type: "string", description: "Public image URL for the token" },
49
- description: { type: "string", description: "Token description (optional)" },
50
- initialMarketCapUSD: { type: "number", description: "Starting mcap in USD (default: 10000, min: 1000)" },
51
- creatorFeePercent: { type: "number", description: "Your % of swap fees (default: 80, max: 100)" },
52
- preminePercent: { type: "number", description: "% of supply to premine at launch (default: 0)" },
53
- fairLaunchDurationMinutes: { type: "number", description: "Fair launch window in minutes (default: 30)" },
54
- },
55
- required: ["name", "symbol", "imageUrl"],
56
- },
57
- },
58
35
  {
59
36
  name: "claim_fees",
60
37
  description: "Claim accumulated ETH from your Flaunch token swap fees. Calls claim() on the Flaunch PositionManager — pulls all pending ETH from your deployed tokens to your wallet.",
@@ -64,77 +41,16 @@ exports.DEFI_TOOLS = [
64
41
  required: [],
65
42
  },
66
43
  },
67
- {
68
- name: "mint_nft",
69
- description: "Auto-mint any NFT on Base. Pass the mint page URL or contract address. " +
70
- "Noel detects the contract, checks your eligibility and balance, " +
71
- "then mints directly from your wallet. Works with OpenSea, Zora, Highlight, and most Base NFT projects.",
72
- inputSchema: {
73
- type: "object",
74
- properties: {
75
- mintUrl: {
76
- type: "string",
77
- description: "NFT mint URL (OpenSea, Zora, Highlight) or raw contract address (0x...)",
78
- },
79
- quantity: {
80
- type: "number",
81
- description: "How many to mint (default: 1)",
82
- },
83
- },
84
- required: ["mintUrl"],
85
- },
86
- },
87
44
  ];
88
45
  const SwapSchema = zod_1.z.object({ fromToken: zod_1.z.string().min(1), toToken: zod_1.z.string().min(1), amount: zod_1.z.string().min(1) });
89
46
  const SendSchema = zod_1.z.object({ token: zod_1.z.string().min(1), toAddress: zod_1.z.string().regex(/^0x[0-9a-fA-F]{40}$/, "must be a valid 0x address"), amount: zod_1.z.string().min(1) });
90
- const MintNftSchema = zod_1.z.object({
91
- mintUrl: zod_1.z.string().min(1),
92
- quantity: zod_1.z.number().min(1).max(100).optional(),
93
- });
94
- const DeployTokenSchema = zod_1.z.object({
95
- name: zod_1.z.string().min(1),
96
- symbol: zod_1.z.string().min(3).max(6),
97
- imageUrl: zod_1.z.string().url(),
98
- description: zod_1.z.string().optional(),
99
- initialMarketCapUSD: zod_1.z.number().min(1000).optional(),
100
- creatorFeePercent: zod_1.z.number().min(0).max(100).optional(),
101
- preminePercent: zod_1.z.number().min(0).max(50).optional(),
102
- fairLaunchDurationMinutes: zod_1.z.number().min(1).optional(),
103
- });
104
47
  async function handleDefiTool(name, args) {
105
48
  switch (name) {
106
- case "get_portfolio": {
107
- const data = await (0, convex_js_1.callConvex)("/mcp/defi/portfolio", "GET", undefined, "get_portfolio");
108
- if (data.error)
109
- return { content: [{ type: "text", text: `Portfolio error: ${data.error}` }], isError: true };
110
- const lines = [
111
- `**Portfolio — Base Mainnet**`, `Address: \`${data.address}\``, ``, `**Balances**`,
112
- ];
113
- for (const b of (data.balances ?? [])) {
114
- lines.push(`• ${b.token}: ${b.balance}${b.valueUsd ? ` (~$${Number(b.valueUsd).toFixed(2)})` : ""}`);
115
- }
116
- lines.push(``, `**Total Value:** ~$${Number(data.totalValueUsd ?? 0).toFixed(2)}`);
117
- return { content: [{ type: "text", text: lines.join("\n") }] };
118
- }
119
49
  case "swap_tokens": {
120
50
  const parsed = SwapSchema.safeParse(args);
121
51
  if (!parsed.success)
122
52
  return { content: [{ type: "text", text: `Invalid input: ${String(parsed.error.issues[0].path[0])} ${parsed.error.issues[0].message}` }], isError: true };
123
53
  let { fromToken, toToken, amount } = parsed.data;
124
- // Resolve percentage amounts e.g. "50%", "100%", "25%"
125
- const pctMatch = amount.trim().match(/^(\d+(?:\.\d+)?)\s*%$/);
126
- if (pctMatch) {
127
- const pct = parseFloat(pctMatch[1]) / 100;
128
- const portfolio = await (0, convex_js_1.callConvex)("/mcp/defi/portfolio", "GET", undefined, "get_portfolio");
129
- const balance = (portfolio.balances ?? []).find((b) => b.token?.toUpperCase() === fromToken.toUpperCase());
130
- if (!balance)
131
- return { content: [{ type: "text", text: `No ${fromToken.toUpperCase()} balance found in portfolio.` }], isError: true };
132
- const rawBalance = parseFloat(balance.balance ?? "0");
133
- if (rawBalance <= 0)
134
- return { content: [{ type: "text", text: `${fromToken.toUpperCase()} balance is 0.` }], isError: true };
135
- const resolved = (rawBalance * pct).toFixed(6).replace(/\.?0+$/, "");
136
- amount = resolved;
137
- }
138
54
  const wallet = await (0, wallet_js_1.getOrCreateWallet)();
139
55
  const result = await (0, convex_js_1.callConvex)("/mcp/defi/swap", "POST", { fromToken, toToken, amount }, "swap_tokens");
140
56
  if (!result.success)
@@ -172,56 +88,6 @@ async function handleDefiTool(name, args) {
172
88
  }],
173
89
  };
174
90
  }
175
- case "mint_nft": {
176
- const parsed = MintNftSchema.safeParse(args);
177
- if (!parsed.success)
178
- return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
179
- const { mintUrl, quantity } = parsed.data;
180
- const wallet = await (0, wallet_js_1.getOrCreateWallet)();
181
- const result = await (0, convex_js_1.callConvex)("/mcp/nft/mint", "POST", { mintUrl, quantity: quantity ?? 1 }, "mint_nft");
182
- if (result.error)
183
- return { content: [{ type: "text", text: `Mint failed: ${result.error}` }], isError: true };
184
- if (result.action !== "sign_and_broadcast")
185
- return { content: [{ type: "text", text: "Unexpected response from server" }], isError: true };
186
- const txHash = await (0, wallet_js_1.signAndBroadcast)(wallet, result);
187
- const meta = result.metadata ?? {};
188
- return {
189
- content: [{
190
- type: "text",
191
- text: [
192
- `✅ Minted successfully!`,
193
- `Contract: ${meta.contractAddress ?? result.to}`,
194
- `Quantity: ${meta.quantity ?? quantity ?? 1}`,
195
- `Cost: ${meta.totalCost ?? "0"} ETH`,
196
- `Tx: \`${txHash}\``,
197
- `https://basescan.org/tx/${txHash}`,
198
- ].join("\n"),
199
- }],
200
- };
201
- }
202
- case "deploy_token": {
203
- const parsed = DeployTokenSchema.safeParse(args);
204
- if (!parsed.success)
205
- return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
206
- const wallet = await (0, wallet_js_1.getOrCreateWallet)();
207
- const result = await (0, convex_js_1.callConvex)("/mcp/token/deploy", "POST", parsed.data, "deploy_token");
208
- if (result.error)
209
- return { content: [{ type: "text", text: `Deploy failed: ${result.error}` }], isError: true };
210
- const txHash = await (0, wallet_js_1.signAndBroadcast)(wallet, result);
211
- return {
212
- content: [{
213
- type: "text",
214
- text: [
215
- `✅ Token deployed!`,
216
- `Name: ${parsed.data.name} ($${parsed.data.symbol})`,
217
- `Tx Hash: \`${txHash}\``,
218
- `https://basescan.org/tx/${txHash}`,
219
- ``,
220
- `Your Memestream NFT earns ETH from every swap.`,
221
- ].join("\n"),
222
- }],
223
- };
224
- }
225
91
  case "claim_fees": {
226
92
  const wallet = await (0, wallet_js_1.getOrCreateWallet)();
227
93
  const result = await (0, convex_js_1.callConvex)("/mcp/token/claim", "POST", {}, "claim_fees");
@@ -94,7 +94,7 @@ async function handleHumanizerTool(name, args) {
94
94
  "Authorization": `Bearer ${apiKey}`,
95
95
  },
96
96
  body: JSON.stringify({
97
- model: "MiniMax-Text-01",
97
+ model: "MiniMax-M2.7",
98
98
  messages: [
99
99
  { role: "system", content: HUMANIZER_SYSTEM },
100
100
  { role: "user", content: userMsg },
@@ -109,7 +109,8 @@ async function handleHumanizerTool(name, args) {
109
109
  return { content: [{ type: "text", text: `API error: ${res.status} ${err.slice(0, 200)}` }], isError: true };
110
110
  }
111
111
  const data = await res.json();
112
- const output = data?.choices?.[0]?.message?.content?.trim();
112
+ const raw = data?.choices?.[0]?.message?.content ?? "";
113
+ const output = raw.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
113
114
  if (!output)
114
115
  return { content: [{ type: "text", text: "Empty response from model" }], isError: true };
115
116
  return { content: [{ type: "text", text: output }] };
@@ -23,52 +23,9 @@ exports.MARKET_TOOLS = [
23
23
  required: ["question"],
24
24
  },
25
25
  },
26
- {
27
- name: "get_latest_signal",
28
- description: "Get the latest BTC and/or ETH 1H trading signals from Noel. Includes entry price, take profit targets, stop loss, confidence score, and reasoning. Generated daily at 08:00 UTC.",
29
- inputSchema: {
30
- type: "object",
31
- properties: { token: { type: "string", description: "Token to get signal for: 'BTC', 'ETH', or omit for both" } },
32
- required: [],
33
- },
34
- },
35
- {
36
- name: "get_signal_history",
37
- description: "Get signal history with win/loss record and winrate statistics.",
38
- inputSchema: {
39
- type: "object",
40
- properties: {
41
- token: { type: "string", description: "BTC or ETH" },
42
- days: { type: "number", description: "Number of days to look back (default: 7)" },
43
- },
44
- required: [],
45
- },
46
- },
47
- {
48
- name: "get_smart_money_alerts",
49
- description: "Get smart money and insider wallet movements for micro-cap tokens.",
50
- inputSchema: {
51
- type: "object",
52
- properties: { hours: { type: "number", description: "How many hours back to look (default: 24)" } },
53
- required: [],
54
- },
55
- },
56
- {
57
- name: "get_daily_recap",
58
- description: "Get today's trading performance recap with winrate, PnL stats, and AI review.",
59
- inputSchema: {
60
- type: "object",
61
- properties: { date: { type: "string", description: "Date in YYYY-MM-DD format (default: today UTC)" } },
62
- required: [],
63
- },
64
- },
65
26
  ];
66
27
  const GetMarketDataSchema = zod_1.z.object({ token: zod_1.z.string().optional() });
67
28
  const GetTokenDataSchema = zod_1.z.object({ question: zod_1.z.string().min(1) });
68
- const GetLatestSignalSchema = zod_1.z.object({ token: zod_1.z.string().optional() });
69
- const GetSignalHistorySchema = zod_1.z.object({ token: zod_1.z.string().optional(), days: zod_1.z.number().optional() });
70
- const GetSmartMoneyAlertsSchema = zod_1.z.object({ hours: zod_1.z.number().optional() });
71
- const GetDailyRecapSchema = zod_1.z.object({ date: zod_1.z.string().optional() });
72
29
  async function handleMarketTool(name, args) {
73
30
  switch (name) {
74
31
  case "get_market_data": {
@@ -76,38 +33,16 @@ async function handleMarketTool(name, args) {
76
33
  if (!parsed.success)
77
34
  return { content: [{ type: "text", text: `Invalid input: token ${parsed.error.issues[0].message}` }], isError: true };
78
35
  const { token } = parsed.data;
79
- const tokenQ = token ? `?token=${encodeURIComponent(token)}` : "";
80
- const data = await (0, convex_js_1.callConvex)(`/mcp/market${tokenQ}`, "GET", undefined, "get_market_data");
81
- const lines = [`**Market Data** — ${data.fetchedAt ?? new Date().toISOString()}`, ""];
82
- if (data.keyPrices) {
83
- lines.push("**Key Prices**");
84
- for (const [coin, info] of Object.entries(data.keyPrices)) {
85
- const price = info.usd?.toLocaleString("en-US", { style: "currency", currency: "USD" });
86
- const change = info.usd_24h_change?.toFixed(2);
87
- const sign = (info.usd_24h_change ?? 0) >= 0 ? "+" : "";
88
- lines.push(`• ${coin.toUpperCase()}: ${price} (${sign}${change}%)`);
89
- }
90
- lines.push("");
91
- }
92
- if (data.trending?.length) {
93
- lines.push("**Trending** (top 10)");
94
- for (const c of data.trending) {
95
- const ch = c.change24h?.toFixed(2);
96
- const sign = (c.change24h ?? 0) >= 0 ? "+" : "";
97
- lines.push(`• ${c.name} (${c.symbol?.toUpperCase()}) — rank #${c.rank ?? "?"} ${ch != null ? `${sign}${ch}%` : ""}`);
98
- }
99
- lines.push("");
100
- }
101
- if (data.top20?.length) {
102
- lines.push("**Top 20 by Market Cap**");
103
- lines.push("| # | Name | Price | 24h% |");
104
- lines.push("|---|------|-------|------|");
105
- for (const c of data.top20) {
106
- const price = c.price?.toLocaleString("en-US", { style: "currency", currency: "USD", maximumFractionDigits: 4 });
107
- const ch = c.change24h?.toFixed(2);
108
- const sign = (c.change24h ?? 0) >= 0 ? "+" : "";
109
- lines.push(`| ${c.rank} | ${c.name} (${c.symbol?.toUpperCase()}) | ${price} | ${sign}${ch}% |`);
110
- }
36
+ const symbols = token ? [token.toUpperCase()] : ["BTC", "ETH", "SOL"];
37
+ const results = await Promise.all(symbols.map((s) => (0, convex_js_1.callConvex)(`/swarm/memory/read?key=market/${s}`, "GET", undefined, "get_market_data")));
38
+ const lines = [`**Market Data** — ${new Date().toISOString()}`, ""];
39
+ for (const d of results) {
40
+ if (d.error)
41
+ continue;
42
+ const price = d.price_usd?.toLocaleString("en-US", { style: "currency", currency: "USD" });
43
+ const ch = d.change_24h_pct?.toFixed(2);
44
+ const sign = (d.change_24h_pct ?? 0) >= 0 ? "+" : "";
45
+ lines.push(`• **${d.symbol}**: ${price} (${sign}${ch}%) — mcap $${(d.market_cap_usd / 1e9).toFixed(1)}B`);
111
46
  }
112
47
  return { content: [{ type: "text", text: lines.join("\n") }] };
113
48
  }
@@ -115,111 +50,21 @@ async function handleMarketTool(name, args) {
115
50
  const parsed = GetTokenDataSchema.safeParse(args);
116
51
  if (!parsed.success)
117
52
  return { content: [{ type: "text", text: `Invalid input: question ${parsed.error.issues[0].message}` }], isError: true };
118
- // Extract token symbol from natural-language question
119
53
  const q = parsed.data.question.toUpperCase();
120
54
  const KNOWN = ["BTC", "ETH", "SOL", "BNB", "USDT", "USDC", "XRP", "DOGE", "ADA", "AVAX", "DOT", "LINK", "UNI", "OP", "ARB", "HYPE", "PEPE", "SUI", "APT", "NEAR", "INJ", "TIA", "MATIC", "TON", "SHIB", "WIF", "BONK"];
121
- const found = KNOWN.find((t) => new RegExp(`\\b${t}\\b`).test(q));
122
- const tokenQ = found ? `?token=${found}` : "";
123
- const data = await (0, convex_js_1.callConvex)(`/mcp/market${tokenQ}`, "GET", undefined, "get_token_data");
124
- const lines = [`**Token Data** — ${data.fetchedAt ?? new Date().toISOString()}`, ""];
125
- if (data.keyPrices) {
126
- lines.push("**Prices**");
127
- for (const [coin, info] of Object.entries(data.keyPrices)) {
128
- const price = info.usd?.toLocaleString("en-US", { style: "currency", currency: "USD" });
129
- const ch = (info.usd_24h_change ?? 0).toFixed(2);
130
- const sign = (info.usd_24h_change ?? 0) >= 0 ? "+" : "";
131
- lines.push(`• ${coin.toUpperCase()}: ${price} (${sign}${ch}%)`);
132
- }
133
- }
134
- if (data.trending?.length) {
135
- lines.push("", "**Trending**");
136
- for (const c of data.trending.slice(0, 5)) {
137
- const ch = c.change24h?.toFixed(2);
138
- const sign = (c.change24h ?? 0) >= 0 ? "+" : "";
139
- lines.push(`• ${c.symbol?.toUpperCase()} — #${c.rank ?? "?"} ${ch != null ? `${sign}${ch}%` : ""}`);
140
- }
141
- }
142
- return { content: [{ type: "text", text: lines.join("\n") }] };
143
- }
144
- case "get_latest_signal": {
145
- const parsed = GetLatestSignalSchema.safeParse(args ?? {});
146
- if (!parsed.success)
147
- return { content: [{ type: "text", text: `Invalid input: token ${parsed.error.issues[0].message}` }], isError: true };
148
- const tokenParam = parsed.data.token?.toUpperCase() ?? "both";
149
- const data = await (0, convex_js_1.callConvex)(`/signals/latest${tokenParam !== "BOTH" && tokenParam !== "both" ? `?token=${encodeURIComponent(tokenParam)}` : ""}`, "GET", undefined, "get_latest_signal");
150
- const lines = ["**Latest Noel Signals**", ""];
151
- for (const [tok, sig] of Object.entries(data)) {
152
- if (!sig) {
153
- lines.push(`**${tok}:** No signal available`, "");
154
- continue;
155
- }
156
- const emoji = sig.signalType === "BUY" ? "🟢" : sig.signalType === "SELL" ? "🔴" : "🟡";
157
- lines.push(`${emoji} **${tok}/USD — ${sig.signalType}**`, `Entry: $${sig.entryPrice?.toLocaleString()} | TP1: $${sig.target1?.toLocaleString()}${sig.target2 ? ` | TP2: $${sig.target2?.toLocaleString()}` : ""} | SL: $${sig.stopLoss?.toLocaleString()}`, `Confidence: ${sig.confidence}% | Status: ${sig.status}`, `📝 ${sig.reasoning}`, "");
158
- }
159
- return { content: [{ type: "text", text: lines.join("\n") }] };
160
- }
161
- case "get_signal_history": {
162
- const parsed = GetSignalHistorySchema.safeParse(args ?? {});
163
- if (!parsed.success)
164
- return { content: [{ type: "text", text: `Invalid input: ${String(parsed.error.issues[0].path[0])} ${parsed.error.issues[0].message}` }], isError: true };
165
- const token = parsed.data.token?.toUpperCase() ?? "BTC";
166
- const days = parsed.data.days ?? 7;
167
- const [hist, wr] = await Promise.all([
168
- (0, convex_js_1.callConvex)(`/signals/history?token=${token}&days=${days}`, "GET", undefined, "get_signal_history"),
169
- (0, convex_js_1.callConvex)(`/signals/winrate?token=${token}&days=${days}`, "GET", undefined, "get_signal_history"),
170
- ]);
171
- const lines = [
172
- `**${token} Signal History — Last ${days} days**`,
173
- `Total: ${wr.total} resolved | Wins: ${wr.wins} | Losses: ${wr.losses}`,
174
- `Winrate: ${wr.winrate}% | Avg PnL: ${Number(wr.avgPnl) >= 0 ? "+" : ""}${wr.avgPnl}%`,
175
- `Best: +${wr.bestPnl}% | Worst: ${wr.worstPnl}%`,
176
- "", "**Recent Signals:**",
177
- ];
178
- for (const sig of (hist.signals ?? []).slice(0, 5)) {
179
- const emoji = sig.signalType === "BUY" ? "🟢" : sig.signalType === "SELL" ? "🔴" : "🟡";
180
- const outcome = sig.isWin === true ? "✅" : sig.isWin === false ? "❌" : "⏳";
181
- const pnl = sig.pnlPercent != null ? ` (${sig.pnlPercent >= 0 ? "+" : ""}${sig.pnlPercent.toFixed(2)}%)` : "";
182
- lines.push(`${emoji} ${sig.token} ${sig.signalType} @ $${sig.entryPrice?.toLocaleString()} — ${outcome} ${sig.status}${pnl}`);
183
- }
184
- return { content: [{ type: "text", text: lines.join("\n") }] };
185
- }
186
- case "get_smart_money_alerts": {
187
- const parsed = GetSmartMoneyAlertsSchema.safeParse(args ?? {});
188
- if (!parsed.success)
189
- return { content: [{ type: "text", text: `Invalid input: hours ${parsed.error.issues[0].message}` }], isError: true };
190
- const hours = parsed.data.hours ?? 24;
191
- const data = await (0, convex_js_1.callConvex)(`/whales/latest?hours=${hours}`, "GET", undefined, "get_smart_money_alerts");
192
- if (!data.count)
193
- return { content: [{ type: "text", text: `No whale alerts in the last ${hours}h.` }] };
194
- const lines = [`**Whale Alerts — Last ${hours}h** (${data.count} total)`, ""];
195
- for (const alert of (data.alerts ?? []).slice(0, 5)) {
196
- const sig = alert.significance === "HIGH" ? "🔴" : "🟡";
197
- lines.push(`${sig} **${alert.token} | ${alert.direction}**`, `${alert.description}`, `💡 ${alert.implication}`, "");
198
- }
199
- return { content: [{ type: "text", text: lines.join("\n") }] };
200
- }
201
- case "get_daily_recap": {
202
- const parsed = GetDailyRecapSchema.safeParse(args ?? {});
203
- if (!parsed.success)
204
- return { content: [{ type: "text", text: `Invalid input: date ${parsed.error.issues[0].message}` }], isError: true };
205
- const date = parsed.data.date ?? new Date().toISOString().slice(0, 10);
206
- let data;
207
- try {
208
- data = await (0, convex_js_1.callConvex)("/recap/today", "GET", undefined, "get_daily_recap");
209
- }
210
- catch {
211
- return { content: [{ type: "text", text: `No recap available for ${date}` }] };
212
- }
55
+ const found = KNOWN.find((t) => new RegExp(`\\b${t}\\b`).test(q)) ?? "BTC";
56
+ const data = await (0, convex_js_1.callConvex)(`/swarm/memory/read?key=market/${found}`, "GET", undefined, "get_token_data");
213
57
  if (data.error)
214
- return { content: [{ type: "text", text: data.error }] };
58
+ return { content: [{ type: "text", text: `Error fetching ${found}: ${data.error}` }], isError: true };
59
+ const price = data.price_usd?.toLocaleString("en-US", { style: "currency", currency: "USD" });
60
+ const ch = data.change_24h_pct?.toFixed(2);
61
+ const sign = (data.change_24h_pct ?? 0) >= 0 ? "+" : "";
215
62
  const lines = [
216
- `**Noel Daily Recap — ${data.date ?? date}**`, "",
217
- `₿ **BTC** — ${data.btcWins}W / ${data.btcLosses}L | Winrate: ${data.btcWinrate?.toFixed(1)}%`,
218
- `Best: +${data.btcBestPnl?.toFixed(2)}% | Worst: ${data.btcWorstPnl?.toFixed(2)}%`, "",
219
- **ETH** — ${data.ethWins}W / ${data.ethLosses}L | Winrate: ${data.ethWinrate?.toFixed(1)}%`,
220
- `Best: +${data.ethBestPnl?.toFixed(2)}% | Worst: ${data.ethWorstPnl?.toFixed(2)}%`, "",
221
- `**Overall:** ${data.totalWinrate?.toFixed(1)}% winrate | Avg PnL: ${Number(data.avgPnl) >= 0 ? "+" : ""}${data.avgPnl?.toFixed(2)}% per signal`, "",
222
- `🤖 AI Review:`, data.aiReview ?? "No review",
63
+ `**${data.symbol}** — ${data.fetchedAt}`,
64
+ `Price: ${price} (${sign}${ch}% 24h)`,
65
+ `Market Cap: $${(data.market_cap_usd / 1e9).toFixed(2)}B`,
66
+ `Volume 24h: $${(data.volume_24h_usd / 1e9).toFixed(2)}B`,
67
+ `Source: ${data.source}`,
223
68
  ];
224
69
  return { content: [{ type: "text", text: lines.join("\n") }] };
225
70
  }
@@ -2,98 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NEWS_TOOLS = void 0;
4
4
  exports.handleNewsTool = handleNewsTool;
5
- const zod_1 = require("zod");
6
- const convex_js_1 = require("../convex.js");
7
- exports.NEWS_TOOLS = [
8
- {
9
- name: "get_news",
10
- description: "Get the latest crypto news digest — top stories, market-moving events, " +
11
- "regulatory updates, and AI-generated sentiment summary. Updated every 12 hours.",
12
- inputSchema: {
13
- type: "object",
14
- properties: {
15
- limit: {
16
- type: "number",
17
- description: "Number of news items to return (default: 10, max: 30)",
18
- },
19
- },
20
- required: [],
21
- },
22
- },
23
- {
24
- name: "generate_signal",
25
- description: "Manually trigger a fresh BTC and/or ETH trading signal right now — " +
26
- "instead of waiting for the 08:00 UTC daily cron. " +
27
- "Uses live market data, RSI, volume, and sentiment.",
28
- inputSchema: {
29
- type: "object",
30
- properties: {
31
- token: {
32
- type: "string",
33
- description: "Token to generate signal for: 'BTC', 'ETH', or omit for both.",
34
- },
35
- },
36
- required: [],
37
- },
38
- },
39
- ];
40
- const GetNewsSchema = zod_1.z.object({ limit: zod_1.z.number().min(1).max(30).optional() });
41
- const GenerateSignalSchema = zod_1.z.object({ token: zod_1.z.string().optional() });
42
- async function handleNewsTool(name, args) {
43
- switch (name) {
44
- case "get_news": {
45
- const parsed = GetNewsSchema.safeParse(args ?? {});
46
- if (!parsed.success) {
47
- return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
48
- }
49
- const limit = parsed.data.limit ?? 10;
50
- const data = await (0, convex_js_1.callConvex)(`/news/latest?limit=${limit}`, "GET", undefined, "get_news");
51
- const articles = data.articles ?? data.news ?? data.items ?? [];
52
- if (!articles.length) {
53
- return { content: [{ type: "text", text: "No recent news available. Try again shortly." }] };
54
- }
55
- const lines = [
56
- `**Crypto News Digest** — ${data.fetchedAt ?? data.date ?? new Date().toISOString().slice(0, 10)}`,
57
- ``,
58
- ];
59
- if (data.summary) {
60
- lines.push(`📊 **Summary:** ${data.summary}`, ``);
61
- }
62
- for (const a of articles.slice(0, limit)) {
63
- const sentiment = a.sentiment === "bullish" ? "🟢" : a.sentiment === "bearish" ? "🔴" : "⚪";
64
- lines.push(`${sentiment} **${a.title ?? a.headline ?? "Untitled"}**`);
65
- if (a.source)
66
- lines.push(` _${a.source}${a.publishedAt ? ` · ${a.publishedAt}` : ""}_`);
67
- if (a.summary ?? a.description)
68
- lines.push(` ${(a.summary ?? a.description ?? "").slice(0, 140)}…`);
69
- if (a.affectedTokens?.length)
70
- lines.push(` Tokens: ${a.affectedTokens.join(", ")}`);
71
- lines.push(``);
72
- }
73
- return { content: [{ type: "text", text: lines.join("\n") }] };
74
- }
75
- case "generate_signal": {
76
- const parsed = GenerateSignalSchema.safeParse(args ?? {});
77
- if (!parsed.success) {
78
- return { content: [{ type: "text", text: `Invalid input: ${parsed.error.issues[0].message}` }], isError: true };
79
- }
80
- const token = parsed.data.token?.toUpperCase();
81
- const data = await (0, convex_js_1.callConvex)("/signals/generate", "POST", { token }, "generate_signal");
82
- if (data.error) {
83
- return { content: [{ type: "text", text: `Signal generation failed: ${data.error}` }], isError: true };
84
- }
85
- const signals = data.signals ?? (data.signal ? [data.signal] : []);
86
- if (!signals.length) {
87
- return { content: [{ type: "text", text: "Signal generated. Use get_latest_signal to retrieve it." }] };
88
- }
89
- const lines = [`**Fresh Signals Generated**`, ``];
90
- for (const sig of signals) {
91
- const emoji = sig.signalType === "BUY" ? "🟢" : sig.signalType === "SELL" ? "🔴" : "🟡";
92
- lines.push(`${emoji} **${sig.token}/USD — ${sig.signalType}**`, `Entry: $${sig.entryPrice?.toLocaleString()} | TP1: $${sig.target1?.toLocaleString()}${sig.target2 ? ` | TP2: $${sig.target2?.toLocaleString()}` : ""} | SL: $${sig.stopLoss?.toLocaleString()}`, `Confidence: ${sig.confidence}% | Timeframe: 1H`, `📝 ${sig.reasoning}`, ``);
93
- }
94
- return { content: [{ type: "text", text: lines.join("\n") }] };
95
- }
96
- default:
97
- return null;
98
- }
99
- }
5
+ exports.NEWS_TOOLS = [];
6
+ async function handleNewsTool(_name, _args) { return null; }
@@ -79,11 +79,10 @@ async function handleSwarmTool(name, args) {
79
79
  const data = await (0, convex_js_1.callConvex)("/swarm/start", "POST", { config: parsed.data.config }, "start_swarm");
80
80
  if (!data.success)
81
81
  return { content: [{ type: "text", text: `Failed: ${data.error}` }], isError: true };
82
- const agents = data.activeAgents ?? [];
83
82
  return {
84
83
  content: [{
85
84
  type: "text",
86
- text: [`🤖 **Swarm Started**`, `Status: ${data.status}`, `Active agents (${agents.length}): ${agents.join(", ")}`, ``, `Use \`get_swarm_status\` to monitor, \`stop_swarm\` to stop.`].join("\n"),
85
+ text: [`🤖 **Swarm Started**`, `Session ID: ${data.sessionId}`, `Started at: ${data.startedAt}`, ``, `Use \`get_swarm_status\` to monitor, \`stop_swarm\` to stop.`].join("\n"),
87
86
  }],
88
87
  };
89
88
  }
@@ -97,12 +96,12 @@ async function handleSwarmTool(name, args) {
97
96
  const data = await (0, convex_js_1.callConvex)("/swarm/status", "GET", undefined, "get_swarm_status");
98
97
  if (data.error)
99
98
  return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
100
- const job = data.job;
99
+ const session = data.session;
101
100
  const memory = data.memory ?? [];
102
101
  const scores = data.scores ?? [];
103
102
  const lines = [
104
103
  `🤖 **Swarm Status**`,
105
- job ? `Status: ${job.status} | Agents: ${(job.activeAgents ?? []).join(", ")}` : `No active swarm.`,
104
+ data.active && session ? `Status: active | Session: ${session.id}` : `No active swarm.`,
106
105
  ``,
107
106
  ];
108
107
  if (memory.length > 0) {
@@ -133,7 +132,7 @@ async function handleSwarmTool(name, args) {
133
132
  const parsed = GetMemorySchema.safeParse(args);
134
133
  if (!parsed.success)
135
134
  return { content: [{ type: "text", text: `Invalid input: key ${parsed.error.issues[0].message}` }], isError: true };
136
- const data = await (0, convex_js_1.callConvex)(`/swarm/memory?key=${encodeURIComponent(parsed.data.key)}`, "GET", undefined, "get_swarm_memory");
135
+ const data = await (0, convex_js_1.callConvex)(`/swarm/memory/read?key=${encodeURIComponent(parsed.data.key)}`, "GET", undefined, "get_swarm_memory");
137
136
  if (data.error)
138
137
  return { content: [{ type: "text", text: `Error: ${data.error}` }], isError: true };
139
138
  if (data.value === null || data.value === undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noelclaw/mcp",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "Noelclaw as an MCP skill — persistent memory, multi-agent coordination, scenario simulation, DeFi execution, and Sentinel-gated playbooks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {