get-prediction-markets
Retrieve prediction market prices from Polymarket, PredictIt, and Kalshi by entering a keyword to find current market data for specific events or topics.
Instructions
Get prediction market prices from Polymarket, PredictIt, and Kalshi
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| keyword | Yes | Keyword for the market you're looking for (e.g. 'trump') |
Input Schema (JSON Schema)
{
"properties": {
"keyword": {
"description": "Keyword for the market you're looking for (e.g. 'trump')",
"maxLength": 50,
"type": "string"
}
},
"required": [
"keyword"
],
"type": "object"
}
Implementation Reference
- src/index.ts:20-192 (registration)Registers the 'get-prediction-markets' tool, defines input schema, and provides the inline handler function that fetches data from Polymarket, PredictIt, and Kalshi APIs, filters by keyword, handles errors, and returns formatted text response.server.tool( "get-prediction-markets", "Get prediction market prices from Polymarket, PredictIt, and Kalshi", { keyword: z .string() .max(50) .describe("Keyword for the market you're looking for (e.g. 'trump')"), }, async ({ keyword }) => { const lowerKeyword = keyword.toLowerCase(); const errors: string[] = []; // Try each API separately with error handling let polyMarkets: any[] = []; let predictItMarkets: any[] = []; let kalshiMarkets: any[] = []; try { polyMarkets = await getPolymarketPredictionData(50, keyword); } catch (error) { errors.push(`Polymarket: ${error instanceof Error ? error.message : 'Unknown error'}`); } try { predictItMarkets = await getPredictItMarkets(); } catch (error) { errors.push(`PredictIt: ${error instanceof Error ? error.message : 'Unknown error'}`); } try { kalshiMarkets = await getKalshiEventsWithMarkets(keyword); } catch (error) { errors.push(`Kalshi: ${error instanceof Error ? error.message : 'Unknown error'}`); } const filteredPredictIt = predictItMarkets.filter( (m) => m.name.toLowerCase().includes(lowerKeyword) || m.shortName.toLowerCase().includes(lowerKeyword), ); const filteredKalshi = kalshiMarkets; // If all APIs failed if (errors.length === 3) { return { content: [ { type: "text", text: `Failed to fetch prediction markets from all platforms:\n${errors.join('\n')}\n\nPlease try again later or check your internet connection.`, }, ], }; } // If no markets found but some APIs succeeded if ( polyMarkets.length === 0 && filteredPredictIt.length === 0 && filteredKalshi.length === 0 ) { const errorText = errors.length > 0 ? `⚠️ Some platforms failed: ${errors.join('; ')}\n\n` : ''; return { content: [ { type: "text", text: `${errorText}No prediction markets found for keyword: "${keyword}"`, }, ], }; } const polyText = polyMarkets .map((m: any) => { const oddsList = Object.entries(m.odds) .map(([outcome, prob]: [string, any]) => `${outcome}: ${(prob * 100).toFixed(1)}%`) .join(" | "); return `**Polymarket: ${m.question}**\n${oddsList}`; }) .join("\n\n"); const predictItText = filteredPredictIt .map((m: any) => { const contractOdds = m.contracts .map((c: any) => { const pct = c.lastTradePrice != null ? `${(c.lastTradePrice * 100).toFixed(1)}%` : "n/a"; return `${c.shortName}: ${pct}`; }) .join(" | "); return `**PredictIt: ${m.name}**\n${contractOdds}`; }) .join("\n\n"); const kalshiText = filteredKalshi .map((e) => { if (!e.markets || e.markets.length === 0) { return `**Kalshi: ${e.title}**\n${e.sub_title} (${e.category}) - No active markets`; } const marketTexts = e.markets.map((market: KalshiMarket) => { // Calculate probability from bid/ask midpoint, or use last price const yesBid = parseFloat(market.yes_bid_dollars || "0"); const yesAsk = parseFloat(market.yes_ask_dollars || "0"); const noBid = parseFloat(market.no_bid_dollars || "0"); const noAsk = parseFloat(market.no_ask_dollars || "0"); const lastPrice = parseFloat(market.last_price_dollars || "0"); let yesProb = "n/a"; let noProb = "n/a"; // Calculate Yes probability if (yesBid > 0 || yesAsk > 0) { const yesMid = yesBid > 0 && yesAsk > 0 ? (yesBid + yesAsk) / 2 : lastPrice > 0 ? lastPrice : yesBid > 0 ? yesBid : yesAsk; yesProb = `${(yesMid * 100).toFixed(1)}%`; } else if (lastPrice > 0) { yesProb = `${(lastPrice * 100).toFixed(1)}%`; } // Calculate No probability (complement of Yes for binary markets) if (noBid > 0 || noAsk > 0) { const noMid = noBid > 0 && noAsk > 0 ? (noBid + noAsk) / 2 : (1 - lastPrice) > 0 && lastPrice > 0 ? (1 - lastPrice) : noBid > 0 ? noBid : noAsk; noProb = `${(noMid * 100).toFixed(1)}%`; } else if (lastPrice > 0) { noProb = `${((1 - lastPrice) * 100).toFixed(1)}%`; } const statusText = market.status && market.status !== "open" && market.status !== "active" && market.status !== "live" ? ` [${market.status}]` : ""; const marketSubtitle = market.subtitle || market.yes_sub_title || ""; const subtitleText = marketSubtitle ? ` (${marketSubtitle})` : ""; return ` ${market.yes_sub_title || "Yes"}: ${yesProb} | ${market.no_sub_title || "No"}: ${noProb}${subtitleText}${statusText}`; }); const eventSubtitle = e.sub_title ? `\n${e.sub_title}` : ""; return `**Kalshi: ${e.title}**${eventSubtitle} (${e.category})\n${marketTexts.join("\n")}`; }) .join("\n\n"); const text = [polyText, predictItText, kalshiText] .filter(Boolean) .join("\n\n"); const errorText = errors.length > 0 ? `⚠️ Some platforms failed: ${errors.join('; ')}\n\n` : ''; return { content: [ { type: "text", text: errorText + text, }, ], }; }, );
- src/index.ts:29-192 (handler)The core handler logic for executing the tool: calls helper functions to fetch data from three prediction market platforms, filters results by keyword, computes probabilities, formats into markdown, and handles API errors gracefully.async ({ keyword }) => { const lowerKeyword = keyword.toLowerCase(); const errors: string[] = []; // Try each API separately with error handling let polyMarkets: any[] = []; let predictItMarkets: any[] = []; let kalshiMarkets: any[] = []; try { polyMarkets = await getPolymarketPredictionData(50, keyword); } catch (error) { errors.push(`Polymarket: ${error instanceof Error ? error.message : 'Unknown error'}`); } try { predictItMarkets = await getPredictItMarkets(); } catch (error) { errors.push(`PredictIt: ${error instanceof Error ? error.message : 'Unknown error'}`); } try { kalshiMarkets = await getKalshiEventsWithMarkets(keyword); } catch (error) { errors.push(`Kalshi: ${error instanceof Error ? error.message : 'Unknown error'}`); } const filteredPredictIt = predictItMarkets.filter( (m) => m.name.toLowerCase().includes(lowerKeyword) || m.shortName.toLowerCase().includes(lowerKeyword), ); const filteredKalshi = kalshiMarkets; // If all APIs failed if (errors.length === 3) { return { content: [ { type: "text", text: `Failed to fetch prediction markets from all platforms:\n${errors.join('\n')}\n\nPlease try again later or check your internet connection.`, }, ], }; } // If no markets found but some APIs succeeded if ( polyMarkets.length === 0 && filteredPredictIt.length === 0 && filteredKalshi.length === 0 ) { const errorText = errors.length > 0 ? `⚠️ Some platforms failed: ${errors.join('; ')}\n\n` : ''; return { content: [ { type: "text", text: `${errorText}No prediction markets found for keyword: "${keyword}"`, }, ], }; } const polyText = polyMarkets .map((m: any) => { const oddsList = Object.entries(m.odds) .map(([outcome, prob]: [string, any]) => `${outcome}: ${(prob * 100).toFixed(1)}%`) .join(" | "); return `**Polymarket: ${m.question}**\n${oddsList}`; }) .join("\n\n"); const predictItText = filteredPredictIt .map((m: any) => { const contractOdds = m.contracts .map((c: any) => { const pct = c.lastTradePrice != null ? `${(c.lastTradePrice * 100).toFixed(1)}%` : "n/a"; return `${c.shortName}: ${pct}`; }) .join(" | "); return `**PredictIt: ${m.name}**\n${contractOdds}`; }) .join("\n\n"); const kalshiText = filteredKalshi .map((e) => { if (!e.markets || e.markets.length === 0) { return `**Kalshi: ${e.title}**\n${e.sub_title} (${e.category}) - No active markets`; } const marketTexts = e.markets.map((market: KalshiMarket) => { // Calculate probability from bid/ask midpoint, or use last price const yesBid = parseFloat(market.yes_bid_dollars || "0"); const yesAsk = parseFloat(market.yes_ask_dollars || "0"); const noBid = parseFloat(market.no_bid_dollars || "0"); const noAsk = parseFloat(market.no_ask_dollars || "0"); const lastPrice = parseFloat(market.last_price_dollars || "0"); let yesProb = "n/a"; let noProb = "n/a"; // Calculate Yes probability if (yesBid > 0 || yesAsk > 0) { const yesMid = yesBid > 0 && yesAsk > 0 ? (yesBid + yesAsk) / 2 : lastPrice > 0 ? lastPrice : yesBid > 0 ? yesBid : yesAsk; yesProb = `${(yesMid * 100).toFixed(1)}%`; } else if (lastPrice > 0) { yesProb = `${(lastPrice * 100).toFixed(1)}%`; } // Calculate No probability (complement of Yes for binary markets) if (noBid > 0 || noAsk > 0) { const noMid = noBid > 0 && noAsk > 0 ? (noBid + noAsk) / 2 : (1 - lastPrice) > 0 && lastPrice > 0 ? (1 - lastPrice) : noBid > 0 ? noBid : noAsk; noProb = `${(noMid * 100).toFixed(1)}%`; } else if (lastPrice > 0) { noProb = `${((1 - lastPrice) * 100).toFixed(1)}%`; } const statusText = market.status && market.status !== "open" && market.status !== "active" && market.status !== "live" ? ` [${market.status}]` : ""; const marketSubtitle = market.subtitle || market.yes_sub_title || ""; const subtitleText = marketSubtitle ? ` (${marketSubtitle})` : ""; return ` ${market.yes_sub_title || "Yes"}: ${yesProb} | ${market.no_sub_title || "No"}: ${noProb}${subtitleText}${statusText}`; }); const eventSubtitle = e.sub_title ? `\n${e.sub_title}` : ""; return `**Kalshi: ${e.title}**${eventSubtitle} (${e.category})\n${marketTexts.join("\n")}`; }) .join("\n\n"); const text = [polyText, predictItText, kalshiText] .filter(Boolean) .join("\n\n"); const errorText = errors.length > 0 ? `⚠️ Some platforms failed: ${errors.join('; ')}\n\n` : ''; return { content: [ { type: "text", text: errorText + text, }, ], }; }, );
- src/index.ts:23-28 (schema)Zod schema for the tool's input parameter: a string keyword limited to 50 characters.{ keyword: z .string() .max(50) .describe("Keyword for the market you're looking for (e.g. 'trump')"), },
- src/utils/utils.ts:20-57 (helper)Helper function to fetch and filter Polymarket data, calculating outcome probabilities from token prices.export async function getPolymarketPredictionData( limit = 50, keyword = "", ): Promise<MarketWithOdds[]> { const res = await superagent .get(`${POLYMARKET_API_BASE}?limit=${limit}`) .set("User-Agent", USER_AGENT); const json = res.body; if (!Array.isArray(json.data)) { throw new Error("Unexpected API response format"); } const lowerKeyword = keyword.toLowerCase(); const currentMarkets = json.data.filter( (market: Market) => market.active && !market.archived && (market.question?.toLowerCase().includes(lowerKeyword) || market.description?.toLowerCase().includes(lowerKeyword) || market.market_slug?.toLowerCase().includes(lowerKeyword)), ); return currentMarkets.map((market: { tokens: any[] }) => { const totalPrice = market.tokens.reduce( (sum, token) => sum + token.price, 0, ); const odds: Record<string, number> = {}; for (const token of market.tokens) { odds[token.outcome] = totalPrice > 0 ? token.price / totalPrice : 0; } return { ...market, odds }; }); }
- src/utils/utils.ts:59-71 (helper)Helper to fetch open markets from PredictIt.export async function getPredictItMarkets(): Promise<PredictItMarket[]> { const res = await superagent .get(PREDICTIT_API_URL) .set("User-Agent", USER_AGENT); const data: PredictItResponse = res.body; if (!Array.isArray(data.markets)) { throw new Error("Unexpected PredictIt API format"); } return data.markets.filter((market) => market.status === "Open"); }
- src/utils/utils.ts:127-154 (helper)Helper to fetch Kalshi events and their active markets, filtered by keyword.export async function getKalshiEventsWithMarkets( keyword: string = "" ): Promise<KalshiEventWithMarkets[]> { const events = await getKalshiMarkets(); const lowerKeyword = keyword.toLowerCase(); // Filter events by keyword const filteredEvents = events.filter( (e) => e.title.toLowerCase().includes(lowerKeyword) || e.sub_title.toLowerCase().includes(lowerKeyword) || e.category.toLowerCase().includes(lowerKeyword) ); // Fetch market data for filtered events (limit to first 20 to avoid too many API calls) const eventsWithMarkets: KalshiEventWithMarkets[] = await Promise.all( filteredEvents.slice(0, 20).map(async (event) => { const markets = await getKalshiMarketData(event.event_ticker); return { ...event, markets: markets.length > 0 ? markets : undefined, }; }) ); // Only return events that have active markets return eventsWithMarkets.filter((e) => e.markets && e.markets.length > 0); }