propline-mcp 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,7 +27,8 @@ The model uses these tools transparently:
27
27
  | `propline_list_events` | Upcoming events for a sport, with ids |
28
28
  | `propline_list_event_markets` | Available market types for an event |
29
29
  | `propline_get_odds` | Live odds — bulk by sport or full props per event |
30
- | `propline_get_odds_history` | Pro: full snapshot history per outcome |
30
+ | `propline_get_odds_history` | Hobby+: snapshot history per outcome; supports period filters (from/to, relative_from/relative_to, interval, changes_only) |
31
+ | `propline_get_odds_closing` | Hobby+: closing line per (book, market, outcome) — CLV helper |
31
32
  | `propline_get_scores` | Game scores + status (free) |
32
33
  | `propline_get_resolution_summary` | Graded-prop volume + per-sport/market breakdown (free) |
33
34
  | `propline_get_event_stats` | Raw box-score stats (free, book-agnostic) |
package/dist/index.js CHANGED
@@ -96,6 +96,20 @@ var PropLineClient = class {
96
96
  getOddsHistory(sportKey, eventId, opts = {}) {
97
97
  return this.request(
98
98
  `/v1/sports/${sportKey}/events/${eventId}/odds/history`,
99
+ {
100
+ markets: opts.markets,
101
+ from: opts.from,
102
+ to: opts.to,
103
+ relative_from: opts.relativeFrom,
104
+ relative_to: opts.relativeTo,
105
+ interval: opts.interval,
106
+ changes_only: opts.changesOnly ? "true" : void 0
107
+ }
108
+ );
109
+ }
110
+ getOddsClosing(sportKey, eventId, opts = {}) {
111
+ return this.request(
112
+ `/v1/sports/${sportKey}/events/${eventId}/odds/closing`,
99
113
  { markets: opts.markets }
100
114
  );
101
115
  }
@@ -138,7 +152,7 @@ var PropLineClient = class {
138
152
  };
139
153
 
140
154
  // src/index.ts
141
- var VERSION = "0.3.0";
155
+ var VERSION = "0.4.0";
142
156
  var apiKey = process.env.PROPLINE_API_KEY;
143
157
  var baseUrl = process.env.PROPLINE_BASE_URL;
144
158
  var _client = null;
@@ -234,18 +248,70 @@ var tools = [
234
248
  },
235
249
  {
236
250
  name: "propline_get_odds_history",
237
- description: "Pro-tier endpoint. Returns the historical line-movement snapshot series for an event (every recorded price/point change per outcome over the event's lifetime). Free tier returns market structure with redacted snapshots and an upgrade pointer.",
251
+ description: "Hobby+ endpoint. Returns the historical line-movement snapshot series for an event (every recorded price/point change per outcome over the event's lifetime). Free tier returns market structure with redacted snapshots and an upgrade pointer. Supports period-historical filters: from/to (absolute ISO), relative_from/relative_to (offsets to commence_time like '-3h' or '0'), interval downsample ('30s'/'1m'/'5m'/'15m'/'30m'/'1h'), and changes_only=true to drop unchanged adjacent snapshots.",
238
252
  inputSchema: {
239
253
  type: "object",
240
254
  properties: {
241
255
  sport_key: { type: "string" },
242
256
  event_id: { type: ["string", "number"] },
243
- markets: { type: "string" }
257
+ markets: { type: "string" },
258
+ from: {
259
+ type: "string",
260
+ description: "ISO timestamp; only include snapshots at or after. Mutually exclusive with relative_from."
261
+ },
262
+ to: {
263
+ type: "string",
264
+ description: "ISO timestamp; only include snapshots at or before. Mutually exclusive with relative_to."
265
+ },
266
+ relative_from: {
267
+ type: "string",
268
+ description: "Offset to commence_time, e.g. '-3h', '-30m', '-90s'. Mutually exclusive with from."
269
+ },
270
+ relative_to: {
271
+ type: "string",
272
+ description: "Offset to commence_time, e.g. '-1m' or '0'. Mutually exclusive with to."
273
+ },
274
+ interval: {
275
+ type: "string",
276
+ enum: ["30s", "1m", "5m", "15m", "30m", "1h"],
277
+ description: "Downsample bucket. Latest snapshot per bucket wins."
278
+ },
279
+ changes_only: {
280
+ type: "boolean",
281
+ description: "When true, drop snapshots whose (price, point) match the previous one."
282
+ }
244
283
  },
245
284
  required: ["sport_key", "event_id"],
246
285
  additionalProperties: false
247
286
  },
248
287
  handler: (args) => client().getOddsHistory(
288
+ args.sport_key,
289
+ args.event_id,
290
+ {
291
+ markets: args.markets,
292
+ from: args.from,
293
+ to: args.to,
294
+ relativeFrom: args.relative_from,
295
+ relativeTo: args.relative_to,
296
+ interval: args.interval,
297
+ changesOnly: args.changes_only
298
+ }
299
+ )
300
+ },
301
+ {
302
+ name: "propline_get_odds_closing",
303
+ description: "Hobby+ endpoint. Returns the closing line per (book, market, outcome) for an event \u2014 the last snapshot at or before commence_time. Canonical CLV-tracking helper; one call returns the data point your bet should be measured against, instead of fetching full history and post-processing. Each outcome carries a closing_at field with the snapshot's recorded_at. Free tier returns redacted structure with upgrade pointer.",
304
+ inputSchema: {
305
+ type: "object",
306
+ properties: {
307
+ sport_key: { type: "string" },
308
+ event_id: { type: ["string", "number"] },
309
+ markets: { type: "string" }
310
+ },
311
+ required: ["sport_key", "event_id"],
312
+ additionalProperties: false
313
+ },
314
+ handler: (args) => client().getOddsClosing(
249
315
  args.sport_key,
250
316
  args.event_id,
251
317
  { markets: args.markets }
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 * 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.3.0\";\n\nconst apiKey = process.env.PROPLINE_API_KEY;\nconst baseUrl = process.env.PROPLINE_BASE_URL;\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. The key\n// is enforced lazily on the first real tool call instead; tools/list never\n// touches the client.\nlet _client: PropLineClient | null = null;\nfunction client(): PropLineClient {\n if (!_client) {\n if (!apiKey) {\n throw new Error(\n \"PROPLINE_API_KEY is not set. Get a free key at \" +\n \"https://prop-line.com and add it to your MCP client config \" +\n \"(env var: PROPLINE_API_KEY).\",\n );\n }\n _client = new PropLineClient({ apiKey, 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).\",\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 },\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 }),\n },\n {\n name: \"propline_get_odds_history\",\n description:\n \"Pro-tier 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 inputSchema: {\n type: \"object\",\n properties: {\n sport_key: { type: \"string\" },\n event_id: { type: [\"string\", \"number\"] },\n markets: { type: \"string\" },\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 { markets: args.markets as string | undefined },\n ),\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_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_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 a completed event. Returns \" +\n \"per-player stats (e.g. strikeouts, hits, points, rebounds, \" +\n \"shots-on-goal) decoupled from any sportsbook's lines. 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_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_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\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 (!apiKey) {\n console.error(\n \"[propline-mcp] note: PROPLINE_API_KEY is not set. Tools are listed \" +\n \"for discovery, but any tool call will error until you configure it. \" +\n \"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 } = {},\n ): Promise<unknown> {\n if (opts.eventId) {\n return this.request(\n `/v1/sports/${sportKey}/events/${opts.eventId}/odds`,\n {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n },\n );\n }\n return this.request(`/v1/sports/${sportKey}/odds`, {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n });\n }\n\n getOddsHistory(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/odds/history`,\n { markets: opts.markets },\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 getResolutionSummary(opts: { days?: number } = {}): Promise<unknown> {\n return this.request(`/v1/markets/resolution-summary`, {\n days: opts.days,\n });\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 // ----- 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 // ----- 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"],"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 * 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.4.0\";\n\nconst apiKey = process.env.PROPLINE_API_KEY;\nconst baseUrl = process.env.PROPLINE_BASE_URL;\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. The key\n// is enforced lazily on the first real tool call instead; tools/list never\n// touches the client.\nlet _client: PropLineClient | null = null;\nfunction client(): PropLineClient {\n if (!_client) {\n if (!apiKey) {\n throw new Error(\n \"PROPLINE_API_KEY is not set. Get a free key at \" +\n \"https://prop-line.com and add it to your MCP client config \" +\n \"(env var: PROPLINE_API_KEY).\",\n );\n }\n _client = new PropLineClient({ apiKey, 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).\",\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 },\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 }),\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 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 },\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 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 },\n ),\n },\n {\n name: \"propline_get_odds_closing\",\n description:\n \"Hobby+ endpoint. Returns the closing line per (book, market, \" +\n \"outcome) for an event — the last snapshot at or before \" +\n \"commence_time. Canonical CLV-tracking helper; one call returns \" +\n \"the data point your bet should be measured against, instead of \" +\n \"fetching full history and post-processing. Each outcome carries \" +\n \"a closing_at field with the snapshot's recorded_at. Free tier \" +\n \"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 },\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 { markets: args.markets as string | undefined },\n ),\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_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_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 a completed event. Returns \" +\n \"per-player stats (e.g. strikeouts, hits, points, rebounds, \" +\n \"shots-on-goal) decoupled from any sportsbook's lines. 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_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_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\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 (!apiKey) {\n console.error(\n \"[propline-mcp] note: PROPLINE_API_KEY is not set. Tools are listed \" +\n \"for discovery, but any tool call will error until you configure it. \" +\n \"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 } = {},\n ): Promise<unknown> {\n if (opts.eventId) {\n return this.request(\n `/v1/sports/${sportKey}/events/${opts.eventId}/odds`,\n {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n },\n );\n }\n return this.request(`/v1/sports/${sportKey}/odds`, {\n markets: opts.markets,\n bookmakers: opts.bookmakers,\n });\n }\n\n getOddsHistory(\n sportKey: string,\n eventId: string | number,\n opts: {\n markets?: string;\n from?: string;\n to?: string;\n relativeFrom?: string;\n relativeTo?: string;\n interval?: string;\n changesOnly?: boolean;\n } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/odds/history`,\n {\n markets: opts.markets,\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 },\n );\n }\n\n getOddsClosing(\n sportKey: string,\n eventId: string | number,\n opts: { markets?: string } = {},\n ): Promise<unknown> {\n return this.request(\n `/v1/sports/${sportKey}/events/${eventId}/odds/closing`,\n { markets: opts.markets },\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 getResolutionSummary(opts: { days?: number } = {}): Promise<unknown> {\n return this.request(`/v1/markets/resolution-summary`, {\n days: opts.days,\n });\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 // ----- 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 // ----- 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"],"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.0",
3
+ "version": "0.4.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": [