@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 +3 -4
- package/dist/tools/defi.js +0 -134
- package/dist/tools/market.js +0 -125
- package/dist/tools/miroshark.js +23 -23
- package/dist/tools/news.js +2 -95
- package/package.json +1 -1
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, //
|
|
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, //
|
|
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:
|
|
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 }));
|
package/dist/tools/defi.js
CHANGED
|
@@ -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");
|
package/dist/tools/market.js
CHANGED
|
@@ -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
|
}
|
package/dist/tools/miroshark.js
CHANGED
|
@@ -164,30 +164,31 @@ async function handleMirosharkTool(name, args) {
|
|
|
164
164
|
}
|
|
165
165
|
const simId = a.simulation_id.trim();
|
|
166
166
|
try {
|
|
167
|
-
// Check
|
|
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
|
-
//
|
|
190
|
-
|
|
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"),
|
package/dist/tools/news.js
CHANGED
|
@@ -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
|
-
|
|
6
|
-
|
|
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.
|
|
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": {
|