propline-mcp 0.23.1 → 0.25.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
@@ -45,10 +45,12 @@ The model uses these tools transparently:
45
45
  | `propline_get_player_trends` | Hit-rate trends — over/under/push splits over last 5/10/20/50 graded games, streak, avg actual (optional `dfs_odds_type` to scope to a PrizePicks flavor) |
46
46
  | `propline_get_event_ev` | Pro: cross-book +EV with no-vig fair lines |
47
47
  | `propline_get_best_line` | Hobby+: cross-book line shopping — best price per (market, player, line) across all comparable books, `all_prices` sorted best-first; optional `bookmakers` filter |
48
+ | `propline_list_webhooks` | Streaming Lite+: list webhook subscriptions (read-only, secrets masked) |
49
+ | `propline_get_webhook_deliveries` | Streaming Lite+: recent delivery attempts for a webhook — status, HTTP code, attempts, payload; `before_id` pages backwards. The "why isn't my webhook firing" tool |
48
50
 
49
51
  ## Hosted endpoint (no install)
50
52
 
51
- The same 21 tools are served over **Streamable HTTP** at
53
+ The same 23 tools are served over **Streamable HTTP** at
52
54
 
53
55
  ```
54
56
  https://mcp.prop-line.com/mcp
@@ -145,6 +147,7 @@ Pricing: free at 1,000 req/day (vs their 500/month), Pro at $19/mo for 25,000 re
145
147
  ## Links
146
148
 
147
149
  - **Hosted MCP endpoint**: `https://mcp.prop-line.com/mcp` (Streamable HTTP)
