@noelclaw/mcp 1.0.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.
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.INSIGHT_TOOLS = void 0;
4
+ exports.handleInsightTool = handleInsightTool;
5
+ const zod_1 = require("zod");
6
+ const convex_js_1 = require("../convex.js");
7
+ exports.INSIGHT_TOOLS = [
8
+ {
9
+ name: "get_insight",
10
+ description: "Get Noel's daily crypto + macro insight. Covers Bitcoin/ETH price action, macro events, trending narratives on X/Twitter, and one actionable takeaway.",
11
+ inputSchema: { type: "object", properties: {}, required: [] },
12
+ },
13
+ {
14
+ name: "ask_noel",
15
+ description: "Ask Noel AI for DeFi analysis, trade ideas, market outlook, and crypto research. Noel has live market context.",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ question: { type: "string", description: "Your question or request for Noel" },
20
+ messages: {
21
+ type: "array",
22
+ description: "Previous conversation messages for context (optional)",
23
+ items: {
24
+ type: "object",
25
+ properties: { role: { type: "string", enum: ["user", "assistant"] }, content: { type: "string" } },
26
+ required: ["role", "content"],
27
+ },
28
+ },
29
+ },
30
+ required: ["question"],
31
+ },
32
+ },
33
+ ];
34
+ const AskNoelSchema = zod_1.z.object({
35
+ question: zod_1.z.string().min(1),
36
+ messages: zod_1.z.array(zod_1.z.object({ role: zod_1.z.enum(["user", "assistant"]), content: zod_1.z.string() })).optional(),
37
+ });
38
+ async function handleInsightTool(name, args) {
39
+ switch (name) {
40
+ case "get_insight": {
41
+ const data = await (0, convex_js_1.callConvex)("/insights/now", "GET", undefined, "get_insight");
42
+ const insight = data.insight ?? data.text ?? data.choices?.[0]?.message?.content ?? "Failed to get insight";
43
+ return { content: [{ type: "text", text: insight }] };
44
+ }
45
+ case "ask_noel": {
46
+ const parsed = AskNoelSchema.safeParse(args);
47
+ if (!parsed.success)
48
+ return { content: [{ type: "text", text: `Invalid input: question ${parsed.error.issues[0].message}` }], isError: true };
49
+ const data = await (0, convex_js_1.callConvex)("/mcp/chat", "POST", {
50
+ question: parsed.data.question,
51
+ agentId: "noel-default",
52
+ messages: parsed.data.messages ?? [],
53
+ }, "ask_noel");
54
+ return { content: [{ type: "text", text: data.answer ?? JSON.stringify(data) }] };
55
+ }
56
+ default:
57
+ return null;
58
+ }
59
+ }
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MARKET_TOOLS = void 0;
4
+ exports.handleMarketTool = handleMarketTool;
5
+ const zod_1 = require("zod");
6
+ const convex_js_1 = require("../convex.js");
7
+ exports.MARKET_TOOLS = [
8
+ {
9
+ name: "get_market_data",
10
+ description: "Get live crypto market data: top 20 coins by market cap, trending coins, and key prices for BTC/ETH/SOL. Results are also sent to your Telegram if configured.",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: { token: { type: "string", description: "Optional: specific token to focus on, e.g. 'BTC', 'ETH'" } },
14
+ required: [],
15
+ },
16
+ },
17
+ {
18
+ name: "get_token_data",
19
+ description: "Get market data for specific tokens. Returns price, 24h change, market cap, and volume. Results are sent to your Telegram if configured.",
20
+ inputSchema: {
21
+ type: "object",
22
+ properties: { question: { type: "string", description: "Describe which tokens to look up" } },
23
+ required: ["question"],
24
+ },
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
+ ];
66
+ const GetMarketDataSchema = zod_1.z.object({ token: zod_1.z.string().optional() });
67
+ 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
+ async function handleMarketTool(name, args) {
73
+ switch (name) {
74
+ case "get_market_data": {
75
+ const parsed = GetMarketDataSchema.safeParse(args ?? {});
76
+ if (!parsed.success)
77
+ return { content: [{ type: "text", text: `Invalid input: token ${parsed.error.issues[0].message}` }], isError: true };
78
+ 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
+ }
111
+ }
112
+ return { content: [{ type: "text", text: lines.join("\n") }] };
113
+ }
114
+ case "get_token_data": {
115
+ const parsed = GetTokenDataSchema.safeParse(args);
116
+ if (!parsed.success)
117
+ return { content: [{ type: "text", text: `Invalid input: question ${parsed.error.issues[0].message}` }], isError: true };
118
+ // Extract token symbol from natural-language question
119
+ const q = parsed.data.question.toUpperCase();
120
+ 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
+ }
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
+ default:
227
+ return null;
228
+ }
229
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MIROSHARK_TOOLS = void 0;
4
+ exports.handleMirosharkTool = handleMirosharkTool;
5
+ exports.MIROSHARK_TOOLS = [
6
+ {
7
+ name: "miroshark_simulate",
8
+ description: "Run a MiroShark multi-agent simulation. Describe a scenario in plain English and get back strategic insights from a network of AI agents (market actors, risk managers, analysts). " +
9
+ "Returns a simulation ID you can poll with miroshark_status.",
10
+ inputSchema: {
11
+ type: "object",
12
+ properties: {
13
+ scenario: {
14
+ type: "string",
15
+ description: "Plain-English description of the scenario to simulate. E.g. 'What happens if ETH drops 20% and whale wallets start selling?'",
16
+ },
17
+ agents: {
18
+ type: "number",
19
+ description: "Number of agents in the simulation (default: 10, max: 50)",
20
+ },
21
+ steps: {
22
+ type: "number",
23
+ description: "Number of simulation steps to run (default: 5)",
24
+ },
25
+ },
26
+ required: ["scenario"],
27
+ },
28
+ },
29
+ {
30
+ name: "miroshark_status",
31
+ description: "Poll the status and results of a MiroShark simulation by ID. Returns agent outputs, consensus findings, and final strategic insights when complete.",
32
+ inputSchema: {
33
+ type: "object",
34
+ properties: {
35
+ simulation_id: {
36
+ type: "string",
37
+ description: "Simulation ID returned by miroshark_simulate",
38
+ },
39
+ },
40
+ required: ["simulation_id"],
41
+ },
42
+ },
43
+ ];
44
+ function getConfig() {
45
+ const url = process.env.MIROSHARK_URL?.replace(/\/$/, "");
46
+ const token = process.env.MIROSHARK_ADMIN_TOKEN;
47
+ return { url, token };
48
+ }
49
+ async function mirofetch(path, method, body) {
50
+ const { url, token } = getConfig();
51
+ if (!url)
52
+ throw new Error("MIROSHARK_URL not set");
53
+ if (!token)
54
+ throw new Error("MIROSHARK_ADMIN_TOKEN not set");
55
+ const res = await fetch(`${url}${path}`, {
56
+ method,
57
+ headers: {
58
+ "Content-Type": "application/json",
59
+ "Authorization": `Bearer ${token}`,
60
+ },
61
+ ...(body ? { body: JSON.stringify(body) } : {}),
62
+ signal: AbortSignal.timeout(30000),
63
+ });
64
+ if (!res.ok) {
65
+ const err = await res.text();
66
+ throw new Error(`MiroShark ${res.status}: ${err.slice(0, 200)}`);
67
+ }
68
+ return res.json();
69
+ }
70
+ async function handleMirosharkTool(name, args) {
71
+ const a = (args ?? {});
72
+ if (name === "miroshark_simulate") {
73
+ if (!a.scenario?.trim()) {
74
+ return { content: [{ type: "text", text: "scenario is required" }], isError: true };
75
+ }
76
+ const { url, token } = getConfig();
77
+ if (!url || !token) {
78
+ return {
79
+ content: [{ type: "text", text: "MiroShark not configured โ€” set MIROSHARK_URL and MIROSHARK_ADMIN_TOKEN" }],
80
+ isError: true,
81
+ };
82
+ }
83
+ try {
84
+ // Step 1: ask (parse scenario into simulation params)
85
+ const asked = await mirofetch("/api/simulation/ask", "POST", { question: a.scenario });
86
+ // Step 2: create simulation
87
+ const created = await mirofetch("/api/simulation/create", "POST", {
88
+ ...asked,
89
+ num_agents: Math.min(a.agents ?? 10, 50),
90
+ num_steps: a.steps ?? 5,
91
+ });
92
+ const simId = created.simulation_id ?? created.id;
93
+ if (!simId)
94
+ throw new Error("No simulation ID in create response");
95
+ // Step 3: prepare
96
+ await mirofetch(`/api/simulation/${simId}/prepare`, "POST", {});
97
+ // Step 4: start
98
+ await mirofetch(`/api/simulation/${simId}/start`, "POST", {});
99
+ return {
100
+ content: [{
101
+ type: "text",
102
+ text: [
103
+ `๐Ÿฆˆ **MiroShark simulation started**`,
104
+ `Scenario: ${a.scenario}`,
105
+ `Simulation ID: \`${simId}\``,
106
+ `Agents: ${a.agents ?? 10} ยท Steps: ${a.steps ?? 5}`,
107
+ ``,
108
+ `Poll results with: \`miroshark_status simulation_id="${simId}"\``,
109
+ ].join("\n"),
110
+ }],
111
+ };
112
+ }
113
+ catch (err) {
114
+ return { content: [{ type: "text", text: `MiroShark error: ${err.message}` }], isError: true };
115
+ }
116
+ }
117
+ if (name === "miroshark_status") {
118
+ if (!a.simulation_id?.trim()) {
119
+ return { content: [{ type: "text", text: "simulation_id is required" }], isError: true };
120
+ }
121
+ try {
122
+ const data = await mirofetch(`/api/simulation/${a.simulation_id}/status`, "GET");
123
+ const status = data.status ?? "unknown";
124
+ const lines = [
125
+ `๐Ÿฆˆ **MiroShark Simulation \`${a.simulation_id}\`**`,
126
+ `Status: **${status}**`,
127
+ ];
128
+ if (data.progress != null)
129
+ lines.push(`Progress: ${data.progress}%`);
130
+ if (status === "completed" && data.results) {
131
+ lines.push("", "**Results**");
132
+ if (data.results.summary)
133
+ lines.push(data.results.summary);
134
+ if (Array.isArray(data.results.insights)) {
135
+ for (const insight of data.results.insights.slice(0, 5)) {
136
+ lines.push(`โ€ข ${typeof insight === "string" ? insight : JSON.stringify(insight)}`);
137
+ }
138
+ }
139
+ if (data.results.consensus)
140
+ lines.push("", `**Consensus:** ${data.results.consensus}`);
141
+ }
142
+ else if (status === "failed") {
143
+ lines.push("", `Error: ${data.error ?? "unknown"}`);
144
+ }
145
+ else {
146
+ lines.push("", "Simulation still running โ€” poll again in a few seconds.");
147
+ }
148
+ return { content: [{ type: "text", text: lines.join("\n") }] };
149
+ }
150
+ catch (err) {
151
+ return { content: [{ type: "text", text: `MiroShark error: ${err.message}` }], isError: true };
152
+ }
153
+ }
154
+ return null;
155
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NEWS_TOOLS = void 0;
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
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESEARCH_TOOLS = void 0;
4
+ exports.handleResearchTool = handleResearchTool;
5
+ const zod_1 = require("zod");
6
+ const convex_js_1 = require("../convex.js");
7
+ exports.RESEARCH_TOOLS = [
8
+ {
9
+ name: "research",
10
+ description: "Research any crypto topic on demand โ€” like Perplexity but for crypto. Ask about a token, protocol, market event, or trend. Noel searches the web and returns a structured analysis.",
11
+ inputSchema: {
12
+ type: "object",
13
+ properties: { query: { type: "string", description: "Topic to research" } },
14
+ required: ["query"],
15
+ },
16
+ },
17
+ ];
18
+ const ResearchSchema = zod_1.z.object({ query: zod_1.z.string().min(1) });
19
+ async function handleResearchTool(name, args) {
20
+ if (name !== "research")
21
+ return null;
22
+ const parsed = ResearchSchema.safeParse(args);
23
+ if (!parsed.success)
24
+ return { content: [{ type: "text", text: `Invalid input: query ${parsed.error.issues[0].message}` }], isError: true };
25
+ const data = await (0, convex_js_1.callConvex)("/mcp/research", "POST", { query: parsed.data.query }, "research");
26
+ if (!data.success)
27
+ return { content: [{ type: "text", text: `Research failed: ${data.error ?? "unknown error"}` }] };
28
+ return { content: [{ type: "text", text: data.text ?? "No results returned." }] };
29
+ }