@noelclaw/mcp 1.2.0 → 1.3.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");
@@ -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": {
@@ -141,88 +98,6 @@ async function handleMarketTool(name, args) {
141
98
  }
142
99
  return { content: [{ type: "text", text: lines.join("\n") }] };
143
100
  }
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
- }
213
- if (data.error)
214
- return { content: [{ type: "text", text: data.error }] };
215
- 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",
223
- ];
224
- return { content: [{ type: "text", text: lines.join("\n") }] };
225
- }
226
101
  default:
227
102
  return null;
228
103
  }
@@ -164,30 +164,31 @@ async function handleMirosharkTool(name, args) {
164
164
  }
165
165
  const simId = a.simulation_id.trim();
166
166
  try {
167
- // Check if agents are still being prepared
168
- const prepStatus = await miroJson("/miroshark/api/simulation/prepare/status", "POST", { simulation_id: simId }).catch(() => null);
169
- const prepState = (prepStatus?.status ?? "").toLowerCase();
170
- if (prepState === "processing" || prepState === "pending") {
171
- const pct = prepStatus?.progress ?? 0;
172
- const msg = prepStatus?.message ?? "preparing agents...";
173
- return {
174
- content: [{
175
- type: "text",
176
- text: [
177
- `**MiroShark \`${simId}\`** — preparing agents`,
178
- `Progress: ${pct}%`,
179
- `${msg}`,
180
- ``,
181
- `Poll again in a few seconds.`,
182
- ].join("\n"),
183
- }],
184
- };
185
- }
186
- // Agents ready (or prepare already done) — check run status
167
+ // Check run status first
187
168
  const runStatus = await miroJson(`/miroshark/api/simulation/${simId}/run-status`, "GET").catch(() => ({ runner_status: "idle" }));
188
169
  const runnerStatus = (runStatus?.runner_status ?? "idle").toLowerCase();
189
- // Auto-start the simulation when agents are ready but not yet running
190
- if (runnerStatus === "idle" && (prepState === "ready" || prepState === "completed" || prepStatus?.already_prepared)) {
170
+ // If not yet running, check whether agents are prepared by probing /config
171
+ // (config only exists after /prepare completes)
172
+ if (runnerStatus === "idle") {
173
+ const config = await miroJson(`/miroshark/api/simulation/${simId}/config`, "GET").catch(() => null);
174
+ if (!config) {
175
+ // Preparation still in progress — check profiles for real-time progress
176
+ const profiles = await miroJson(`/miroshark/api/simulation/${simId}/profiles/realtime`, "GET").catch(() => null);
177
+ const total = profiles?.total_expected ?? "?";
178
+ const ready = profiles?.profiles_ready ?? 0;
179
+ return {
180
+ content: [{
181
+ type: "text",
182
+ text: [
183
+ `**MiroShark \`${simId}\`** — preparing agents`,
184
+ total !== "?" ? `Profiles: ${ready} / ${total} ready` : `Profiles generating...`,
185
+ ``,
186
+ `Poll again in ~10 seconds.`,
187
+ ].join("\n"),
188
+ }],
189
+ };
190
+ }
191
+ // Config exists → agents prepared → auto-start
191
192
  await miroJson("/miroshark/api/simulation/start", "POST", {
192
193
  simulation_id: simId,
193
194
  platform: "parallel",
@@ -251,7 +252,6 @@ async function handleMirosharkTool(name, args) {
251
252
  type: "text",
252
253
  text: [
253
254
  `**MiroShark \`${simId}\`** — status: ${runnerStatus || "unknown"}`,
254
- prepState ? `Prepare state: ${prepState}` : "",
255
255
  ``,
256
256
  `If agents are still preparing, poll again shortly.`,
257
257
  ].filter(Boolean).join("\n"),
@@ -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; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noelclaw/mcp",
3
- "version": "1.2.0",
3
+ "version": "1.3.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": {