propline-mcp 0.20.0 → 0.21.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/index.js +40 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -196,7 +196,7 @@ var PropLineClient = class {
|
|
|
196
196
|
getEventEv(sportKey, eventId, opts = {}) {
|
|
197
197
|
return this.request(`/v1/sports/${sportKey}/events/${eventId}/ev`, {
|
|
198
198
|
markets: opts.markets,
|
|
199
|
-
|
|
199
|
+
bookmakers: opts.bookmakers
|
|
200
200
|
});
|
|
201
201
|
}
|
|
202
202
|
getEventBestLine(sportKey, eventId, opts = {}) {
|
|
@@ -209,7 +209,7 @@ var PropLineClient = class {
|
|
|
209
209
|
};
|
|
210
210
|
|
|
211
211
|
// src/index.ts
|
|
212
|
-
var VERSION = "0.
|
|
212
|
+
var VERSION = "0.21.0";
|
|
213
213
|
var DEMO_KEY = "be2b8487fcfacb1fbc292a8aa925a84c";
|
|
214
214
|
var apiKey = process.env.PROPLINE_API_KEY;
|
|
215
215
|
var baseUrl = process.env.PROPLINE_BASE_URL;
|
|
@@ -221,6 +221,25 @@ function client() {
|
|
|
221
221
|
}
|
|
222
222
|
return _client;
|
|
223
223
|
}
|
|
224
|
+
function filterByMinEv(res, minEvPct) {
|
|
225
|
+
if (typeof minEvPct !== "number" || Number.isNaN(minEvPct)) return res;
|
|
226
|
+
if (!res || typeof res !== "object") return res;
|
|
227
|
+
const body = res;
|
|
228
|
+
if (!Array.isArray(body.lines)) return res;
|
|
229
|
+
const lines = body.lines.map((line) => {
|
|
230
|
+
const l = line;
|
|
231
|
+
if (!Array.isArray(l.outcomes)) return line;
|
|
232
|
+
const outcomes = l.outcomes.filter((o) => {
|
|
233
|
+
const ev = o.ev_pct;
|
|
234
|
+
return typeof ev === "number" && ev >= minEvPct;
|
|
235
|
+
});
|
|
236
|
+
return { ...line, outcomes };
|
|
237
|
+
}).filter((line) => {
|
|
238
|
+
const outcomes = line.outcomes;
|
|
239
|
+
return Array.isArray(outcomes) ? outcomes.length > 0 : true;
|
|
240
|
+
});
|
|
241
|
+
return { ...res, lines };
|
|
242
|
+
}
|
|
224
243
|
var tools = [
|
|
225
244
|
{
|
|
226
245
|
name: "propline_list_sports",
|
|
@@ -273,7 +292,7 @@ var tools = [
|
|
|
273
292
|
},
|
|
274
293
|
{
|
|
275
294
|
name: "propline_get_odds",
|
|
276
|
-
description: "Get live odds. If event_id is supplied, returns full per-event props for that event; otherwise returns bulk game-line odds for the whole sport. Pass markets as a comma-separated list (e.g. 'h2h,spreads,totals' or 'player_points,player_rebounds'). Response includes a bookmakers[] array across every book that carries the requested markets (currently up to 13: Bovada, DraftKings, FanDuel, Pinnacle, BetMGM, BetRivers, Unibet, Underdog, PrizePicks, Kalshi, Polymarket, Matchbook, Smarkets \u2014 coverage varies by sport). Underdog Fantasy outcomes carry a payout_multiplier
|
|
295
|
+
description: "Get live odds. If event_id is supplied, returns full per-event props for that event; otherwise returns bulk game-line odds for the whole sport. Pass markets as a comma-separated list (e.g. 'h2h,spreads,totals' or 'player_points,player_rebounds'). Response includes a bookmakers[] array across every book that carries the requested markets (currently up to 13: Bovada, DraftKings, FanDuel, Pinnacle, BetMGM, BetRivers, Unibet, Underdog, PrizePicks, Kalshi, Polymarket, Matchbook, Smarkets \u2014 coverage varies by sport). Underdog Fantasy outcomes carry a payout_multiplier on EVERY outcome (1.0 = standard pick, e.g. 1.5 = boost, 0.75 = discount; null means the book is not Underdog) \u2014 keep only payout_multiplier == 1.0 when comparing DFS lines to sportsbook consensus, since filtering on non-null would drop every Underdog line.",
|
|
277
296
|
inputSchema: {
|
|
278
297
|
type: "object",
|
|
279
298
|
properties: {
|
|
@@ -703,13 +722,17 @@ var tools = [
|
|
|
703
722
|
},
|
|
704
723
|
{
|
|
705
724
|
name: "propline_get_event_ev",
|
|
706
|
-
description: "Pro-tier endpoint. Returns cross-book +EV per outcome for an event. We anchor on
|
|
725
|
+
description: "Pro-tier endpoint. Returns cross-book +EV per outcome for an event. We anchor on a sharp book, remove vig, derive a no-vig fair line, and compute EV% per book at the same line. Outcomes are sorted with +EV plays floated to the top of each line group. PrizePicks is excluded from EV math (DFS payouts aren't comparable to per-book prices). The anchor is chosen PER LINE in the order pinnacle \u2192 polymarket \u2192 kalshi \u2192 bovada, and each line's fair_source names the one used \u2014 report the anchor from fair_source per line, never assume Pinnacle anchored all of them. Optional bookmakers filter prices to the books the user holds accounts at; it never changes the anchor, so filtering to DraftKings still measures DraftKings against Pinnacle.",
|
|
707
726
|
inputSchema: {
|
|
708
727
|
type: "object",
|
|
709
728
|
properties: {
|
|
710
729
|
sport_key: { type: "string" },
|
|
711
730
|
event_id: { type: ["string", "number"] },
|
|
712
731
|
markets: { type: "string" },
|
|
732
|
+
bookmakers: {
|
|
733
|
+
type: "string",
|
|
734
|
+
description: "Comma-separated book keys (e.g. 'draftkings,fanduel') to price only the user's books. Narrows prices, not the anchor."
|
|
735
|
+
},
|
|
713
736
|
min_ev_pct: {
|
|
714
737
|
type: "number",
|
|
715
738
|
description: "Filter to outcomes with EV \u2265 this percent (e.g. 2.0)."
|
|
@@ -718,14 +741,17 @@ var tools = [
|
|
|
718
741
|
required: ["sport_key", "event_id"],
|
|
719
742
|
additionalProperties: false
|
|
720
743
|
},
|
|
721
|
-
handler: (args) =>
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
744
|
+
handler: async (args) => {
|
|
745
|
+
const res = await client().getEventEv(
|
|
746
|
+
args.sport_key,
|
|
747
|
+
args.event_id,
|
|
748
|
+
{
|
|
749
|
+
markets: args.markets,
|
|
750
|
+
bookmakers: args.bookmakers
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
return filterByMinEv(res, args.min_ev_pct);
|
|
754
|
+
}
|
|
729
755
|
},
|
|
730
756
|
{
|
|
731
757
|
name: "propline_get_best_line",
|
|
@@ -810,6 +836,7 @@ if (usingDemoKey) {
|
|
|
810
836
|
}
|
|
811
837
|
export {
|
|
812
838
|
DEMO_KEY,
|
|
813
|
-
VERSION
|
|
839
|
+
VERSION,
|
|
840
|
+
filterByMinEv
|
|
814
841
|
};
|
|
815
842
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["/**\n * PropLine MCP server.\n *\n * Exposes the PropLine REST API as Model Context Protocol tools so AI\n * clients (Claude Desktop, Claude Code, ChatGPT desktop with MCP, etc.)\n * can pull live odds, prop resolution, cross-book +EV, scores, and\n * box-score stats directly via tool calls.\n *\n * Run via: npx -y propline-mcp\n * Auth: PROPLINE_API_KEY env var (free key at https://prop-line.com).\n * If unset, falls back to a shared public DEMO key so an agent\n * can answer its first question with zero config — see DEMO_KEY.\n * Optional: PROPLINE_BASE_URL env var (override for self-hosted setups)\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { PropLineClient, PropLineHTTPError } from \"./client.js\";\n\nexport const VERSION = \"0.20.0\";\n\n// Shared public demo key. Baked in on purpose so `npx -y propline-mcp` works\n// with ZERO configuration — an AI agent can discover the server and answer\n// its first question without a human-issued key in the loop. It's a free-tier,\n// read-only, rate-limited key: paid features (resolution, +EV, history,\n// exports) still return the redacted teaser + upgrade_url, so the funnel is\n// intact, and a generous-but-bounded daily cap nudges real production usage\n// toward a personal key. Get your own (higher limits, full features) at\n// https://prop-line.com. Rotated by changing this value AND the seed in the\n// API repo (scraper/seed.py) together.\nexport const DEMO_KEY = \"be2b8487fcfacb1fbc292a8aa925a84c\";\n\nconst apiKey = process.env.PROPLINE_API_KEY;\nconst baseUrl = process.env.PROPLINE_BASE_URL;\nconst usingDemoKey = !apiKey;\n\n// The API key is intentionally NOT required at startup. MCP clients and\n// directory crawlers (Glama, the MCP Registry) must be able to introspect\n// the tool list before a key is configured — exiting here would make the\n// server undiscoverable and break the install-then-configure UX. tools/list\n// never touches the client. When no PROPLINE_API_KEY is set we fall back to\n// the shared DEMO_KEY so the first tool call still succeeds (free-tier data).\nlet _client: PropLineClient | null = null;\nfunction client(): PropLineClient {\n if (!_client) {\n // Demo-key fallback is announced once at startup (see below); no need to\n // repeat it here. apiKey ?? DEMO_KEY keeps the first call working with\n // zero config.\n _client = new PropLineClient({ apiKey: apiKey ?? DEMO_KEY, baseUrl });\n }\n return _client;\n}\n\n// ---------------------------------------------------------------------\n// Tool definitions\n// ---------------------------------------------------------------------\n\ninterface ToolDef {\n name: string;\n description: string;\n inputSchema: {\n type: \"object\";\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n // Returns either JSON-serializable data (will be JSON.stringify'd in the\n // tool result) or a string (returned as-is).\n handler: (args: Record<string, unknown>) => Promise<unknown>;\n}\n\nconst tools: ToolDef[] = [\n {\n name: \"propline_list_sports\",\n description:\n \"List all sports PropLine currently polls. Returns sport keys \" +\n \"(e.g. baseball_mlb, basketball_nba, soccer_epl) along with human \" +\n \"titles and active status. Use this first to discover what \" +\n \"sport_key values are valid for the other tools.\",\n inputSchema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n handler: () => client().listSports(),\n },\n {\n name: \"propline_list_events\",\n description:\n \"List upcoming events for a sport. Returns each event's id, \" +\n \"home_team, away_team, commence_time. Use the returned event_id \" +\n \"to drill into per-event odds, props, +EV, or results.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: {\n type: \"string\",\n description:\n \"Sport key from propline_list_sports — e.g. baseball_mlb, basketball_nba\",\n },\n live: {\n type: \"boolean\",\n description:\n \"If true, only return in-progress (live) events. Defaults to false.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().listEvents(args.sport_key as string, {\n live: args.live as boolean | undefined,\n }),\n },\n {\n name: \"propline_list_event_markets\",\n description:\n \"List the market types available for a specific event (e.g. h2h, \" +\n \"spreads, totals, player_points, pitcher_strikeouts). Useful when \" +\n \"you don't know which prop markets a given event carries.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().listEventMarkets(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_odds\",\n description:\n \"Get live odds. If event_id is supplied, returns full per-event \" +\n \"props for that event; otherwise returns bulk game-line odds for \" +\n \"the whole sport. Pass markets as a comma-separated list (e.g. \" +\n \"'h2h,spreads,totals' or 'player_points,player_rebounds'). \" +\n \"Response includes a bookmakers[] array across every book that \" +\n \"carries the requested markets (currently up to 13: Bovada, \" +\n \"DraftKings, FanDuel, Pinnacle, BetMGM, BetRivers, Unibet, \" +\n \"Underdog, PrizePicks, Kalshi, Polymarket, Matchbook, Smarkets \" +\n \"— coverage varies by sport). Underdog Fantasy outcomes carry a \" +\n \"payout_multiplier (DFS boost/discount factor; null = standard pick, \" +\n \"e.g. 1.5 = boost, 0.75 = discount) — skip non-null values when \" +\n \"comparing DFS lines to sportsbook consensus.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: {\n type: [\"string\", \"number\"],\n description: \"Optional. If set, returns props for this event.\",\n },\n markets: {\n type: \"string\",\n description:\n \"Comma-separated market keys. Defaults to h2h on bulk; h2h,spreads,totals on event. Pass an explicit list to fetch player props (sport-specific — e.g. player_points,player_rebounds for NBA; pitcher_strikeouts,batter_home_runs for MLB).\",\n },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (bovada, draftkings, fanduel, pinnacle, betmgm, betrivers, unibet, underdog, prizepicks, kalshi, polymarket, matchbook, smarkets). Default returns all available.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter. Omitted = full-game markets only. Canonical codes: q1..q4 (quarters), h1/h2 (halves), p1..p3 (hockey periods), i1..i9 (innings), f3/f5/f7 (first N innings). Comma-separated for multiple. 'all' = include every period alongside full-game.\",\n },\n include_links: {\n type: \"boolean\",\n description:\n \"When true, each bookmaker block carries a link — that book's public event-page URL for click-out (Bovada/DraftKings/FanDuel/BetMGM/Kalshi/Polymarket/Smarkets; others null). Plain navigation, no affiliate tagging.\",\n },\n include_book_ids: {\n type: \"boolean\",\n description:\n \"When true, each bookmaker block carries book_event_id and each outcome carries book_outcome_id — that book's OWN ids for the event and the priced selection, for joining onto a book's native feed by id instead of matching team/player names and lines. Kalshi ships both (event ticker + per-contract market ticker, e.g. KXMLBGAME-26AUG08NYYBOS-NYY); most other books ship an event id; books without a stable id return null. NB a two-sided market can share ONE book_outcome_id across both legs — a Kalshi contract is binary, so Over/Under are its YES/NO sides; the outcome's name says which side.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getOdds(args.sport_key as string, {\n eventId: args.event_id as string | number | undefined,\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n period: args.period as string | undefined,\n includeLinks: args.include_links as boolean | undefined,\n includeBookIds: args.include_book_ids as boolean | undefined,\n }),\n },\n {\n name: \"propline_get_odds_history\",\n description:\n \"Hobby+ endpoint. Returns the historical line-movement snapshot \" +\n \"series for an event (every recorded price/point change per \" +\n \"outcome over the event's lifetime). Free tier returns market \" +\n \"structure with redacted snapshots and an upgrade pointer. \" +\n \"Supports period-historical filters: from/to (absolute ISO), \" +\n \"relative_from/relative_to (offsets to commence_time like '-3h' \" +\n \"or '0'), interval downsample ('30s'/'1m'/'5m'/'15m'/'30m'/'1h'), \" +\n \"and changes_only=true to drop unchanged adjacent snapshots.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: { type: \"string\" },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (e.g. 'draftkings,fanduel'). Default returns all available.\",\n },\n from: {\n type: \"string\",\n description:\n \"ISO timestamp; only include snapshots at or after. Mutually exclusive with relative_from.\",\n },\n to: {\n type: \"string\",\n description:\n \"ISO timestamp; only include snapshots at or before. Mutually exclusive with relative_to.\",\n },\n relative_from: {\n type: \"string\",\n description:\n \"Offset to commence_time, e.g. '-3h', '-30m', '-90s'. Mutually exclusive with from.\",\n },\n relative_to: {\n type: \"string\",\n description:\n \"Offset to commence_time, e.g. '-1m' or '0'. Mutually exclusive with to.\",\n },\n interval: {\n type: \"string\",\n enum: [\"30s\", \"1m\", \"5m\", \"15m\", \"30m\", \"1h\"],\n description: \"Downsample bucket. Latest snapshot per bucket wins.\",\n },\n changes_only: {\n type: \"boolean\",\n description:\n \"When true, drop snapshots whose (price, point) match the previous one.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter. Omitted = full-game markets only. Canonical codes (q1..q4, h1/h2, p1..p3, i1..i9, f3/f5/f7), comma-separated, or 'all'.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getOddsHistory(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n from: args.from as string | undefined,\n to: args.to as string | undefined,\n relativeFrom: args.relative_from as string | undefined,\n relativeTo: args.relative_to as string | undefined,\n interval: args.interval as string | undefined,\n changesOnly: args.changes_only as boolean | undefined,\n period: args.period as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_odds_closing\",\n description:\n \"Hobby+ endpoint. Returns the OPENING and CLOSING line per (book, \" +\n \"market, outcome) for an event. Closing = the last snapshot at or \" +\n \"before commence_time (price/point/closing_at); opening = the \" +\n \"first snapshot in the same 14-day pre-kickoff window \" +\n \"(opening_price/opening_point/opening_at). Canonical CLV-tracking \" +\n \"helper; one call returns both data points your bet should be \" +\n \"measured against, instead of fetching full history and \" +\n \"post-processing. Compare the POINTS as well as the prices — on \" +\n \"spreads and totals the number moves as much as the price, so a \" +\n \"price-only comparison mis-measures those markets. \" +\n \"opening_age_seconds says how long before kickoff the opener was \" +\n \"recorded: the archive starts 2026-04, so a small value means \" +\n \"PropLine started polling late and this is not the book's true \" +\n \"open. Free tier returns redacted structure with upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: { type: \"string\" },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (e.g. 'draftkings,fanduel'). Default returns all available.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter. Omitted = full-game markets only. Canonical codes (q1..q4, h1/h2, p1..p3, i1..i9, f3/f5/f7), comma-separated, or 'all'.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getOddsClosing(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n period: args.period as string | undefined,\n },\n ),\n },\n {\n name: \"propline_export_odds_history\",\n description:\n \"Backfill-pass / Enterprise only. Bulk line-movement tick history \" +\n \"as CSV — every recorded odds snapshot (price + line, per book, \" +\n \"including period markets) across a whole sport, one row per \" +\n \"(outcome, snapshot). This is the raw firehose no subscription tier \" +\n \"can bulk-pull (Pro/Streaming use propline_get_odds_history per \" +\n \"event instead). REQUIRES a since/until window to keep the pull \" +\n \"bounded — the full archive runs to gigabytes per sport. The result \" +\n \"is capped to the first 200 rows for context safety; for the full \" +\n \"dataset use the /v1/exports/odds-history endpoint directly with \" +\n \"curl/SDK and stream to disk. Non-entitled keys get a 403 with an \" +\n \"upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: {\n type: \"string\",\n description: \"Sport key, e.g. baseball_mlb\",\n },\n since: {\n type: \"string\",\n description:\n \"ISO datetime lower bound on recorded_at (required, e.g. 2026-04-01T00:00:00Z). Keep the window narrow.\",\n },\n until: {\n type: \"string\",\n description:\n \"ISO datetime upper bound on recorded_at (required, e.g. 2026-05-01T00:00:00Z).\",\n },\n market: { type: \"string\", description: \"Optional market key filter\" },\n bookmaker: { type: \"string\", description: \"Optional bookmaker filter\" },\n },\n required: [\"sport_key\", \"since\", \"until\"],\n additionalProperties: false,\n },\n handler: async (args) => {\n const csv = await client().exportOddsHistory(args.sport_key as string, {\n since: args.since as string,\n until: args.until as string,\n market: args.market as string | undefined,\n bookmaker: args.bookmaker as string | undefined,\n });\n const lines = csv.split(\"\\n\");\n const CAP = 200; // header + 200 data rows\n if (lines.length <= CAP + 1) return csv;\n return (\n lines.slice(0, CAP + 1).join(\"\\n\") +\n `\\n# … truncated: ${lines.length - 1} total rows. ` +\n `Narrow since/until or pull the full file via the ` +\n `/v1/exports/odds-history endpoint (curl/SDK) and stream to disk.`\n );\n },\n },\n {\n name: \"propline_get_futures\",\n description:\n \"Free-tier endpoint. Returns season-long futures (outright) markets \" +\n \"for a sport — championship/Super Bowl/division/conference winners, \" +\n \"MVP and award winners, season win totals — aggregated across \" +\n \"Bovada, FanDuel, DraftKings, and Pinnacle. One row per (futures \" +\n \"event, book, market) with each team/player outcome and its price. \" +\n \"Marquee markets (Super Bowl winner, MVP, division/conference) are \" +\n \"quoted by multiple books for comparison; exotic markets are often \" +\n \"single-book. Useful for: 'who are the Super Bowl favorites across \" +\n \"books', 'NFL MVP odds', 'NBA championship futures'. Futures are \" +\n \"unresolved (no settlement grade).\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: {\n type: \"string\",\n description:\n \"Sport key — e.g. football_nfl, basketball_nba, baseball_mlb.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) => client().getFutures(args.sport_key as string),\n },\n {\n name: \"propline_get_scores\",\n description:\n \"Free-tier endpoint. Returns recent and live game scores plus \" +\n \"status (scheduled, live, final) for a sport. Useful for: 'is \" +\n \"this game over yet, what was the final score'.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n days_from: {\n type: \"number\",\n description:\n \"How many past days of completed games to include. Defaults to 1.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getScores(args.sport_key as string, {\n daysFrom: args.days_from as number | undefined,\n }),\n },\n {\n name: \"propline_get_dfs_payouts\",\n description:\n \"Free-tier reference math. Returns the PrizePicks Power Play (all legs \" +\n \"must hit) and Flex Play (partial payouts) entry payout schedule for \" +\n \"2-6 legs, plus the per-leg breakeven win probability for each play. \" +\n \"Pass leg_win_prob (e.g. 0.58) to also get expected_return (per $1) and \" +\n \"is_plus_ev per play — the slip-level breakeven. Useful for: 'what hit \" +\n \"rate do I need to beat a 4-pick PrizePicks Power play', 'is a 3-leg \" +\n \"flex +EV at 60% per leg'. NOTE: standard published payouts only — \" +\n \"demon/goblin per-pick modifiers aren't in PrizePicks's feed (see the \" +\n \"disclaimer field); breakeven assumes independent legs.\",\n inputSchema: {\n type: \"object\",\n properties: {\n leg_win_prob: {\n type: \"number\",\n description:\n \"Optional assumed per-leg win probability in [0,1]. Adds \" +\n \"expected_return + is_plus_ev to each play.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getDfsPayouts({\n legWinProb: args.leg_win_prob as number | undefined,\n }),\n },\n {\n name: \"propline_get_mlb_grand_salami\",\n description:\n \"Free-tier endpoint. Returns the synthetic daily MLB Grand Salami \" +\n \"for a given UTC date — total runs scored across every MLB game \" +\n \"on the slate plus each book's implied Grand Salami line (median \" +\n \"of per-game primary totals across our MLB books incl. Pinnacle, \" +\n \"Polymarket, Matchbook, Smarkets). No retail sportsbook quotes \" +\n \"this as a single market. Useful for: 'what's the total run line \" +\n \"for tonight's full MLB slate', 'did the Grand Salami go over \" +\n \"yesterday', 'historical Grand Salami results for backtesting'.\",\n inputSchema: {\n type: \"object\",\n properties: {\n date: {\n type: \"string\",\n description:\n \"YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getMlbGrandSalami({\n date: args.date as string | undefined,\n }),\n },\n {\n name: \"propline_get_nhl_daily_goals_total\",\n description:\n \"Free-tier endpoint. Returns the synthetic daily NHL goals total \" +\n \"(hockey's equivalent of the MLB Grand Salami) for a given UTC \" +\n \"date — total goals scored across every NHL game on the slate \" +\n \"(including OT/SO) plus each book's implied Daily Goals Total \" +\n \"line (median of per-game primary totals across our NHL books). \" +\n \"No retail sportsbook quotes this as a single market. Useful \" +\n \"for: 'what's the total goal line for tonight's full NHL slate', \" +\n \"'did the Daily Goals Total go over yesterday', 'historical \" +\n \"NHL daily-goals results for backtesting'.\",\n inputSchema: {\n type: \"object\",\n properties: {\n date: {\n type: \"string\",\n description:\n \"YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getNhlDailyGoalsTotal({\n date: args.date as string | undefined,\n }),\n },\n {\n name: \"propline_get_resolution_summary\",\n description:\n \"Free-tier endpoint. Returns the factual volume of player props \" +\n \"PropLine has graded against real box scores over the last N days \" +\n \"(aggregated counts only): total graded/settled, games, sports \" +\n \"covered, plus per-sport and top-market breakdowns. Useful for: \" +\n \"'how much graded prop data does PropLine have, what's the \" +\n \"coverage'. A coverage proof, never a profitability claim.\",\n inputSchema: {\n type: \"object\",\n properties: {\n days: {\n type: \"number\",\n description: \"Look-back window, 1-90. Defaults to 30.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getResolutionSummary({ days: args.days as number | undefined }),\n },\n {\n name: \"propline_get_event_stats\",\n description:\n \"Book-agnostic raw box-score stats for an event. Returns per-player \" +\n \"stats (e.g. strikeouts, hits, points, rebounds, shots-on-goal) \" +\n \"decoupled from any sportsbook's lines. LIVE during games for major \" +\n \"US sports (MLB + WNBA now; NFL, NCAAF, NBA, NHL at season start): \" +\n \"while the event's status is in_progress, stats refresh ~every 90 \" +\n \"seconds with cumulative in-game values — use this to answer 'how \" +\n \"is this prop tracking right now'. Treat in-progress numbers as \" +\n \"partial; at status=final they are the official box score. Other \" +\n \"sports populate stats at game completion. Free tier.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventStats(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_event_results\",\n description:\n \"Pro-tier endpoint. Returns graded prop outcomes for a completed \" +\n \"event — every Over/Under marked won, lost, push, or void with \" +\n \"the actual stat value next to the line. The single most \" +\n \"distinctive feature vs the-odds-api: they don't grade props at \" +\n \"any tier. Free tier returns the same structure with resolution \" +\n \"and actual_value redacted plus an upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventResults(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_event_context\",\n description:\n \"Game context for an event — the conditions a prop settles under. \" +\n \"MLB: probable starting pitchers and their throwing hand (L/R/S — \" +\n \"platoon-split context for every batter prop), a confirmed-lineup \" +\n \"flag, the home-plate umpire, and first-pitch weather (temperature, \" +\n \"wind, precipitation) at outdoor / open-roof venues (indoor venues \" +\n \"return weather=null). NFL & NCAAF: the venue and kickoff weather. \" +\n \"The same block is embedded in get_event_results, so every graded \" +\n \"prop carries its conditions — unique to PropLine. Free tier. 404 \" +\n \"when no context is on file for the event yet.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventContext(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_event_movement\",\n description:\n \"Line movement + steam detection from the snapshot tick history. \" +\n \"Per (book, market, outcome): opening line, latest line, signed \" +\n \"implied-probability shift, point shift, direction. The steam[] \" +\n \"array flags outcomes that multiple books moved the same direction — \" +\n \"the classic sharp-money signal, computed across all 16 books \" +\n \"PropLine polls. When a book moves the line itself, that outcome's \" +\n \"prob_shift is null and direction is 'line_moved' (excluded from the \" +\n \"steam signal). No pull-only odds API can produce this. Hobby+ full; \" +\n \"free tier redacted.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: {\n type: \"string\",\n description:\n \"Comma-separated market keys. Defaults to h2h,spreads,totals.\",\n },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (e.g. 'draftkings,fanduel'). Default returns all available.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter (q1..q4, h1/h2, p1..p3, i1..i9, f3/f5/f7; comma-separated, or 'all'). Omit for full-game.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventMovement(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n period: args.period as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_player_history\",\n description:\n \"Player prop history across recent games. Returns each prior prop \" +\n \"this player took with line, prices, resolution, and actual value. \" +\n \"Pro tier returns full data; free tier returns redacted \" +\n \"resolution/actual_value with an upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n player_name: {\n type: \"string\",\n description:\n \"Player name as it appears in box scores — e.g. 'Aaron Judge', 'Nikola Jokic'\",\n },\n limit: {\n type: \"number\",\n description: \"Max number of past games (default 20, max 100)\",\n },\n markets: {\n type: \"string\",\n description:\n \"Comma-separated subset of markets (e.g. 'player_points,player_rebounds')\",\n },\n },\n required: [\"sport_key\", \"player_name\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getPlayerHistory(\n args.sport_key as string,\n args.player_name as string,\n {\n limit: args.limit as number | undefined,\n markets: args.markets as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_player_trends\",\n description:\n \"Hit-rate trends / last-N-games over rate for a player — unique to \" +\n \"PropLine's prop-resolution data. For each market the player has \" +\n \"graded history in, returns over/under/push splits across the last \" +\n \"5/10/20/50 graded games, current streak, average actual stat, and \" +\n \"the recent line. This is the 'did X go over in N of his last M \" +\n \"games?' surface. Omit `market` for all markets, or pass one to \" +\n \"scope (e.g. 'player_points', 'batter_hits').\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n player_name: {\n type: \"string\",\n description:\n \"Player name as it appears in box scores — e.g. 'Aaron Judge', 'Nikola Jokic'\",\n },\n market: {\n type: \"string\",\n description:\n \"Optional single market to scope trends to (e.g. 'player_points'). Omit for all markets.\",\n },\n dfs_odds_type: {\n type: \"string\",\n enum: [\"standard\", \"goblin\", \"demon\"],\n description:\n \"Optional PrizePicks pick-em flavor. When set, the trend is computed against that flavor's PrizePicks line only (e.g. compare a player's goblin-line hit-rate vs his standard-line trend). Omit for the default cross-book behavior.\",\n },\n },\n required: [\"sport_key\", \"player_name\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getPlayerTrends(\n args.sport_key as string,\n args.player_name as string,\n {\n market: args.market as string | undefined,\n dfsOddsType: args.dfs_odds_type as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_event_ev\",\n description:\n \"Pro-tier endpoint. Returns cross-book +EV per outcome for an \" +\n \"event. We anchor on Pinnacle's sharp line, remove vig, derive a \" +\n \"no-vig fair line, and compute EV% per book at the same line. \" +\n \"Outcomes are sorted with +EV plays floated to the top of each \" +\n \"line group. PrizePicks is excluded from EV math (DFS payouts \" +\n \"aren't comparable to per-book prices).\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: { type: \"string\" },\n min_ev_pct: {\n type: \"number\",\n description: \"Filter to outcomes with EV ≥ this percent (e.g. 2.0).\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventEv(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n minEvPct: args.min_ev_pct as number | undefined,\n },\n ),\n },\n {\n name: \"propline_get_best_line\",\n description:\n \"Cross-book line shopping (Hobby+ for prices; free tier gets the \" +\n \"full structure with book identities + best-first ranking but \" +\n \"prices nulled and redacted:true). For every (market, \" +\n \"player, line) tuple on an event, returns the single best American \" +\n \"price across all comparable books, plus an all_prices array \" +\n \"sorted best-first (one row per book, each with last_update). \" +\n \"Companion to propline_get_event_ev: /ev says whether a price \" +\n \"beats the no-vig fair line; best-line says which book pays the \" +\n \"most. DFS pick'em books (PrizePicks, Sleeper, Dabble) are \" +\n \"excluded; Underdog only at clean two-way lines. Optional \" +\n \"bookmakers filter to shop only the books the user holds \" +\n \"accounts at.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: {\n type: \"string\",\n description: \"Comma-separated market keys (e.g. 'pitcher_strikeouts,h2h').\",\n },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated book keys (e.g. 'draftkings,fanduel') to shop only those books.\",\n },\n include_links: {\n type: \"boolean\",\n description:\n \"When true, every price row carries a link — that book's public event-page URL, the click-out for 'go bet this'. Books without a verified URL template return null.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventBestLine(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n includeLinks: args.include_links as boolean | undefined,\n },\n ),\n },\n];\n\n// ---------------------------------------------------------------------\n// MCP server wiring\n// ---------------------------------------------------------------------\n\nconst server = new Server(\n { name: \"propline-mcp\", version: VERSION },\n { capabilities: { tools: {} } },\n);\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (req) => {\n const tool = tools.find((t) => t.name === req.params.name);\n if (!tool) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Unknown tool: ${req.params.name}`,\n },\n ],\n };\n }\n\n try {\n const data = await tool.handler(req.params.arguments ?? {});\n const text =\n typeof data === \"string\" ? data : JSON.stringify(data, null, 2);\n return {\n content: [{ type: \"text\", text }],\n };\n } catch (err) {\n const msg =\n err instanceof PropLineHTTPError\n ? `PropLine API error ${err.statusCode}: ${err.body.slice(0, 500)}`\n : err instanceof Error\n ? err.message\n : String(err);\n return {\n isError: true,\n content: [{ type: \"text\", text: msg }],\n };\n }\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n// stderr is fine in MCP — clients route it to logs without breaking the\n// JSON-RPC stream on stdout.\nconsole.error(`[propline-mcp ${VERSION}] connected via stdio`);\nif (usingDemoKey) {\n console.error(\n \"[propline-mcp] note: no PROPLINE_API_KEY set — running on the shared \" +\n \"free demo key. Tool calls work; paid features are redacted and limits \" +\n \"are pooled. Get your own free key: https://prop-line.com\",\n );\n}\n","/**\n * Thin REST client for the PropLine API. Mirrors the public surface of\n * the `propline` npm package but kept inline here so the MCP server has\n * zero non-MCP runtime dependencies (the official SDK is the right choice\n * for application code; MCP servers want the tightest possible install).\n */\n\nconst DEFAULT_BASE_URL = \"https://api.prop-line.com\";\n\nexport class PropLineHTTPError extends Error {\n constructor(public statusCode: number, public body: string) {\n super(`PropLine HTTP ${statusCode}: ${body.slice(0, 200)}`);\n this.name = \"PropLineHTTPError\";\n }\n}\n\nexport interface ClientOptions {\n apiKey: string;\n baseUrl?: string;\n timeoutMs?: number;\n}\n\nexport class PropLineClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(opts: ClientOptions) {\n if (!opts.apiKey) {\n throw new Error(\n \"PROPLINE_API_KEY is required. Get one at https://prop-line.com\",\n );\n }\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.timeoutMs = opts.timeoutMs ?? 20_000;\n }\n\n private async request<T = unknown>(\n path: string,\n query: Record<string, string | number | boolean | undefined> = {},\n ): Promise<T> {\n const url = new URL(this.baseUrl + path);\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined && v !== null && v !== \"\") {\n url.searchParams.set(k, String(v));\n }\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const r = await fetch(url, {\n headers: {\n \"X-API-Key\": this.apiKey,\n Accept: \"application/json\",\n \"User-Agent\": \"propline-mcp/0.1.0\",\n },\n signal: controller.signal,\n });\n const text = await r.text();\n if (!r.ok) {\n throw new PropLineHTTPError(r.status, text);\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n } finally {\n clearTimeout(timer);\n }\n }\n\n // ----- Discovery -----\n\n listSports(): Promise<unknown> {\n return this.request(\"/v1/sports\");\n }\n\n listEvents(sportKey: string, opts: { live?: boolean } = {}): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events`, {\n live: opts.live ? \"true\" : undefined,\n });\n }\n\n listEventMarkets(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/markets`);\n }\n\n // ----- Odds -----\n\n getOdds(\n sportKey: string,\n opts: {\n markets?: string;\n bookmakers?: string;\n eventId?: string | number;\n period?: string;\n includeLinks?: boolean;\n includeBookIds?: boolean;\n } = {},\n ): Promise<unknown> {\n const params = {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n period: opts.period,\n includeLinks: opts.includeLinks ? \"true\" : undefined,\n includeBookIds: opts.includeBookIds ? \"true\" : undefined,\n };\n if (opts.eventId) {\n return this.request(\n `/v1/sports/${sportKey}/events/${opts.eventId}/odds`,\n params,\n );\n }\n return this.request(`/v1/sports/${sportKey}/odds`, params);\n }\n\n getOddsHistory(\n sportKey: string,\n eventId: string | number,\n opts: {\n markets?: string;\n bookmakers?: string;\n from?: string;\n to?: string;\n relativeFrom?: string;\n relativeTo?: string;\n interval?: string;\n changesOnly?: boolean;\n period?: string;\n } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/odds/history`,\n {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n from: opts.from,\n to: opts.to,\n relative_from: opts.relativeFrom,\n relative_to: opts.relativeTo,\n interval: opts.interval,\n changes_only: opts.changesOnly ? \"true\" : undefined,\n period: opts.period,\n },\n );\n }\n\n getOddsClosing(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; bookmakers?: string; period?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/odds/closing`,\n { markets: opts.markets, bookmakers: opts.bookmakers, period: opts.period },\n );\n }\n\n // ----- Bulk exports -----\n\n /**\n * Full line-movement tick history as CSV text (Backfill pass / Enterprise\n * only — other tiers get a 403). Returns the raw CSV string; the MCP tool\n * layer caps the row count so a large pull never floods the context.\n * A time window (since/until) is required at the tool layer to bound the\n * download.\n */\n exportOddsHistory(\n sportKey: string,\n opts: {\n market?: string;\n bookmaker?: string;\n since?: string;\n until?: string;\n } = {},\n ): Promise<string> {\n return this.request<string>(`/v1/exports/odds-history`, {\n sport: sportKey,\n market: opts.market,\n bookmaker: opts.bookmaker,\n since: opts.since,\n until: opts.until,\n });\n }\n\n // ----- Scores + stats + resolution -----\n\n getScores(sportKey: string, opts: { daysFrom?: number } = {}): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/scores`, {\n daysFrom: opts.daysFrom,\n });\n }\n\n getMlbGrandSalami(opts: { date?: string } = {}): Promise<unknown> {\n return this.request(`/v1/sports/baseball_mlb/grand-salami`, {\n date: opts.date,\n });\n }\n\n getDfsPayouts(\n opts: { platform?: string; legWinProb?: number } = {},\n ): Promise<unknown> {\n return this.request(`/v1/dfs/payouts`, {\n platform: opts.platform ?? \"prizepicks\",\n leg_win_prob: opts.legWinProb,\n });\n }\n\n getNhlDailyGoalsTotal(opts: { date?: string } = {}): Promise<unknown> {\n return this.request(`/v1/sports/hockey_nhl/daily-goals-total`, {\n date: opts.date,\n });\n }\n\n getResolutionSummary(opts: { days?: number } = {}): Promise<unknown> {\n return this.request(`/v1/markets/resolution-summary`, {\n days: opts.days,\n });\n }\n\n getFutures(sportKey: string): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/futures`);\n }\n\n getEventStats(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/stats`);\n }\n\n getEventResults(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/results`);\n }\n\n getEventContext(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/context`);\n }\n\n getEventMovement(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; bookmakers?: string; period?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/movement`,\n { markets: opts.markets, bookmakers: opts.bookmakers, period: opts.period },\n );\n }\n\n // ----- Player history -----\n\n getPlayerHistory(\n sportKey: string,\n playerName: string,\n opts: { limit?: number; markets?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/players/${encodeURIComponent(playerName)}/history`,\n { limit: opts.limit, markets: opts.markets },\n );\n }\n\n // ----- Player trends -----\n\n getPlayerTrends(\n sportKey: string,\n playerName: string,\n opts: { market?: string; dfsOddsType?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/players/${encodeURIComponent(playerName)}/trends`,\n { market: opts.market, dfs_odds_type: opts.dfsOddsType },\n );\n }\n\n // ----- Cross-book +EV -----\n\n getEventEv(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; minEvPct?: number } = {},\n ): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/ev`, {\n markets: opts.markets,\n min_ev_pct: opts.minEvPct,\n });\n }\n\n getEventBestLine(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; bookmakers?: string; includeLinks?: boolean } = {},\n ): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/best-line`, {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n includeLinks: opts.includeLinks ? \"true\" : undefined,\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["/**\n * PropLine MCP server.\n *\n * Exposes the PropLine REST API as Model Context Protocol tools so AI\n * clients (Claude Desktop, Claude Code, ChatGPT desktop with MCP, etc.)\n * can pull live odds, prop resolution, cross-book +EV, scores, and\n * box-score stats directly via tool calls.\n *\n * Run via: npx -y propline-mcp\n * Auth: PROPLINE_API_KEY env var (free key at https://prop-line.com).\n * If unset, falls back to a shared public DEMO key so an agent\n * can answer its first question with zero config — see DEMO_KEY.\n * Optional: PROPLINE_BASE_URL env var (override for self-hosted setups)\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { PropLineClient, PropLineHTTPError } from \"./client.js\";\n\nexport const VERSION = \"0.21.0\";\n\n// Shared public demo key. Baked in on purpose so `npx -y propline-mcp` works\n// with ZERO configuration — an AI agent can discover the server and answer\n// its first question without a human-issued key in the loop. It's a free-tier,\n// read-only, rate-limited key: paid features (resolution, +EV, history,\n// exports) still return the redacted teaser + upgrade_url, so the funnel is\n// intact, and a generous-but-bounded daily cap nudges real production usage\n// toward a personal key. Get your own (higher limits, full features) at\n// https://prop-line.com. Rotated by changing this value AND the seed in the\n// API repo (scraper/seed.py) together.\nexport const DEMO_KEY = \"be2b8487fcfacb1fbc292a8aa925a84c\";\n\nconst apiKey = process.env.PROPLINE_API_KEY;\nconst baseUrl = process.env.PROPLINE_BASE_URL;\nconst usingDemoKey = !apiKey;\n\n// The API key is intentionally NOT required at startup. MCP clients and\n// directory crawlers (Glama, the MCP Registry) must be able to introspect\n// the tool list before a key is configured — exiting here would make the\n// server undiscoverable and break the install-then-configure UX. tools/list\n// never touches the client. When no PROPLINE_API_KEY is set we fall back to\n// the shared DEMO_KEY so the first tool call still succeeds (free-tier data).\nlet _client: PropLineClient | null = null;\nfunction client(): PropLineClient {\n if (!_client) {\n // Demo-key fallback is announced once at startup (see below); no need to\n // repeat it here. apiKey ?? DEMO_KEY keeps the first call working with\n // zero config.\n _client = new PropLineClient({ apiKey: apiKey ?? DEMO_KEY, baseUrl });\n }\n return _client;\n}\n\n/**\n * Apply propline_get_event_ev's `min_ev_pct` to an /ev response.\n *\n * Lives here rather than on the server because /ev has no such\n * parameter — the threshold is a convenience this tool offers so an\n * agent can ask for \"plays above 2% EV\" in one call. Lines left with no\n * qualifying outcome are dropped. A missing/non-numeric threshold, or a\n * response that isn't shaped as expected, passes straight through.\n */\nexport function filterByMinEv(res: unknown, minEvPct?: number): unknown {\n if (typeof minEvPct !== \"number\" || Number.isNaN(minEvPct)) return res;\n if (!res || typeof res !== \"object\") return res;\n const body = res as { lines?: unknown };\n if (!Array.isArray(body.lines)) return res;\n\n const lines = body.lines\n .map((line) => {\n const l = line as { outcomes?: unknown };\n if (!Array.isArray(l.outcomes)) return line;\n const outcomes = l.outcomes.filter((o) => {\n const ev = (o as { ev_pct?: unknown }).ev_pct;\n return typeof ev === \"number\" && ev >= minEvPct;\n });\n return { ...(line as object), outcomes };\n })\n .filter((line) => {\n const outcomes = (line as { outcomes?: unknown }).outcomes;\n return Array.isArray(outcomes) ? outcomes.length > 0 : true;\n });\n\n return { ...(res as object), lines };\n}\n\n// ---------------------------------------------------------------------\n// Tool definitions\n// ---------------------------------------------------------------------\n\ninterface ToolDef {\n name: string;\n description: string;\n inputSchema: {\n type: \"object\";\n properties: Record<string, unknown>;\n required?: string[];\n additionalProperties?: boolean;\n };\n // Returns either JSON-serializable data (will be JSON.stringify'd in the\n // tool result) or a string (returned as-is).\n handler: (args: Record<string, unknown>) => Promise<unknown>;\n}\n\nconst tools: ToolDef[] = [\n {\n name: \"propline_list_sports\",\n description:\n \"List all sports PropLine currently polls. Returns sport keys \" +\n \"(e.g. baseball_mlb, basketball_nba, soccer_epl) along with human \" +\n \"titles and active status. Use this first to discover what \" +\n \"sport_key values are valid for the other tools.\",\n inputSchema: {\n type: \"object\",\n properties: {},\n additionalProperties: false,\n },\n handler: () => client().listSports(),\n },\n {\n name: \"propline_list_events\",\n description:\n \"List upcoming events for a sport. Returns each event's id, \" +\n \"home_team, away_team, commence_time. Use the returned event_id \" +\n \"to drill into per-event odds, props, +EV, or results.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: {\n type: \"string\",\n description:\n \"Sport key from propline_list_sports — e.g. baseball_mlb, basketball_nba\",\n },\n live: {\n type: \"boolean\",\n description:\n \"If true, only return in-progress (live) events. Defaults to false.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().listEvents(args.sport_key as string, {\n live: args.live as boolean | undefined,\n }),\n },\n {\n name: \"propline_list_event_markets\",\n description:\n \"List the market types available for a specific event (e.g. h2h, \" +\n \"spreads, totals, player_points, pitcher_strikeouts). Useful when \" +\n \"you don't know which prop markets a given event carries.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().listEventMarkets(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_odds\",\n description:\n \"Get live odds. If event_id is supplied, returns full per-event \" +\n \"props for that event; otherwise returns bulk game-line odds for \" +\n \"the whole sport. Pass markets as a comma-separated list (e.g. \" +\n \"'h2h,spreads,totals' or 'player_points,player_rebounds'). \" +\n \"Response includes a bookmakers[] array across every book that \" +\n \"carries the requested markets (currently up to 13: Bovada, \" +\n \"DraftKings, FanDuel, Pinnacle, BetMGM, BetRivers, Unibet, \" +\n \"Underdog, PrizePicks, Kalshi, Polymarket, Matchbook, Smarkets \" +\n \"— coverage varies by sport). Underdog Fantasy outcomes carry a \" +\n \"payout_multiplier on EVERY outcome (1.0 = standard pick, e.g. \" +\n \"1.5 = boost, 0.75 = discount; null means the book is not \" +\n \"Underdog) — keep only payout_multiplier == 1.0 when comparing \" +\n \"DFS lines to sportsbook consensus, since filtering on non-null \" +\n \"would drop every Underdog line.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: {\n type: [\"string\", \"number\"],\n description: \"Optional. If set, returns props for this event.\",\n },\n markets: {\n type: \"string\",\n description:\n \"Comma-separated market keys. Defaults to h2h on bulk; h2h,spreads,totals on event. Pass an explicit list to fetch player props (sport-specific — e.g. player_points,player_rebounds for NBA; pitcher_strikeouts,batter_home_runs for MLB).\",\n },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (bovada, draftkings, fanduel, pinnacle, betmgm, betrivers, unibet, underdog, prizepicks, kalshi, polymarket, matchbook, smarkets). Default returns all available.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter. Omitted = full-game markets only. Canonical codes: q1..q4 (quarters), h1/h2 (halves), p1..p3 (hockey periods), i1..i9 (innings), f3/f5/f7 (first N innings). Comma-separated for multiple. 'all' = include every period alongside full-game.\",\n },\n include_links: {\n type: \"boolean\",\n description:\n \"When true, each bookmaker block carries a link — that book's public event-page URL for click-out (Bovada/DraftKings/FanDuel/BetMGM/Kalshi/Polymarket/Smarkets; others null). Plain navigation, no affiliate tagging.\",\n },\n include_book_ids: {\n type: \"boolean\",\n description:\n \"When true, each bookmaker block carries book_event_id and each outcome carries book_outcome_id — that book's OWN ids for the event and the priced selection, for joining onto a book's native feed by id instead of matching team/player names and lines. Kalshi ships both (event ticker + per-contract market ticker, e.g. KXMLBGAME-26AUG08NYYBOS-NYY); most other books ship an event id; books without a stable id return null. NB a two-sided market can share ONE book_outcome_id across both legs — a Kalshi contract is binary, so Over/Under are its YES/NO sides; the outcome's name says which side.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getOdds(args.sport_key as string, {\n eventId: args.event_id as string | number | undefined,\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n period: args.period as string | undefined,\n includeLinks: args.include_links as boolean | undefined,\n includeBookIds: args.include_book_ids as boolean | undefined,\n }),\n },\n {\n name: \"propline_get_odds_history\",\n description:\n \"Hobby+ endpoint. Returns the historical line-movement snapshot \" +\n \"series for an event (every recorded price/point change per \" +\n \"outcome over the event's lifetime). Free tier returns market \" +\n \"structure with redacted snapshots and an upgrade pointer. \" +\n \"Supports period-historical filters: from/to (absolute ISO), \" +\n \"relative_from/relative_to (offsets to commence_time like '-3h' \" +\n \"or '0'), interval downsample ('30s'/'1m'/'5m'/'15m'/'30m'/'1h'), \" +\n \"and changes_only=true to drop unchanged adjacent snapshots.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: { type: \"string\" },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (e.g. 'draftkings,fanduel'). Default returns all available.\",\n },\n from: {\n type: \"string\",\n description:\n \"ISO timestamp; only include snapshots at or after. Mutually exclusive with relative_from.\",\n },\n to: {\n type: \"string\",\n description:\n \"ISO timestamp; only include snapshots at or before. Mutually exclusive with relative_to.\",\n },\n relative_from: {\n type: \"string\",\n description:\n \"Offset to commence_time, e.g. '-3h', '-30m', '-90s'. Mutually exclusive with from.\",\n },\n relative_to: {\n type: \"string\",\n description:\n \"Offset to commence_time, e.g. '-1m' or '0'. Mutually exclusive with to.\",\n },\n interval: {\n type: \"string\",\n enum: [\"30s\", \"1m\", \"5m\", \"15m\", \"30m\", \"1h\"],\n description: \"Downsample bucket. Latest snapshot per bucket wins.\",\n },\n changes_only: {\n type: \"boolean\",\n description:\n \"When true, drop snapshots whose (price, point) match the previous one.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter. Omitted = full-game markets only. Canonical codes (q1..q4, h1/h2, p1..p3, i1..i9, f3/f5/f7), comma-separated, or 'all'.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getOddsHistory(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n from: args.from as string | undefined,\n to: args.to as string | undefined,\n relativeFrom: args.relative_from as string | undefined,\n relativeTo: args.relative_to as string | undefined,\n interval: args.interval as string | undefined,\n changesOnly: args.changes_only as boolean | undefined,\n period: args.period as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_odds_closing\",\n description:\n \"Hobby+ endpoint. Returns the OPENING and CLOSING line per (book, \" +\n \"market, outcome) for an event. Closing = the last snapshot at or \" +\n \"before commence_time (price/point/closing_at); opening = the \" +\n \"first snapshot in the same 14-day pre-kickoff window \" +\n \"(opening_price/opening_point/opening_at). Canonical CLV-tracking \" +\n \"helper; one call returns both data points your bet should be \" +\n \"measured against, instead of fetching full history and \" +\n \"post-processing. Compare the POINTS as well as the prices — on \" +\n \"spreads and totals the number moves as much as the price, so a \" +\n \"price-only comparison mis-measures those markets. \" +\n \"opening_age_seconds says how long before kickoff the opener was \" +\n \"recorded: the archive starts 2026-04, so a small value means \" +\n \"PropLine started polling late and this is not the book's true \" +\n \"open. Free tier returns redacted structure with upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: { type: \"string\" },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (e.g. 'draftkings,fanduel'). Default returns all available.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter. Omitted = full-game markets only. Canonical codes (q1..q4, h1/h2, p1..p3, i1..i9, f3/f5/f7), comma-separated, or 'all'.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getOddsClosing(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n period: args.period as string | undefined,\n },\n ),\n },\n {\n name: \"propline_export_odds_history\",\n description:\n \"Backfill-pass / Enterprise only. Bulk line-movement tick history \" +\n \"as CSV — every recorded odds snapshot (price + line, per book, \" +\n \"including period markets) across a whole sport, one row per \" +\n \"(outcome, snapshot). This is the raw firehose no subscription tier \" +\n \"can bulk-pull (Pro/Streaming use propline_get_odds_history per \" +\n \"event instead). REQUIRES a since/until window to keep the pull \" +\n \"bounded — the full archive runs to gigabytes per sport. The result \" +\n \"is capped to the first 200 rows for context safety; for the full \" +\n \"dataset use the /v1/exports/odds-history endpoint directly with \" +\n \"curl/SDK and stream to disk. Non-entitled keys get a 403 with an \" +\n \"upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: {\n type: \"string\",\n description: \"Sport key, e.g. baseball_mlb\",\n },\n since: {\n type: \"string\",\n description:\n \"ISO datetime lower bound on recorded_at (required, e.g. 2026-04-01T00:00:00Z). Keep the window narrow.\",\n },\n until: {\n type: \"string\",\n description:\n \"ISO datetime upper bound on recorded_at (required, e.g. 2026-05-01T00:00:00Z).\",\n },\n market: { type: \"string\", description: \"Optional market key filter\" },\n bookmaker: { type: \"string\", description: \"Optional bookmaker filter\" },\n },\n required: [\"sport_key\", \"since\", \"until\"],\n additionalProperties: false,\n },\n handler: async (args) => {\n const csv = await client().exportOddsHistory(args.sport_key as string, {\n since: args.since as string,\n until: args.until as string,\n market: args.market as string | undefined,\n bookmaker: args.bookmaker as string | undefined,\n });\n const lines = csv.split(\"\\n\");\n const CAP = 200; // header + 200 data rows\n if (lines.length <= CAP + 1) return csv;\n return (\n lines.slice(0, CAP + 1).join(\"\\n\") +\n `\\n# … truncated: ${lines.length - 1} total rows. ` +\n `Narrow since/until or pull the full file via the ` +\n `/v1/exports/odds-history endpoint (curl/SDK) and stream to disk.`\n );\n },\n },\n {\n name: \"propline_get_futures\",\n description:\n \"Free-tier endpoint. Returns season-long futures (outright) markets \" +\n \"for a sport — championship/Super Bowl/division/conference winners, \" +\n \"MVP and award winners, season win totals — aggregated across \" +\n \"Bovada, FanDuel, DraftKings, and Pinnacle. One row per (futures \" +\n \"event, book, market) with each team/player outcome and its price. \" +\n \"Marquee markets (Super Bowl winner, MVP, division/conference) are \" +\n \"quoted by multiple books for comparison; exotic markets are often \" +\n \"single-book. Useful for: 'who are the Super Bowl favorites across \" +\n \"books', 'NFL MVP odds', 'NBA championship futures'. Futures are \" +\n \"unresolved (no settlement grade).\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: {\n type: \"string\",\n description:\n \"Sport key — e.g. football_nfl, basketball_nba, baseball_mlb.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) => client().getFutures(args.sport_key as string),\n },\n {\n name: \"propline_get_scores\",\n description:\n \"Free-tier endpoint. Returns recent and live game scores plus \" +\n \"status (scheduled, live, final) for a sport. Useful for: 'is \" +\n \"this game over yet, what was the final score'.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n days_from: {\n type: \"number\",\n description:\n \"How many past days of completed games to include. Defaults to 1.\",\n },\n },\n required: [\"sport_key\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getScores(args.sport_key as string, {\n daysFrom: args.days_from as number | undefined,\n }),\n },\n {\n name: \"propline_get_dfs_payouts\",\n description:\n \"Free-tier reference math. Returns the PrizePicks Power Play (all legs \" +\n \"must hit) and Flex Play (partial payouts) entry payout schedule for \" +\n \"2-6 legs, plus the per-leg breakeven win probability for each play. \" +\n \"Pass leg_win_prob (e.g. 0.58) to also get expected_return (per $1) and \" +\n \"is_plus_ev per play — the slip-level breakeven. Useful for: 'what hit \" +\n \"rate do I need to beat a 4-pick PrizePicks Power play', 'is a 3-leg \" +\n \"flex +EV at 60% per leg'. NOTE: standard published payouts only — \" +\n \"demon/goblin per-pick modifiers aren't in PrizePicks's feed (see the \" +\n \"disclaimer field); breakeven assumes independent legs.\",\n inputSchema: {\n type: \"object\",\n properties: {\n leg_win_prob: {\n type: \"number\",\n description:\n \"Optional assumed per-leg win probability in [0,1]. Adds \" +\n \"expected_return + is_plus_ev to each play.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getDfsPayouts({\n legWinProb: args.leg_win_prob as number | undefined,\n }),\n },\n {\n name: \"propline_get_mlb_grand_salami\",\n description:\n \"Free-tier endpoint. Returns the synthetic daily MLB Grand Salami \" +\n \"for a given UTC date — total runs scored across every MLB game \" +\n \"on the slate plus each book's implied Grand Salami line (median \" +\n \"of per-game primary totals across our MLB books incl. Pinnacle, \" +\n \"Polymarket, Matchbook, Smarkets). No retail sportsbook quotes \" +\n \"this as a single market. Useful for: 'what's the total run line \" +\n \"for tonight's full MLB slate', 'did the Grand Salami go over \" +\n \"yesterday', 'historical Grand Salami results for backtesting'.\",\n inputSchema: {\n type: \"object\",\n properties: {\n date: {\n type: \"string\",\n description:\n \"YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getMlbGrandSalami({\n date: args.date as string | undefined,\n }),\n },\n {\n name: \"propline_get_nhl_daily_goals_total\",\n description:\n \"Free-tier endpoint. Returns the synthetic daily NHL goals total \" +\n \"(hockey's equivalent of the MLB Grand Salami) for a given UTC \" +\n \"date — total goals scored across every NHL game on the slate \" +\n \"(including OT/SO) plus each book's implied Daily Goals Total \" +\n \"line (median of per-game primary totals across our NHL books). \" +\n \"No retail sportsbook quotes this as a single market. Useful \" +\n \"for: 'what's the total goal line for tonight's full NHL slate', \" +\n \"'did the Daily Goals Total go over yesterday', 'historical \" +\n \"NHL daily-goals results for backtesting'.\",\n inputSchema: {\n type: \"object\",\n properties: {\n date: {\n type: \"string\",\n description:\n \"YYYY-MM-DD UTC date. Defaults to today (UTC) when omitted.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getNhlDailyGoalsTotal({\n date: args.date as string | undefined,\n }),\n },\n {\n name: \"propline_get_resolution_summary\",\n description:\n \"Free-tier endpoint. Returns the factual volume of player props \" +\n \"PropLine has graded against real box scores over the last N days \" +\n \"(aggregated counts only): total graded/settled, games, sports \" +\n \"covered, plus per-sport and top-market breakdowns. Useful for: \" +\n \"'how much graded prop data does PropLine have, what's the \" +\n \"coverage'. A coverage proof, never a profitability claim.\",\n inputSchema: {\n type: \"object\",\n properties: {\n days: {\n type: \"number\",\n description: \"Look-back window, 1-90. Defaults to 30.\",\n },\n },\n required: [],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getResolutionSummary({ days: args.days as number | undefined }),\n },\n {\n name: \"propline_get_event_stats\",\n description:\n \"Book-agnostic raw box-score stats for an event. Returns per-player \" +\n \"stats (e.g. strikeouts, hits, points, rebounds, shots-on-goal) \" +\n \"decoupled from any sportsbook's lines. LIVE during games for major \" +\n \"US sports (MLB + WNBA now; NFL, NCAAF, NBA, NHL at season start): \" +\n \"while the event's status is in_progress, stats refresh ~every 90 \" +\n \"seconds with cumulative in-game values — use this to answer 'how \" +\n \"is this prop tracking right now'. Treat in-progress numbers as \" +\n \"partial; at status=final they are the official box score. Other \" +\n \"sports populate stats at game completion. Free tier.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventStats(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_event_results\",\n description:\n \"Pro-tier endpoint. Returns graded prop outcomes for a completed \" +\n \"event — every Over/Under marked won, lost, push, or void with \" +\n \"the actual stat value next to the line. The single most \" +\n \"distinctive feature vs the-odds-api: they don't grade props at \" +\n \"any tier. Free tier returns the same structure with resolution \" +\n \"and actual_value redacted plus an upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventResults(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_event_context\",\n description:\n \"Game context for an event — the conditions a prop settles under. \" +\n \"MLB: probable starting pitchers and their throwing hand (L/R/S — \" +\n \"platoon-split context for every batter prop), a confirmed-lineup \" +\n \"flag, the home-plate umpire, and first-pitch weather (temperature, \" +\n \"wind, precipitation) at outdoor / open-roof venues (indoor venues \" +\n \"return weather=null). NFL & NCAAF: the venue and kickoff weather. \" +\n \"The same block is embedded in get_event_results, so every graded \" +\n \"prop carries its conditions — unique to PropLine. Free tier. 404 \" +\n \"when no context is on file for the event yet.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventContext(\n args.sport_key as string,\n args.event_id as string | number,\n ),\n },\n {\n name: \"propline_get_event_movement\",\n description:\n \"Line movement + steam detection from the snapshot tick history. \" +\n \"Per (book, market, outcome): opening line, latest line, signed \" +\n \"implied-probability shift, point shift, direction. The steam[] \" +\n \"array flags outcomes that multiple books moved the same direction — \" +\n \"the classic sharp-money signal, computed across all 16 books \" +\n \"PropLine polls. When a book moves the line itself, that outcome's \" +\n \"prob_shift is null and direction is 'line_moved' (excluded from the \" +\n \"steam signal). No pull-only odds API can produce this. Hobby+ full; \" +\n \"free tier redacted.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: {\n type: \"string\",\n description:\n \"Comma-separated market keys. Defaults to h2h,spreads,totals.\",\n },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (e.g. 'draftkings,fanduel'). Default returns all available.\",\n },\n period: {\n type: \"string\",\n description:\n \"Game-period filter (q1..q4, h1/h2, p1..p3, i1..i9, f3/f5/f7; comma-separated, or 'all'). Omit for full-game.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventMovement(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n period: args.period as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_player_history\",\n description:\n \"Player prop history across recent games. Returns each prior prop \" +\n \"this player took with line, prices, resolution, and actual value. \" +\n \"Pro tier returns full data; free tier returns redacted \" +\n \"resolution/actual_value with an upgrade pointer.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n player_name: {\n type: \"string\",\n description:\n \"Player name as it appears in box scores — e.g. 'Aaron Judge', 'Nikola Jokic'\",\n },\n limit: {\n type: \"number\",\n description: \"Max number of past games (default 20, max 100)\",\n },\n markets: {\n type: \"string\",\n description:\n \"Comma-separated subset of markets (e.g. 'player_points,player_rebounds')\",\n },\n },\n required: [\"sport_key\", \"player_name\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getPlayerHistory(\n args.sport_key as string,\n args.player_name as string,\n {\n limit: args.limit as number | undefined,\n markets: args.markets as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_player_trends\",\n description:\n \"Hit-rate trends / last-N-games over rate for a player — unique to \" +\n \"PropLine's prop-resolution data. For each market the player has \" +\n \"graded history in, returns over/under/push splits across the last \" +\n \"5/10/20/50 graded games, current streak, average actual stat, and \" +\n \"the recent line. This is the 'did X go over in N of his last M \" +\n \"games?' surface. Omit `market` for all markets, or pass one to \" +\n \"scope (e.g. 'player_points', 'batter_hits').\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n player_name: {\n type: \"string\",\n description:\n \"Player name as it appears in box scores — e.g. 'Aaron Judge', 'Nikola Jokic'\",\n },\n market: {\n type: \"string\",\n description:\n \"Optional single market to scope trends to (e.g. 'player_points'). Omit for all markets.\",\n },\n dfs_odds_type: {\n type: \"string\",\n enum: [\"standard\", \"goblin\", \"demon\"],\n description:\n \"Optional PrizePicks pick-em flavor. When set, the trend is computed against that flavor's PrizePicks line only (e.g. compare a player's goblin-line hit-rate vs his standard-line trend). Omit for the default cross-book behavior.\",\n },\n },\n required: [\"sport_key\", \"player_name\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getPlayerTrends(\n args.sport_key as string,\n args.player_name as string,\n {\n market: args.market as string | undefined,\n dfsOddsType: args.dfs_odds_type as string | undefined,\n },\n ),\n },\n {\n name: \"propline_get_event_ev\",\n description:\n \"Pro-tier endpoint. Returns cross-book +EV per outcome for an \" +\n \"event. We anchor on a sharp book, remove vig, derive a no-vig \" +\n \"fair line, and compute EV% per book at the same line. Outcomes \" +\n \"are sorted with +EV plays floated to the top of each line group. \" +\n \"PrizePicks is excluded from EV math (DFS payouts aren't \" +\n \"comparable to per-book prices). The anchor is chosen PER LINE in \" +\n \"the order pinnacle → polymarket → kalshi → bovada, and each \" +\n \"line's fair_source names the one used — report the anchor from \" +\n \"fair_source per line, never assume Pinnacle anchored all of them. \" +\n \"Optional bookmakers filter prices to the books the user holds \" +\n \"accounts at; it never changes the anchor, so filtering to \" +\n \"DraftKings still measures DraftKings against Pinnacle.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: { type: \"string\" },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated book keys (e.g. 'draftkings,fanduel') to \" +\n \"price only the user's books. Narrows prices, not the anchor.\",\n },\n min_ev_pct: {\n type: \"number\",\n description: \"Filter to outcomes with EV ≥ this percent (e.g. 2.0).\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: async (args) => {\n const res = await client().getEventEv(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n },\n );\n // min_ev_pct is applied HERE, client-side. It used to be forwarded\n // as a query param that /ev has never accepted, so the threshold\n // was silently dropped and the model got every outcome back while\n // the tool claimed to have filtered.\n return filterByMinEv(res, args.min_ev_pct as number | undefined);\n },\n },\n {\n name: \"propline_get_best_line\",\n description:\n \"Cross-book line shopping (Hobby+ for prices; free tier gets the \" +\n \"full structure with book identities + best-first ranking but \" +\n \"prices nulled and redacted:true). For every (market, \" +\n \"player, line) tuple on an event, returns the single best American \" +\n \"price across all comparable books, plus an all_prices array \" +\n \"sorted best-first (one row per book, each with last_update). \" +\n \"Companion to propline_get_event_ev: /ev says whether a price \" +\n \"beats the no-vig fair line; best-line says which book pays the \" +\n \"most. DFS pick'em books (PrizePicks, Sleeper, Dabble) are \" +\n \"excluded; Underdog only at clean two-way lines. Optional \" +\n \"bookmakers filter to shop only the books the user holds \" +\n \"accounts at.\",\n inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: {\n type: \"string\",\n description: \"Comma-separated market keys (e.g. 'pitcher_strikeouts,h2h').\",\n },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated book keys (e.g. 'draftkings,fanduel') to shop only those books.\",\n },\n include_links: {\n type: \"boolean\",\n description:\n \"When true, every price row carries a link — that book's public event-page URL, the click-out for 'go bet this'. Books without a verified URL template return null.\",\n },\n },\n required: [\"sport_key\", \"event_id\"],\n additionalProperties: false,\n },\n handler: (args) =>\n client().getEventBestLine(\n args.sport_key as string,\n args.event_id as string | number,\n {\n markets: args.markets as string | undefined,\n bookmakers: args.bookmakers as string | undefined,\n includeLinks: args.include_links as boolean | undefined,\n },\n ),\n },\n];\n\n// ---------------------------------------------------------------------\n// MCP server wiring\n// ---------------------------------------------------------------------\n\nconst server = new Server(\n { name: \"propline-mcp\", version: VERSION },\n { capabilities: { tools: {} } },\n);\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: tools.map((t) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (req) => {\n const tool = tools.find((t) => t.name === req.params.name);\n if (!tool) {\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: `Unknown tool: ${req.params.name}`,\n },\n ],\n };\n }\n\n try {\n const data = await tool.handler(req.params.arguments ?? {});\n const text =\n typeof data === \"string\" ? data : JSON.stringify(data, null, 2);\n return {\n content: [{ type: \"text\", text }],\n };\n } catch (err) {\n const msg =\n err instanceof PropLineHTTPError\n ? `PropLine API error ${err.statusCode}: ${err.body.slice(0, 500)}`\n : err instanceof Error\n ? err.message\n : String(err);\n return {\n isError: true,\n content: [{ type: \"text\", text: msg }],\n };\n }\n});\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n// stderr is fine in MCP — clients route it to logs without breaking the\n// JSON-RPC stream on stdout.\nconsole.error(`[propline-mcp ${VERSION}] connected via stdio`);\nif (usingDemoKey) {\n console.error(\n \"[propline-mcp] note: no PROPLINE_API_KEY set — running on the shared \" +\n \"free demo key. Tool calls work; paid features are redacted and limits \" +\n \"are pooled. Get your own free key: https://prop-line.com\",\n );\n}\n","/**\n * Thin REST client for the PropLine API. Mirrors the public surface of\n * the `propline` npm package but kept inline here so the MCP server has\n * zero non-MCP runtime dependencies (the official SDK is the right choice\n * for application code; MCP servers want the tightest possible install).\n */\n\nconst DEFAULT_BASE_URL = \"https://api.prop-line.com\";\n\nexport class PropLineHTTPError extends Error {\n constructor(public statusCode: number, public body: string) {\n super(`PropLine HTTP ${statusCode}: ${body.slice(0, 200)}`);\n this.name = \"PropLineHTTPError\";\n }\n}\n\nexport interface ClientOptions {\n apiKey: string;\n baseUrl?: string;\n timeoutMs?: number;\n}\n\nexport class PropLineClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(opts: ClientOptions) {\n if (!opts.apiKey) {\n throw new Error(\n \"PROPLINE_API_KEY is required. Get one at https://prop-line.com\",\n );\n }\n this.apiKey = opts.apiKey;\n this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, \"\");\n this.timeoutMs = opts.timeoutMs ?? 20_000;\n }\n\n private async request<T = unknown>(\n path: string,\n query: Record<string, string | number | boolean | undefined> = {},\n ): Promise<T> {\n const url = new URL(this.baseUrl + path);\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined && v !== null && v !== \"\") {\n url.searchParams.set(k, String(v));\n }\n }\n\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n try {\n const r = await fetch(url, {\n headers: {\n \"X-API-Key\": this.apiKey,\n Accept: \"application/json\",\n \"User-Agent\": \"propline-mcp/0.1.0\",\n },\n signal: controller.signal,\n });\n const text = await r.text();\n if (!r.ok) {\n throw new PropLineHTTPError(r.status, text);\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n } finally {\n clearTimeout(timer);\n }\n }\n\n // ----- Discovery -----\n\n listSports(): Promise<unknown> {\n return this.request(\"/v1/sports\");\n }\n\n listEvents(sportKey: string, opts: { live?: boolean } = {}): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events`, {\n live: opts.live ? \"true\" : undefined,\n });\n }\n\n listEventMarkets(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/markets`);\n }\n\n // ----- Odds -----\n\n getOdds(\n sportKey: string,\n opts: {\n markets?: string;\n bookmakers?: string;\n eventId?: string | number;\n period?: string;\n includeLinks?: boolean;\n includeBookIds?: boolean;\n } = {},\n ): Promise<unknown> {\n const params = {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n period: opts.period,\n includeLinks: opts.includeLinks ? \"true\" : undefined,\n includeBookIds: opts.includeBookIds ? \"true\" : undefined,\n };\n if (opts.eventId) {\n return this.request(\n `/v1/sports/${sportKey}/events/${opts.eventId}/odds`,\n params,\n );\n }\n return this.request(`/v1/sports/${sportKey}/odds`, params);\n }\n\n getOddsHistory(\n sportKey: string,\n eventId: string | number,\n opts: {\n markets?: string;\n bookmakers?: string;\n from?: string;\n to?: string;\n relativeFrom?: string;\n relativeTo?: string;\n interval?: string;\n changesOnly?: boolean;\n period?: string;\n } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/odds/history`,\n {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n from: opts.from,\n to: opts.to,\n relative_from: opts.relativeFrom,\n relative_to: opts.relativeTo,\n interval: opts.interval,\n changes_only: opts.changesOnly ? \"true\" : undefined,\n period: opts.period,\n },\n );\n }\n\n getOddsClosing(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; bookmakers?: string; period?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/odds/closing`,\n { markets: opts.markets, bookmakers: opts.bookmakers, period: opts.period },\n );\n }\n\n // ----- Bulk exports -----\n\n /**\n * Full line-movement tick history as CSV text (Backfill pass / Enterprise\n * only — other tiers get a 403). Returns the raw CSV string; the MCP tool\n * layer caps the row count so a large pull never floods the context.\n * A time window (since/until) is required at the tool layer to bound the\n * download.\n */\n exportOddsHistory(\n sportKey: string,\n opts: {\n market?: string;\n bookmaker?: string;\n since?: string;\n until?: string;\n } = {},\n ): Promise<string> {\n return this.request<string>(`/v1/exports/odds-history`, {\n sport: sportKey,\n market: opts.market,\n bookmaker: opts.bookmaker,\n since: opts.since,\n until: opts.until,\n });\n }\n\n // ----- Scores + stats + resolution -----\n\n getScores(sportKey: string, opts: { daysFrom?: number } = {}): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/scores`, {\n daysFrom: opts.daysFrom,\n });\n }\n\n getMlbGrandSalami(opts: { date?: string } = {}): Promise<unknown> {\n return this.request(`/v1/sports/baseball_mlb/grand-salami`, {\n date: opts.date,\n });\n }\n\n getDfsPayouts(\n opts: { platform?: string; legWinProb?: number } = {},\n ): Promise<unknown> {\n return this.request(`/v1/dfs/payouts`, {\n platform: opts.platform ?? \"prizepicks\",\n leg_win_prob: opts.legWinProb,\n });\n }\n\n getNhlDailyGoalsTotal(opts: { date?: string } = {}): Promise<unknown> {\n return this.request(`/v1/sports/hockey_nhl/daily-goals-total`, {\n date: opts.date,\n });\n }\n\n getResolutionSummary(opts: { days?: number } = {}): Promise<unknown> {\n return this.request(`/v1/markets/resolution-summary`, {\n days: opts.days,\n });\n }\n\n getFutures(sportKey: string): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/futures`);\n }\n\n getEventStats(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/stats`);\n }\n\n getEventResults(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/results`);\n }\n\n getEventContext(sportKey: string, eventId: string | number): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/context`);\n }\n\n getEventMovement(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; bookmakers?: string; period?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/movement`,\n { markets: opts.markets, bookmakers: opts.bookmakers, period: opts.period },\n );\n }\n\n // ----- Player history -----\n\n getPlayerHistory(\n sportKey: string,\n playerName: string,\n opts: { limit?: number; markets?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/players/${encodeURIComponent(playerName)}/history`,\n { limit: opts.limit, markets: opts.markets },\n );\n }\n\n // ----- Player trends -----\n\n getPlayerTrends(\n sportKey: string,\n playerName: string,\n opts: { market?: string; dfsOddsType?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/players/${encodeURIComponent(playerName)}/trends`,\n { market: opts.market, dfs_odds_type: opts.dfsOddsType },\n );\n }\n\n // ----- Cross-book +EV -----\n\n getEventEv(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; bookmakers?: string } = {},\n ): Promise<unknown> {\n // NB: no min_ev_pct here. This client used to send one, but /ev has\n // never accepted that parameter — FastAPI drops unknown query params,\n // so the request succeeded and the threshold was silently ignored.\n // The tool handler filters on ev_pct itself instead.\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/ev`, {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n });\n }\n\n getEventBestLine(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string; bookmakers?: string; includeLinks?: boolean } = {},\n ): Promise<unknown> {\n return this.request(`/v1/sports/${sportKey}/events/${eventId}/best-line`, {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n includeLinks: opts.includeLinks ? \"true\" : undefined,\n });\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "propline-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"mcpName": "io.github.proplineapi/propline-mcp",
|
|
5
5
|
"description": "Model Context Protocol server for the PropLine player props betting odds API. Exposes live odds, prop resolution, cross-book +EV, scores, and box-score stats as tool calls for Claude Desktop, Claude Code, and any MCP-compatible client.",
|
|
6
6
|
"keywords": [
|