@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 +3 -4
- package/dist/tools/defi.js +0 -134
- package/dist/tools/humanizer.js +3 -2
- package/dist/tools/market.js +21 -176
- package/dist/tools/news.js +2 -95
- package/dist/tools/swarm.js +4 -5
- 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/humanizer.js
CHANGED
|
@@ -94,7 +94,7 @@ async function handleHumanizerTool(name, args) {
|
|
|
94
94
|
"Authorization": `Bearer ${apiKey}`,
|
|
95
95
|
},
|
|
96
96
|
body: JSON.stringify({
|
|
97
|
-
model: "MiniMax-
|
|
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
|
|
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 }] };
|
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": {
|
|
@@ -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
|
|
80
|
-
const
|
|
81
|
-
const lines = [`**Market Data** — ${
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
`
|
|
219
|
-
|
|
220
|
-
`
|
|
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
|
}
|
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/dist/tools/swarm.js
CHANGED
|
@@ -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**`, `
|
|
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
|
|
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
|
-
|
|
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.
|
|
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": {
|