150
+ - **Privacy policy**: [prop-line.com/privacy](https://prop-line.com/privacy)
148
151
  - **Website**: [prop-line.com](https://prop-line.com/?ref=mcp)
149
152
  - **API Docs**: [prop-line.com/docs](https://prop-line.com/docs?ref=mcp)
150
153
  - **Recipes** (code for common jobs): [prop-line.com/recipes](https://prop-line.com/recipes?ref=mcp)
package/dist/http.js CHANGED
@@ -210,10 +210,25 @@ var PropLineClient = class {
210
210
  includeLinks: opts.includeLinks ? "true" : void 0
211
211
  });
212
212
  }
213
+ // ----- Webhooks (READ-ONLY on purpose) -----
214
+ // Create is excluded because it returns the HMAC signing secret exactly
215
+ // once — through MCP that lands in a model context window and whatever
216
+ // logs it. Update/delete/test are excluded as side-effectful. The read
217
+ // surface (list shows the secret MASKED; deliveries is the debugging
218
+ // log) is what an agent needs to answer "why isn't my webhook firing".
219
+ listWebhooks() {
220
+ return this.request("/v1/webhooks");
221
+ }
222
+ listWebhookDeliveries(webhookId, opts = {}) {
223
+ return this.request(`/v1/webhooks/${webhookId}/deliveries`, {
224
+ limit: opts.limit,
225
+ before_id: opts.beforeId
226
+ });
227
+ }
213
228
  };
214
229
 
215
230
  // src/server.ts
216
- var VERSION = "0.23.1";
231
+ var VERSION = "0.25.0";
217
232
  var DEMO_KEY = "be2b8487fcfacb1fbc292a8aa925a84c";
218
233
  var apiKey = process.env.PROPLINE_API_KEY;
219
234
  var baseUrl = process.env.PROPLINE_BASE_URL;
@@ -252,6 +267,7 @@ function filterByMinEv(res, minEvPct) {
252
267
  var tools = [
253
268
  {
254
269
  name: "propline_list_sports",
270
+ title: "List sports",
255
271
  description: "List all sports PropLine currently polls. Returns sport keys (e.g. baseball_mlb, basketball_nba, soccer_epl) along with human titles and active status. Use this first to discover what sport_key values are valid for the other tools.",
256
272
  inputSchema: {
257
273
  type: "object",
@@ -262,6 +278,7 @@ var tools = [
262
278
  },
263
279
  {
264
280
  name: "propline_list_events",
281
+ title: "List events",
265
282
  description: "List upcoming events for a sport. Returns each event's id, home_team, away_team, commence_time. Use the returned event_id to drill into per-event odds, props, +EV, or results.",
266
283
  inputSchema: {
267
284
  type: "object",
@@ -284,6 +301,7 @@ var tools = [
284
301
  },
285
302
  {
286
303
  name: "propline_list_event_markets",
304
+ title: "List event markets",
287
305
  description: "List the market types available for a specific event (e.g. h2h, spreads, totals, player_points, pitcher_strikeouts). Useful when you don't know which prop markets a given event carries.",
288
306
  inputSchema: {
289
307
  type: "object",
@@ -301,6 +319,7 @@ var tools = [
301
319
  },
302
320
  {
303
321
  name: "propline_get_odds",
322
+ title: "Get odds",
304
323
  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. Each market carries suspended_at: null while on the board, set when that book pulled the market pregame (late scratch, dropped market type) \u2014 its outcomes are then the last quoted legs, not a live price. Treat a suspended market as unbettable and, if several books show it for one player, as a probable scratch.",
305
324
  inputSchema: {
306
325
  type: "object",
@@ -345,6 +364,7 @@ var tools = [
345
364
  },
346
365
  {
347
366
  name: "propline_get_odds_history",
367
+ title: "Get odds history",
348
368
  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.",
349
369
  inputSchema: {
350
370
  type: "object",
@@ -407,6 +427,7 @@ var tools = [
407
427
  },
408
428
  {
409
429
  name: "propline_get_odds_closing",
430
+ title: "Get opening & closing lines",
410
431
  description: "Hobby+ endpoint. Returns the OPENING and CLOSING line per (book, market, outcome) for an event. Closing = the last snapshot at or before commence_time (price/point/closing_at); opening = the first snapshot in the same 14-day pre-kickoff window (opening_price/opening_point/opening_at). Canonical CLV-tracking helper; one call returns both data points your bet should be measured against, instead of fetching full history and post-processing. Compare the POINTS as well as the prices \u2014 on spreads and totals the number moves as much as the price, so a price-only comparison mis-measures those markets. opening_age_seconds says how long before kickoff the opener was recorded: the archive starts 2026-04, so a small value means PropLine started polling late and this is not the book's true open. Free tier returns redacted structure with upgrade pointer.",
411
432
  inputSchema: {
412
433
  type: "object",
@@ -438,6 +459,7 @@ var tools = [
438
459
  },
439
460
  {
440
461
  name: "propline_export_odds_history",
462
+ title: "Export odds history",
441
463
  description: "Backfill-pass / Enterprise only. Bulk line-movement tick history as CSV \u2014 every recorded odds snapshot (price + line, per book, including period markets) across a whole sport, one row per (outcome, snapshot). This is the raw firehose no subscription tier can bulk-pull (Pro/Streaming use propline_get_odds_history per event instead). REQUIRES a since/until window to keep the pull bounded \u2014 the full archive runs to gigabytes per sport. The result is capped to the first 200 rows for context safety; for the full dataset use the /v1/exports/odds-history endpoint directly with curl/SDK and stream to disk. Non-entitled keys get a 403 with an upgrade pointer.",
442
464
  inputSchema: {
443
465
  type: "object",
@@ -476,6 +498,7 @@ var tools = [
476
498
  },
477
499
  {
478
500
  name: "propline_get_futures",
501
+ title: "Get futures",
479
502
  description: "Free-tier endpoint. Returns season-long futures (outright) markets for a sport \u2014 championship/Super Bowl/division/conference winners, MVP and award winners, season win totals \u2014 aggregated across Bovada, FanDuel, DraftKings, and Pinnacle. One row per (futures event, book, market) with each team/player outcome and its price. Marquee markets (Super Bowl winner, MVP, division/conference) are quoted by multiple books for comparison; exotic markets are often single-book. Useful for: 'who are the Super Bowl favorites across books', 'NFL MVP odds', 'NBA championship futures'. Futures are unresolved (no settlement grade).",
480
503
  inputSchema: {
481
504
  type: "object",
@@ -492,6 +515,7 @@ var tools = [
492
515
  },
493
516
  {
494
517
  name: "propline_get_scores",
518
+ title: "Get scores",
495
519
  description: "Free-tier endpoint. Returns recent and live game scores plus status (scheduled, live, final) for a sport. Useful for: 'is this game over yet, what was the final score'.",
496
520
  inputSchema: {
497
521
  type: "object",
@@ -511,6 +535,7 @@ var tools = [
511
535
  },
512
536
  {
513
537
  name: "propline_get_dfs_payouts",
538
+ title: "Get DFS payouts",
514
539
  description: "Free-tier reference math. Returns the PrizePicks Power Play (all legs must hit) and Flex Play (partial payouts) entry payout schedule for 2-6 legs, plus the per-leg breakeven win probability for each play. Pass leg_win_prob (e.g. 0.58) to also get expected_return (per $1) and is_plus_ev per play \u2014 the slip-level breakeven. Useful for: 'what hit rate do I need to beat a 4-pick PrizePicks Power play', 'is a 3-leg flex +EV at 60% per leg'. NOTE: standard published payouts only \u2014 demon/goblin per-pick modifiers aren't in PrizePicks's feed (see the disclaimer field); breakeven assumes independent legs.",
515
540
  inputSchema: {
516
541
  type: "object",
@@ -529,6 +554,7 @@ var tools = [
529
554
  },
530
555
  {
531
556
  name: "propline_get_mlb_grand_salami",
557
+ title: "Get MLB Grand Salami",
532
558
  description: "Free-tier endpoint. Returns the synthetic daily MLB Grand Salami for a given UTC date \u2014 total runs scored across every MLB game on the slate plus each book's implied Grand Salami line (median of per-game primary totals across our MLB books incl. Pinnacle, Polymarket, Matchbook, Smarkets). No retail sportsbook quotes this as a single market. Useful for: 'what's the total run line for tonight's full MLB slate', 'did the Grand Salami go over yesterday', 'historical Grand Salami results for backtesting'.",
533
559
  inputSchema: {
534
560
  type: "object",
@@ -547,6 +573,7 @@ var tools = [
547
573
  },
548
574
  {
549
575
  name: "propline_get_nhl_daily_goals_total",
576
+ title: "Get NHL daily goals total",
550
577
  description: "Free-tier endpoint. Returns the synthetic daily NHL goals total (hockey's equivalent of the MLB Grand Salami) for a given UTC date \u2014 total goals scored across every NHL game on the slate (including OT/SO) plus each book's implied Daily Goals Total line (median of per-game primary totals across our NHL books). No retail sportsbook quotes this as a single market. Useful for: 'what's the total goal line for tonight's full NHL slate', 'did the Daily Goals Total go over yesterday', 'historical NHL daily-goals results for backtesting'.",
551
578
  inputSchema: {
552
579
  type: "object",
@@ -565,6 +592,7 @@ var tools = [
565
592
  },
566
593
  {
567
594
  name: "propline_get_resolution_summary",
595
+ title: "Get resolution summary",
568
596
  description: "Free-tier endpoint. Returns the factual volume of player props PropLine has graded against real box scores over the last N days (aggregated counts only): total graded/settled, games, sports covered, plus per-sport and top-market breakdowns. Useful for: 'how much graded prop data does PropLine have, what's the coverage'. A coverage proof, never a profitability claim.",
569
597
  inputSchema: {
570
598
  type: "object",
@@ -581,6 +609,7 @@ var tools = [
581
609
  },
582
610
  {
583
611
  name: "propline_get_event_stats",
612
+ title: "Get event stats",
584
613
  description: "Book-agnostic raw box-score stats for an event. Returns per-player stats (e.g. strikeouts, hits, points, rebounds, shots-on-goal) decoupled from any sportsbook's lines. LIVE during games for major US sports (MLB + WNBA now; NFL, NCAAF, NBA, NHL at season start): while the event's status is in_progress, stats refresh ~every 90 seconds with cumulative in-game values \u2014 use this to answer 'how is this prop tracking right now'. Treat in-progress numbers as partial; at status=final they are the official box score. Other sports populate stats at game completion. Free tier.",
585
614
  inputSchema: {
586
615
  type: "object",
@@ -598,6 +627,7 @@ var tools = [
598
627
  },
599
628
  {
600
629
  name: "propline_get_event_results",
630
+ title: "Get graded prop results",
601
631
  description: "Pro-tier endpoint. Returns graded prop outcomes for a completed event \u2014 every Over/Under marked won, lost, push, or void with the actual stat value next to the line. The single most distinctive feature vs the-odds-api: they don't grade props at any tier. Free tier returns the same structure with resolution and actual_value redacted plus an upgrade pointer.",
602
632
  inputSchema: {
603
633
  type: "object",
@@ -615,6 +645,7 @@ var tools = [
615
645
  },
616
646
  {
617
647
  name: "propline_get_event_context",
648
+ title: "Get event context",
618
649
  description: "Game context for an event \u2014 the conditions a prop settles under. MLB: probable starting pitchers and their throwing hand (L/R/S \u2014 platoon-split context for every batter prop), a confirmed-lineup flag, the home-plate umpire, and first-pitch weather (temperature, wind, precipitation) at outdoor / open-roof venues (indoor venues return weather=null). NFL & NCAAF: the venue and kickoff weather. The same block is embedded in get_event_results, so every graded prop carries its conditions \u2014 unique to PropLine. Free tier. 404 when no context is on file for the event yet.",
619
650
  inputSchema: {
620
651
  type: "object",
@@ -632,6 +663,7 @@ var tools = [
632
663
  },
633
664
  {
634
665
  name: "propline_get_event_movement",
666
+ title: "Get line movement & steam",
635
667
  description: "Line movement + steam detection from the snapshot tick history. Per (book, market, outcome): opening line, latest line, signed implied-probability shift, point shift, direction. The steam[] array flags outcomes that multiple books moved the same direction \u2014 the classic sharp-money signal, computed across all 16 books PropLine polls. When a book moves the line itself, that outcome's prob_shift is null and direction is 'line_moved' (excluded from the steam signal). No pull-only odds API can produce this. Hobby+ full; free tier redacted.",
636
668
  inputSchema: {
637
669
  type: "object",
@@ -666,6 +698,7 @@ var tools = [
666
698
  },
667
699
  {
668
700
  name: "propline_get_player_history",
701
+ title: "Get player prop history",
669
702
  description: "Player prop history across recent games. Returns each prior prop this player took with line, prices, resolution, and actual value. Pro tier returns full data; free tier returns redacted resolution/actual_value with an upgrade pointer.",
670
703
  inputSchema: {
671
704
  type: "object",
@@ -698,6 +731,7 @@ var tools = [
698
731
  },
699
732
  {
700
733
  name: "propline_get_player_trends",
734
+ title: "Get player trends",
701
735
  description: "Hit-rate trends / last-N-games over rate for a player \u2014 unique to PropLine's prop-resolution data. For each market the player has graded history in, returns over/under/push splits across the last 5/10/20/50 graded games, current streak, average actual stat, and the recent line. This is the 'did X go over in N of his last M games?' surface. Omit `market` for all markets, or pass one to scope (e.g. 'player_points', 'batter_hits').",
702
736
  inputSchema: {
703
737
  type: "object",
@@ -731,6 +765,7 @@ var tools = [
731
765
  },
732
766
  {
733
767
  name: "propline_get_event_ev",
768
+ title: "Get cross-book +EV",
734
769
  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.",
735
770
  inputSchema: {
736
771
  type: "object",
@@ -764,6 +799,7 @@ var tools = [
764
799
  },
765
800
  {
766
801
  name: "propline_get_best_line",
802
+ title: "Get best line",
767
803
  description: "Cross-book line shopping (Hobby+ for prices; free tier gets the full structure with book identities + best-first ranking but prices nulled and redacted:true). For every (market, player, line) tuple on an event, returns the single best American price across all comparable books, plus an all_prices array sorted best-first (one row per book, each with last_update). Companion to propline_get_event_ev: /ev says whether a price beats the no-vig fair line; best-line says which book pays the most. DFS pick'em books (PrizePicks, Sleeper, Dabble) are excluded; Underdog only at clean two-way lines. Optional bookmakers filter to shop only the books the user holds accounts at.",
768
804
  inputSchema: {
769
805
  type: "object",
@@ -795,6 +831,48 @@ var tools = [
795
831
  includeLinks: args.include_links
796
832
  }
797
833
  )
834
+ },
835
+ // -- Webhooks: READ-ONLY on purpose. Create returns the HMAC secret exactly
836
+ // once (a credential in a model context); update/delete/test are
837
+ // side-effectful. The read pair is the self-service debugging surface.
838
+ {
839
+ name: "propline_list_webhooks",
840
+ title: "List webhooks",
841
+ description: "List the API key's webhook subscriptions (Streaming Lite tier and up; other tiers get a 403 with an upgrade URL). Read-only: signing secrets are always masked, and this server deliberately has no create/update/delete tools \u2014 manage subscriptions via the REST API or SDKs. Each row shows url, subscribed events (line_movement, resolution, steam, market_suspended), filters and active status. Use this first to find the webhook id for propline_get_webhook_deliveries.",
842
+ inputSchema: {
843
+ type: "object",
844
+ properties: {},
845
+ additionalProperties: false
846
+ },
847
+ handler: () => client().listWebhooks()
848
+ },
849
+ {
850
+ name: "propline_get_webhook_deliveries",
851
+ title: "Get webhook deliveries",
852
+ description: "Recent delivery attempts for one webhook (Streaming Lite tier and up), newest first \u2014 the debugging surface for 'why isn't my webhook firing'. Each row: status (pending/success/failed), HTTP response_code, attempts, delivered_at and the payload that was sent. A pending row with attempts > 0 is mid-retry-backoff; status 'failed' with response_code null means the endpoint was unreachable or timed out (8s). Page backwards through a deep queue with before_id = the smallest id on the previous page; a page shorter than limit is the last one.",
853
+ inputSchema: {
854
+ type: "object",
855
+ properties: {
856
+ webhook_id: {
857
+ type: "number",
858
+ description: "Webhook id (from propline_list_webhooks)."
859
+ },
860
+ limit: {
861
+ type: "number",
862
+ description: "Rows per page, 1-200. Default 50."
863
+ },
864
+ before_id: {
865
+ type: "number",
866
+ description: "Cursor: smallest delivery id from the previous page."
867
+ }
868
+ },
869
+ required: ["webhook_id"],
870
+ additionalProperties: false
871
+ },
872
+ handler: (args) => client().listWebhookDeliveries(args.webhook_id, {
873
+ limit: args.limit,
874
+ beforeId: args.before_id
875
+ })
798
876
  }
799
877
  ];
800
878
  function createServer() {
@@ -805,8 +883,19 @@ function createServer() {
805
883
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
806
884
  tools: tools.map((t) => ({
807
885
  name: t.name,
886
+ title: t.title,
808
887
  description: t.description,
809
- inputSchema: t.inputSchema
888
+ inputSchema: t.inputSchema,
889
+ // Every PropLine tool is a READ of the odds API — none creates,
890
+ // changes or deletes anything. Directories (Claude connectors, Cursor)
891
+ // require these hints; clients use them to skip confirmation prompts.
892
+ annotations: {
893
+ title: t.title,
894
+ readOnlyHint: true,
895
+ destructiveHint: false,
896
+ idempotentHint: true,
897
+ openWorldHint: true
898
+ }
810
899
  }))
811
900
  }));
812
901
  server.setRequestHandler(CallToolRequestSchema, async (req) => {