#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const API_BASE = "https://app.eventgraph.ai";
const API_KEY = process.env.EVENTGRAPH_API_KEY;
if (!API_KEY) {
process.stderr.write("Error: EVENTGRAPH_API_KEY environment variable is required\n");
process.exit(1);
}
async function apiCall(path, params = {}) {
const url = new URL(`${API_BASE}${path}`);
for (const [k, v] of Object.entries(params)) {
if (v !== undefined && v !== null && v !== "") {
url.searchParams.set(k, String(v));
}
}
const res = await fetch(url.toString(), {
headers: { "X-API-Key": API_KEY },
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`API error ${res.status}: ${err?.detail?.message || res.statusText}`);
}
const json = await res.json();
return json.data ?? json;
}
const server = new McpServer({
name: "eventgraph",
version: "1.0.0",
});
// ── Free tier tools ──────────────────────────────────────────────────────────
server.tool(
"search_markets",
"Search prediction markets across Polymarket, Kalshi, Limitless, and OpinionTrade. Returns live market data including prices, volumes, and platform details.",
{
query: z.string().optional().describe("Keyword search query"),
platform: z.enum(["polymarket", "kalshi", "limitless", "opiniontrade"]).optional().describe("Filter by platform"),
status: z.enum(["active", "resolved", "all"]).optional().default("active").describe("Market status filter"),
category: z.string().optional().describe("Category filter e.g. 'politics', 'sports', 'crypto'"),
page_size: z.number().int().min(1).max(100).optional().default(20).describe("Number of results"),
},
async (params) => {
const data = await apiCall("/api/v1/markets", params);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
server.tool(
"get_market",
"Get full details for a specific prediction market including current prices, volume, liquidity, and metadata.",
{
market_id: z.string().describe("The market ID (from search_markets results)"),
},
async ({ market_id }) => {
const data = await apiCall(`/api/v1/markets/${market_id}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
server.tool(
"search_events",
"Search prediction market events. Events are groups of related markets (e.g. 'US 2026 Midterms' is an event containing many individual markets).",
{
query: z.string().optional().describe("Keyword search query"),
category: z.string().optional().describe("Category filter"),
page_size: z.number().int().min(1).max(100).optional().default(20),
},
async (params) => {
const data = await apiCall("/api/v1/events", params);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
server.tool(
"get_api_status",
"Check EventGraph API health and data freshness. Shows when data was last updated for each platform.",
{},
async () => {
const data = await apiCall("/api/v1/status");
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// ── Pro tier tools ───────────────────────────────────────────────────────────
server.tool(
"find_arbitrage",
"Find cross-platform arbitrage opportunities — markets where the same event is priced differently on different platforms. Pro tier required.",
{
min_spread: z.number().optional().describe("Minimum spread percentage to return (e.g. 2 for 2%)"),
page_size: z.number().int().min(1).max(100).optional().default(20),
},
async (params) => {
const data = await apiCall("/api/v1/arbitrage", params);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
server.tool(
"compare_markets",
"Compare how the same event is priced across multiple prediction market platforms side by side. Pro tier required.",
{
event_id: z.string().describe("The event ID to compare across platforms (from search_events results)"),
},
async ({ event_id }) => {
const data = await apiCall("/api/v1/compare", { event_id });
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
server.tool(
"get_market_history",
"Get historical price time series data for a prediction market. Pro tier required.",
{
market_id: z.string().describe("The market ID"),
interval: z.enum(["1h", "4h", "1d", "1w"]).optional().default("1d").describe("Time interval between data points"),
},
async ({ market_id, interval }) => {
const data = await apiCall(`/api/v1/markets/${market_id}/history`, { interval });
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// ── Start server ─────────────────────────────────────────────────────────────
const transport = new StdioServerTransport();
await server.connect(transport);