mcp-server-madeonsol 1.26.1 → 1.27.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/README.md CHANGED
@@ -13,6 +13,8 @@ MCP server for [MadeOnSol](https://madeonsol.com) Solana KOL intelligence API. U
13
13
 
14
14
  > Real-time Solana trading intelligence: track 1,069 KOL wallets with <3s latency on paid keys and x402 pay-per-call (free-tier live feeds are 5-min delayed), score 23,000+ Pump.fun deployers, surface deshred deploy signals **~500ms before on-chain confirmation**, detect multi-KOL coordination, surface bundle-cohort holdings (which same-slot wallets still hold a token's supply), verify any wallet's CURRENT on-chain holdings straight from its token accounts, and stream every DEX trade across 9+ programs. Free tier: 200 requests/day across 40+ endpoints (live feeds 5-min delayed) — no signup payment. Get a key at [madeonsol.com/pricing](https://madeonsol.com/pricing).
15
15
 
16
+ > **New in 1.27.0 — top traders, sniper watchlist management, and two update tools (104 tools total).** Six additions found by an internal agentic-infra coverage audit. `madeonsol_token_top_traders` (`GET /tokens/{mint}/top-traders`, PRO/ULTRA) ranks a token's wallets by realized PnL or ROI, enriched with KOL/alpha-wallet identity — this endpoint already existed on the REST API and x402 rail but had no MCP tool. `madeonsol_sniper_watchlist_list` / `_add` / `_remove` (PRO+/ULTRA) manage your custom sniper-deploy watchlist (`madeonsol_sniper_recent(watchlist: true)` narrows to it) — previously only reachable via raw HTTP. `madeonsol_update_webhook` (PATCH) changes a webhook's URL, events, or active state without deleting and recreating it. `madeonsol_wallet_tracker_relabel` (PATCH) renames or clears a tracked wallet's label.
17
+ >
16
18
  > **New in 1.26.0 — deployer reputation as-of a date, and creator-fee rewards.** Two new tools (PRO+, keyed `msk_` API only — no x402 route). `madeonsol_deployer_as_of` binds `GET /deployer-hunter/{wallet}/as-of`: the deployer's reputation exactly as it stood on `date` (default today, UTC) — the latest write-on-change snapshot at or before it, so an agent backtests without look-ahead bias. `snapshot.snapshot_date` can predate `date` (write-on-change); `snapshot.carried: true` marks that. No snapshot at or before `date` → `as_of: false, snapshot: null` — nothing is ever synthesized. `date` must be ≥ 2026-04-07 and not in the future. `madeonsol_deployer_rewards` binds `GET /deployer-hunter/{wallet}/rewards`: pump.fun creator-fee rewards, answered two ways that are never merged — `collected` (what actually reached the wallet: direct vault claims kept 90 days, social-handle claims, shareholder payouts on **any** token) and `attributed` (every payout on the tokens it **deployed**, split `to_self`/`to_others` + `redirected_pct`). Every money field is `{sol, usdc, usd}`; `usd` is `null` (never a silent 0) when a SOL amount exists and no SOL price was available. `top_tokens`/`top_recipients` (≤10, USD-sorted) show where attributed fees went. Works for non-deployers too (`is_deployer: false`, `attributed` empty).
17
19
 
18
20
  > **New in 1.25.0 — token surges & revivals: momentum fires with the honest half attached.** The new `madeonsol_tokens_surges` tool binds `GET /tokens/surges` (PRO+, keyed `msk_` API only — no x402 route): every token momentum fire, newest first. Two kinds, one row shape. **`surge`** — a token < 30 min old whose market cap runs hard against its *launch* MC, in three tiers that each fire at most once per mint: `early` (≤10 min, ≥$12k, ≥3× launch), `strong` (≤30 min, ≥$30k, ≥6× launch **and** ≥2× the lowest sample of the last 3 min — it is climbing *now*), `breakout` (≤2 min, ≥$45k, ≥8×). A tier must be **sustained** (current tick *and* a sample ≥10 s older; nothing fires before 20 s of age) — a same-slot bundle marked to $475k at age 1 s is a spike, not a surge. **`revival`** — a token with no 1-minute trade candle for ≥24 h that starts trading again, confirmed **only by the tape** (≥5 buys, ≥$500 buy volume, MC ≥1.5× the pre-dormancy close), never by the price mark; `tier` is `null`. Hard gates on both: liquidity ≥$1.5k and ≥2 % of MC, and the MC gained must be **paid for** by buy volume (a price mark in a spoof pool moves MC on ~$0). Every row carries the burst `tape` (`source` candles / wallet_trades; `unique_buyers` only where the mint is in trade coverage — `wallet_data_available:false` otherwise, never an inferred zero), `kol` buyers, the first-20 `early_buyers` cohort (bundled / sold / sniper wallets), `deployer` reputation and `risk_flags[]` (`bundled_launch`, `few_buyers`, `wash_pattern`, `thin_liquidity`, `cold_deployer`, `sniper_heavy`, `early_buyers_exiting`, `sell_pressure`, `no_tape_trades`, `no_prior_price`, `mint_authority_active`, `transfer_fee` — empty means no flag raised, not verified clean). Rows ≥65 min old carry the +1 h `outcome`; `stats=1` prints per-(kind, tier) hit-rates (`up_1h_pct`, `median_peak_multiple`, `doubled_1h_pct`) — out-of-sample by construction. Filters `kind`, `tier`, `mint`, `launchpad`, `deployer_tier`, `min_mc_usd` / `max_mc_usd`, `min_buys`, `exclude_flags`, `only_clean`; cursors `since` / `before`. Pushed live on the new **`token:surges`** WS channel (events `token:surge` / `token:revival`; subscribe filters `kinds[]`, `tiers[]`, `launchpads[]`, `exclude_flags[]`, `min_mc_usd` / `max_mc_usd`, `deployer_tier[]`) and accepted by `madeonsol_create_webhook` as events `token:surge` / `token:revival` with the same filters. The response echoes the live thresholds in `definitions`.
package/dist/index.js CHANGED
@@ -375,6 +375,49 @@ function registerTools(server) {
375
375
  }
376
376
  return { content: [{ type: "text", text: JSON.stringify(await res.json(), null, 2) }] };
377
377
  });
378
+ server.tool("madeonsol_sniper_watchlist_list", "List your custom sniper watchlist (tracked deployer wallets, any tier). PRO+/ULTRA only. Added 2026-09-10.", {}, { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async () => {
379
+ if (authMode !== "madeonsol")
380
+ return { content: [{ type: "text", text: "Sniper watchlist requires MADEONSOL_API_KEY (msk_)." }] };
381
+ const res = await fetch(`${BASE_URL}/api/v1/sniper/watchlist`, { headers: apiKeyHeaders() });
382
+ if (!res.ok) {
383
+ const body = await res.text().catch(() => "");
384
+ return { content: [{ type: "text", text: `Error ${res.status}: ${body}` }] };
385
+ }
386
+ return { content: [{ type: "text", text: JSON.stringify(await res.json(), null, 2) }] };
387
+ });
388
+ server.tool("madeonsol_sniper_watchlist_add", "Add one or many deployer wallets to your sniper watchlist — narrows madeonsol_sniper_recent(watchlist:true) to just these. PRO+/ULTRA only. Added 2026-09-10.", {
389
+ wallet: z.string().optional().describe("Single deployer wallet address (base58) — provide this or `wallets`"),
390
+ wallets: z.array(z.string()).optional().describe("Multiple deployer wallet addresses (base58, up to 50) — provide this or `wallet`"),
391
+ label: z.string().optional().describe("Optional human-readable label"),
392
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async ({ wallet, wallets, label }) => {
393
+ if (authMode !== "madeonsol")
394
+ return { content: [{ type: "text", text: "Sniper watchlist requires MADEONSOL_API_KEY (msk_)." }] };
395
+ const body = {};
396
+ if (wallet)
397
+ body.wallet = wallet;
398
+ if (wallets)
399
+ body.wallets = wallets;
400
+ if (label)
401
+ body.label = label;
402
+ const res = await fetch(`${BASE_URL}/api/v1/sniper/watchlist`, { method: "POST", headers: { "Content-Type": "application/json", ...apiKeyHeaders() }, body: JSON.stringify(body) });
403
+ if (!res.ok) {
404
+ const respBody = await res.text().catch(() => "");
405
+ return { content: [{ type: "text", text: `Error ${res.status}: ${respBody}` }] };
406
+ }
407
+ return { content: [{ type: "text", text: JSON.stringify(await res.json(), null, 2) }] };
408
+ });
409
+ server.tool("madeonsol_sniper_watchlist_remove", "Remove a deployer wallet from your sniper watchlist. PRO+/ULTRA only. Added 2026-09-10.", {
410
+ wallet: z.string().describe("Deployer wallet address (base58) to remove"),
411
+ }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }, async ({ wallet }) => {
412
+ if (authMode !== "madeonsol")
413
+ return { content: [{ type: "text", text: "Sniper watchlist requires MADEONSOL_API_KEY (msk_)." }] };
414
+ const res = await fetch(`${BASE_URL}/api/v1/sniper/watchlist/${encodeURIComponent(wallet)}`, { method: "DELETE", headers: apiKeyHeaders() });
415
+ if (!res.ok) {
416
+ const body = await res.text().catch(() => "");
417
+ return { content: [{ type: "text", text: `Error ${res.status}: ${body}` }] };
418
+ }
419
+ return { content: [{ type: "text", text: JSON.stringify(await res.json(), null, 2) }] };
420
+ });
378
421
  server.tool("madeonsol_discovery", "List all available MadeOnSol API endpoints with prices and parameter docs. Free, no auth required. The keyless x402 catalog now covers 25 endpoints — recent additions: token candles ($0.01), almost-bonded ($0.01), top-traders ($0.02), cap-table ($0.02), sniper recent deploys ($0.01), token flow ($0.01), and deployer trajectory ($0.01).", {}, { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, async () => {
379
422
  const res = await fetch(new URL("/api/x402", BASE_URL).toString());
380
423
  const data = await res.json();
@@ -414,6 +457,12 @@ function registerTools(server) {
414
457
  }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }, async ({ wallet_address }) => ({
415
458
  content: [{ type: "text", text: await walletTrackerRequest("DELETE", `/wallet-tracker/watchlist/${encodeURIComponent(wallet_address)}`) }],
416
459
  }));
460
+ server.tool("madeonsol_wallet_tracker_relabel", "Rename (or clear) the label on a wallet already in your watchlist. Added 2026-09-10.", {
461
+ wallet_address: z.string().describe("Solana wallet address (base58) already on your watchlist"),
462
+ label: z.string().nullable().describe("New label, or null to clear it"),
463
+ }, { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async ({ wallet_address, label }) => ({
464
+ content: [{ type: "text", text: await walletTrackerRequest("PATCH", `/wallet-tracker/watchlist/${encodeURIComponent(wallet_address)}`, { label }) }],
465
+ }));
417
466
  server.tool("madeonsol_wallet_tracker_trades", "Historical swap and transfer events for all your watched wallets. BASIC: truncated wallets, no tx_signature.", {
418
467
  wallet: z.string().optional().describe("Filter to a specific wallet address"),
419
468
  action: z.enum(["buy", "sell", "transfer_in", "transfer_out"]).optional().describe("Filter by action type"),
@@ -570,6 +619,21 @@ function registerTools(server) {
570
619
  }, { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true }, async ({ id }) => ({
571
620
  content: [{ type: "text", text: await restQuery("DELETE", `/webhooks/${id}`) }],
572
621
  }));
622
+ server.tool("madeonsol_update_webhook", "Update a webhook's URL, subscribed events, or active state. Only the fields you pass are changed. Added 2026-09-10.", {
623
+ id: z.number().describe("Webhook ID to update"),
624
+ url: z.string().url().optional().describe("New HTTPS webhook URL"),
625
+ events: z.array(z.enum(["kol:trade", "kol:coordination", "deployer:alert", "deployer:bond"])).optional().describe("Replace the subscribed event types"),
626
+ is_active: z.boolean().optional().describe("Enable or disable this webhook"),
627
+ }, webhookAnnotations, async ({ id, url, events, is_active }) => {
628
+ const body = {};
629
+ if (url !== undefined)
630
+ body.url = url;
631
+ if (events !== undefined)
632
+ body.events = events;
633
+ if (is_active !== undefined)
634
+ body.is_active = is_active;
635
+ return { content: [{ type: "text", text: await restQuery("PATCH", `/webhooks/${id}`, body) }] };
636
+ });
573
637
  server.tool("madeonsol_test_webhook", "Send a sample event payload to a webhook URL to verify it works. Returns status code and response time.", {
574
638
  webhook_id: z.number().describe("ID of the webhook to test"),
575
639
  }, { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async ({ webhook_id }) => ({
@@ -849,6 +913,26 @@ function registerTools(server) {
849
913
  const path = `/tokens/${encodeURIComponent(mint)}/flow${query ? `?${query}` : ""}`;
850
914
  return { content: [{ type: "text", text: await restQuery("GET", path) }] };
851
915
  });
916
+ server.tool("madeonsol_token_top_traders", "The wallets that made (or lost) the most on a token, ranked by realized PnL or ROI (up to 25 on PRO, 100 on ULTRA). Each trader enriched with KOL identity and alpha-wallet reputation (bot_confidence, historical win rate/PnL) so you can tell smart money from bots. Added 2026-09-10 — this tool was missing despite the endpoint existing on every other surface. PRO/ULTRA only.", {
917
+ mint: z.string().describe("Token mint address (base58)"),
918
+ limit: z.number().min(1).max(100).optional().describe("Max traders returned — 1-25 PRO, 1-100 ULTRA (default 10)"),
919
+ sort: z.enum(["pnl", "roi"]).optional().describe("Rank by realized PnL (default) or ROI"),
920
+ window_days: z.number().min(1).max(180).optional().describe("Lookback window in days (default 90)"),
921
+ min_bought_sol: z.number().optional().describe("Exclude traders below this total SOL bought (default 0.1)"),
922
+ }, { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true }, async ({ mint, limit, sort, window_days, min_bought_sol }) => {
923
+ const qs = new URLSearchParams();
924
+ if (limit !== undefined)
925
+ qs.set("limit", String(limit));
926
+ if (sort !== undefined)
927
+ qs.set("sort", sort);
928
+ if (window_days !== undefined)
929
+ qs.set("window_days", String(window_days));
930
+ if (min_bought_sol !== undefined)
931
+ qs.set("min_bought_sol", String(min_bought_sol));
932
+ const query = qs.toString();
933
+ const path = `/tokens/${encodeURIComponent(mint)}/top-traders${query ? `?${query}` : ""}`;
934
+ return { content: [{ type: "text", text: await restQuery("GET", path) }] };
935
+ });
852
936
  server.tool("madeonsol_token_trades", "Mint-scoped trade tape — cursor-paginated raw trades for one token, newest first (the backfill/history complement to the live DEX firehose stream). Each trade: tx_signature, wallet_address, action (buy|sell), sol_amount, token_amount, price_sol|null, price_usd|null, market_price_sol|null, market_price_usd|null, early_buyer_rank|null, slot|null, block_time (unix sec), traded_at (ISO). TWO PRICES, and picking the wrong one gives wrong answers: price_sol is THIS trade's executed price (sol_amount / token_amount — the trader's all-in rate, including swap fee and any account rent, not the pool mid), while market_price_sol is the canonical pool price sampled near that slot and is SHARED by every trade in the slot. Use price_sol for cost basis, fills and PnL; use market_price_sol for a per-token price series independent of trade size and direction. Filters: action, wallet, since/until (unix seconds — defaults to FULL history, not 90d). Pass next_cursor from the previous response to page older trades; has_more tells you when to stop. Coverage honesty: capture starts 2026-04-12 and is pump.fun-pipeline scoped — the response carries coverage.history_start + coverage.scope so agents can reason about gaps. PRO/ULTRA only.", {
853
937
  mint: z.string().describe("Token mint address (base58)"),
854
938
  limit: z.number().min(1).max(500).default(100).describe("Trades per page (1-500, default 100)"),
@@ -1260,6 +1344,7 @@ async function main() {
1260
1344
  { name: "madeonsol_token_buyer_quality", description: "0–100 buyer quality score for a token's first-buyer cohort." },
1261
1345
  { name: "madeonsol_token_depth", description: "Per-pool price impact / slippage — quotes per SOL buy size + SOL to move price 1%/5%/10%; unsupported pools flagged with a reason. PRO+." },
1262
1346
  { name: "madeonsol_token_candles", description: "Historical OHLCV price candles (1m–1d). PRO=OHLCV 30d; ULTRA=+net flow, liquidity delta, full history." },
1347
+ { name: "madeonsol_token_top_traders", description: "The wallets that made or lost the most on a token, ranked by PnL or ROI, enriched with KOL/alpha-wallet identity. PRO/ULTRA." },
1263
1348
  { name: "madeonsol_token_trades", description: "Mint-scoped trade tape — cursor-paginated raw trades for one token, full history from 2026-04-12. PRO+." },
1264
1349
  { name: "madeonsol_tokens_batch_buyer_quality", description: "Bulk buyer-quality scoring for up to 50 mints. Shares the LRU cache." },
1265
1350
  { name: "madeonsol_tokens_batch_risk", description: "Bulk rug-risk/safety scoring for up to 50 mints — same shape as madeonsol_token_risk + as_of; untracked mints don't fail the batch. PRO+." },
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.26.1";
1
+ export declare const VERSION = "1.27.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // AUTO-GENERATED by ../gen-version.mjs (npm prebuild) from package.json. Do not edit.
2
- export const VERSION = "1.26.1";
2
+ export const VERSION = "1.27.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-madeonsol",
3
- "version": "1.26.1",
3
+ "version": "1.27.0",
4
4
  "mcpName": "io.github.MadeOnSol/madeonsol",
5
5
  "description": "MCP server for MadeOnSol Solana KOL intelligence API — use from Claude, Cursor, or any MCP client. Free tier 200 req/day (live feeds 5-min delayed; paid is real-time), no payment to start.",
6
6
  "type": "module",