madeonsol_kol_feed
Fetch real-time Solana KOL trades from 1000+ tracked wallets with market cap at trade time. Supports filtering by trade type, wallet, and PRO+ parameters like size, token age, strategy, and winrate.
Instructions
Get real-time Solana KOL trades from 1,000+ tracked wallets. Each trade includes the token's market cap (USD) at the moment of trade — sourced from our in-memory price tracker, accurate to the millisecond, faster than Dexscreener spot. PRO+ adds size/age/strategy/winrate filters.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Number of trades to return (1-100) | |
| before | No | Cursor — ISO 8601 timestamp; returns trades strictly older than this. Pass next_before from the previous response for polling. | |
| action | No | Filter by trade type: buy or sell | |
| kol | No | Filter by specific KOL wallet address (base58) | |
| min_sol | No | PRO+: minimum SOL size per trade | |
| token_age_max_min | No | PRO+: max token age in minutes at time of trade | |
| exclude_sells | No | PRO+: drop sell-side trades | |
| min_kol_winrate | No | PRO+: minimum 7d winrate of the KOL (0-100) | |
| strategy | No | PRO+: filter by auto-tagged strategy |
Implementation Reference
- src/index.ts:85-112 (registration)Tool registration using server.tool() with name 'madeonsol_kol_feed', description, Zod schema params, annotations, and async handler.
server.tool( "madeonsol_kol_feed", "Get real-time Solana KOL trades from 1,000+ tracked wallets. Each trade includes the token's market cap (USD) at the moment of trade — sourced from our in-memory price tracker, accurate to the millisecond, faster than Dexscreener spot. PRO+ adds size/age/strategy/winrate filters.", { limit: z.number().min(1).max(100).default(10).describe("Number of trades to return (1-100)"), before: z.string().optional().describe("Cursor — ISO 8601 timestamp; returns trades strictly older than this. Pass next_before from the previous response for polling."), action: z.enum(["buy", "sell"]).optional().describe("Filter by trade type: buy or sell"), kol: z.string().optional().describe("Filter by specific KOL wallet address (base58)"), min_sol: z.number().optional().describe("PRO+: minimum SOL size per trade"), token_age_max_min: z.number().optional().describe("PRO+: max token age in minutes at time of trade"), exclude_sells: z.boolean().optional().describe("PRO+: drop sell-side trades"), min_kol_winrate: z.number().optional().describe("PRO+: minimum 7d winrate of the KOL (0-100)"), strategy: z.enum(["scalper", "day_trader", "swing_trader", "hodler", "mixed"]).optional().describe("PRO+: filter by auto-tagged strategy"), }, readOnlyAnnotations, async ({ limit, before, action, kol, min_sol, token_age_max_min, exclude_sells, min_kol_winrate, strategy }) => { const params: Record<string, string | number> = { limit }; if (before) params.before = before; if (action) params.action = action; if (kol) params.kol = kol; if (min_sol !== undefined) params.min_sol = min_sol; if (token_age_max_min !== undefined) params.token_age_max_min = token_age_max_min; if (exclude_sells) params.exclude_sells = "true"; if (min_kol_winrate !== undefined) params.min_kol_winrate = min_kol_winrate; if (strategy) params.strategy = strategy; return { content: [{ type: "text" as const, text: await query("/api/x402/kol/feed", params) }] }; } ); - src/index.ts:88-98 (schema)Zod schema defining input validation: limit (1-100, default 10), before (ISO cursor), action (buy/sell), kol, min_sol, token_age_max_min, exclude_sells, min_kol_winrate, and strategy.
{ limit: z.number().min(1).max(100).default(10).describe("Number of trades to return (1-100)"), before: z.string().optional().describe("Cursor — ISO 8601 timestamp; returns trades strictly older than this. Pass next_before from the previous response for polling."), action: z.enum(["buy", "sell"]).optional().describe("Filter by trade type: buy or sell"), kol: z.string().optional().describe("Filter by specific KOL wallet address (base58)"), min_sol: z.number().optional().describe("PRO+: minimum SOL size per trade"), token_age_max_min: z.number().optional().describe("PRO+: max token age in minutes at time of trade"), exclude_sells: z.boolean().optional().describe("PRO+: drop sell-side trades"), min_kol_winrate: z.number().optional().describe("PRO+: minimum 7d winrate of the KOL (0-100)"), strategy: z.enum(["scalper", "day_trader", "swing_trader", "hodler", "mixed"]).optional().describe("PRO+: filter by auto-tagged strategy"), }, - src/index.ts:100-112 (handler)Handler: builds a params object from incoming args, then calls query('/api/x402/kol/feed', params) and returns the result as text content.
async ({ limit, before, action, kol, min_sol, token_age_max_min, exclude_sells, min_kol_winrate, strategy }) => { const params: Record<string, string | number> = { limit }; if (before) params.before = before; if (action) params.action = action; if (kol) params.kol = kol; if (min_sol !== undefined) params.min_sol = min_sol; if (token_age_max_min !== undefined) params.token_age_max_min = token_age_max_min; if (exclude_sells) params.exclude_sells = "true"; if (min_kol_winrate !== undefined) params.min_kol_winrate = min_kol_winrate; if (strategy) params.strategy = strategy; return { content: [{ type: "text" as const, text: await query("/api/x402/kol/feed", params) }] }; } ); - src/index.ts:60-80 (helper)The query() helper function used by the handler — builds the URL with params, adds auth headers, performs the fetch (with x402 payment integration if applicable), and returns JSON string or error text.
async function query(path: string, params?: Record<string, string | number>) { // API key uses /api/v1/ endpoints; x402 uses /api/x402/ const apiPath = authMode === "x402" || authMode === "none" ? path : path.replace("/api/x402/", "/api/v1/"); const url = new URL(apiPath, BASE_URL); if (params) { for (const [k, v] of Object.entries(params)) { if (v !== undefined) url.searchParams.set(k, String(v)); } } const headers = apiKeyHeaders(); const res = authMode === "x402" ? await paidFetch(url.toString()) : await fetch(url.toString(), { headers }); if (!res.ok) { const body = await res.text().catch(() => ""); return `Error ${res.status}: ${body}`; } return JSON.stringify(await res.json(), null, 2); }