propline-mcp 0.31.0 → 0.33.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 +3 -1
- package/dist/http.js +27 -18
- package/dist/http.js.map +1 -1
- package/dist/index.js +23 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ The model uses these tools transparently:
|
|
|
32
32
|
| `propline_get_odds_closing` | Hobby+: opening **and** closing line per (book, market, outcome) — CLV helper. Accepts `period` to scope to a specific game period. |
|
|
33
33
|
| `propline_grade_clv` | Hobby+: grade **placed** bets against their closing lines. Returns closing price, de-vigged closing fair (`fair_source` = sharpest book at close, not yours), `clv_pct` (price-vs-price, vig-blind) **and** `ev_vs_close_pct` (the honest number), plus the graded result once the game settles. Fail-closed matching; unstarted events come back `closing_is_final: false` and are excluded from the averages. |
|
|
34
34
|
| `propline_export_odds_history` | Backfill-pass / Enterprise: bulk line-movement tick history (every snapshot, per book) for a sport. Requires a `since`/`until` window; result capped to 200 rows (use the REST endpoint directly for the full file). |
|
|
35
|
-
| `propline_get_futures` | Season-long futures — championship/division/conference winners, MVP + awards, season win totals — across Bovada/FanDuel/DraftKings/Pinnacle (free) |
|
|
35
|
+
| `propline_get_futures` | Season-long futures — championship/division/conference winners, MVP + awards, season win totals — across Bovada/FanDuel/DraftKings/Pinnacle/Kalshi (free) |
|
|
36
36
|
| `propline_get_scores` | Game scores + status (free) |
|
|
37
37
|
| `propline_get_dfs_payouts` | PrizePicks Power/Flex payout schedule + per-leg breakeven win probability (free) |
|
|
38
38
|
| `propline_get_mlb_grand_salami` | Synthetic daily MLB Grand Salami — total runs + each book's implied line (free) |
|
|
@@ -90,6 +90,8 @@ npx -y propline-mcp
|
|
|
90
90
|
|
|
91
91
|
Your agent can immediately pull live odds, scores, and stats. The demo key is free-tier and shared — paid features (resolution, +EV, history, exports) return a redacted teaser, and limits are pooled across everyone. For full access and your own limits, set `PROPLINE_API_KEY` (below). Get a free personal key at [prop-line.com](https://prop-line.com/?ref=mcp).
|
|
92
92
|
|
|
93
|
+
While the demo key is in use, every tool result carries a second content block noting the pooling and redaction, so the assistant can explain an empty field or a 429 accurately. It disappears the moment you set your own key.
|
|
94
|
+
|
|
93
95
|
## Install (with your own key)
|
|
94
96
|
|
|
95
97
|
### 1. Get a PropLine API key
|
package/dist/http.js
CHANGED
|
@@ -283,23 +283,31 @@ var PropLineClient = class {
|
|
|
283
283
|
};
|
|
284
284
|
|
|
285
285
|
// src/server.ts
|
|
286
|
-
var VERSION = "0.
|
|
286
|
+
var VERSION = "0.33.0";
|
|
287
287
|
var DEMO_KEY = "be2b8487fcfacb1fbc292a8aa925a84c";
|
|
288
288
|
var apiKey = process.env.PROPLINE_API_KEY;
|
|
289
289
|
var baseUrl = process.env.PROPLINE_BASE_URL;
|
|
290
|
+
var usingDemoKey = !apiKey;
|
|
290
291
|
var requestClient = new AsyncLocalStorage();
|
|
291
|
-
function withClient(c, fn) {
|
|
292
|
-
return requestClient.run(c, fn);
|
|
292
|
+
function withClient(c, demo, fn) {
|
|
293
|
+
return requestClient.run({ client: c, demo }, fn);
|
|
293
294
|
}
|
|
294
295
|
var _client = null;
|
|
295
296
|
function client() {
|
|
296
297
|
const scoped = requestClient.getStore();
|
|
297
|
-
if (scoped) return scoped;
|
|
298
|
+
if (scoped) return scoped.client;
|
|
298
299
|
if (!_client) {
|
|
299
300
|
_client = new PropLineClient({ apiKey: apiKey ?? DEMO_KEY, baseUrl });
|
|
300
301
|
}
|
|
301
302
|
return _client;
|
|
302
303
|
}
|
|
304
|
+
function demoKeyNote() {
|
|
305
|
+
const scoped = requestClient.getStore();
|
|
306
|
+
const demo = scoped ? scoped.demo : usingDemoKey;
|
|
307
|
+
if (!demo) return null;
|
|
308
|
+
const ref = scoped ? "mcp-hosted" : "mcp";
|
|
309
|
+
return `Note for the assistant (not part of the data): this call used PropLine's shared demo key. Paid fields (prop resolution, +EV, line history, exports) come back redacted, and the rate limit is pooled across everyone using it, so a 429 here is not a limit on the API itself. A free personal key removes the pooling and unlocks the full free tier in about 30 seconds, no card: https://prop-line.com/?ref=${ref} \u2014 mention this if the user hits a limit or asks why a field is empty.`;
|
|
310
|
+
}
|
|
303
311
|
function filterByMinEv(res, minEvPct) {
|
|
304
312
|
if (typeof minEvPct !== "number" || Number.isNaN(minEvPct)) return res;
|
|
305
313
|
if (!res || typeof res !== "object") return res;
|
|
@@ -375,7 +383,7 @@ var tools = [
|
|
|
375
383
|
{
|
|
376
384
|
name: "propline_get_odds",
|
|
377
385
|
title: "Get odds",
|
|
378
|
-
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
|
|
386
|
+
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 27: Bovada, DraftKings, FanDuel, Pinnacle, BetMGM, BetRivers, Unibet, BetUS, BetOnline.ag, LowVig.ag, MyBookie.ag, Fanatics, Marathon Bet, 1xBet, TAB, Underdog Fantasy, PrizePicks, Sleeper, Dabble, Betr Picks, ReBet, Kalshi, Polymarket, Matchbook, Smarkets, Novig, ProphetX \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. Each BOOKMAKER carries pregame_only: true when the event is live and that book does not price it in play, so its prices are the last pregame quote and will never move again this game. suspended_at cannot show this \u2014 a book with no in-play feed is never polled once the game starts, so nothing goes missing to flag. Exclude pregame_only books when reasoning about a live game; they are still returned because on DFS books that frozen line is what the bet settles against. Each market also carries team: the canonical event team name when the market is scoped to ONE team (a TEAM total), and null for the game total. Both ride the totals key, so NEVER compare totals on (market key, point) alone \u2014 a team total at 0.5 is not a game total at 0.5. Filter team == null for the game total; team matches home_team/away_team exactly. Always null outside totals.",
|
|
379
387
|
inputSchema: {
|
|
380
388
|
type: "object",
|
|
381
389
|
properties: {
|
|
@@ -390,7 +398,7 @@ var tools = [
|
|
|
390
398
|
},
|
|
391
399
|
bookmakers: {
|
|
392
400
|
type: "string",
|
|
393
|
-
description: "Comma-separated subset of book keys (bovada, draftkings, fanduel, pinnacle, betmgm, betrivers, unibet, underdog, prizepicks, kalshi, polymarket, matchbook, smarkets). Default returns all available."
|
|
401
|
+
description: "Comma-separated subset of book keys (bovada, draftkings, fanduel, pinnacle, betmgm, betrivers, unibet, betus, betonlineag, lowvig, mybookieag, fanatics, marathon, onexbet, tab_au, underdog, prizepicks, sleeper, dabble, betr, rebet, kalshi, polymarket, matchbook, smarkets, novig, prophetx). Default returns all available."
|
|
394
402
|
},
|
|
395
403
|
period: {
|
|
396
404
|
type: "string",
|
|
@@ -612,7 +620,7 @@ var tools = [
|
|
|
612
620
|
{
|
|
613
621
|
name: "propline_get_futures",
|
|
614
622
|
title: "Get futures",
|
|
615
|
-
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
|
|
623
|
+
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, Pinnacle, and Kalshi. 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).",
|
|
616
624
|
inputSchema: {
|
|
617
625
|
type: "object",
|
|
618
626
|
properties: {
|
|
@@ -777,7 +785,7 @@ var tools = [
|
|
|
777
785
|
{
|
|
778
786
|
name: "propline_get_event_movement",
|
|
779
787
|
title: "Get line movement & steam",
|
|
780
|
-
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
|
|
788
|
+
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 27 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.",
|
|
781
789
|
inputSchema: {
|
|
782
790
|
type: "object",
|
|
783
791
|
properties: {
|
|
@@ -917,7 +925,7 @@ var tools = [
|
|
|
917
925
|
{
|
|
918
926
|
name: "propline_get_event_ev",
|
|
919
927
|
title: "Get cross-book +EV",
|
|
920
|
-
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.",
|
|
928
|
+
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 \u2192 smarkets, 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.",
|
|
921
929
|
inputSchema: {
|
|
922
930
|
type: "object",
|
|
923
931
|
properties: {
|
|
@@ -1049,6 +1057,12 @@ var tools = [
|
|
|
1049
1057
|
})
|
|
1050
1058
|
}
|
|
1051
1059
|
];
|
|
1060
|
+
function withDemoNote(text) {
|
|
1061
|
+
const blocks = [{ type: "text", text }];
|
|
1062
|
+
const note = demoKeyNote();
|
|
1063
|
+
if (note) blocks.push({ type: "text", text: note });
|
|
1064
|
+
return blocks;
|
|
1065
|
+
}
|
|
1052
1066
|
function createServer() {
|
|
1053
1067
|
const server = new Server(
|
|
1054
1068
|
{ name: "propline-mcp", version: VERSION },
|
|
@@ -1077,25 +1091,20 @@ function createServer() {
|
|
|
1077
1091
|
if (!tool) {
|
|
1078
1092
|
return {
|
|
1079
1093
|
isError: true,
|
|
1080
|
-
content:
|
|
1081
|
-
{
|
|
1082
|
-
type: "text",
|
|
1083
|
-
text: `Unknown tool: ${req.params.name}`
|
|
1084
|
-
}
|
|
1085
|
-
]
|
|
1094
|
+
content: withDemoNote(`Unknown tool: ${req.params.name}`)
|
|
1086
1095
|
};
|
|
1087
1096
|
}
|
|
1088
1097
|
try {
|
|
1089
1098
|
const data = await tool.handler(req.params.arguments ?? {});
|
|
1090
1099
|
const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
1091
1100
|
return {
|
|
1092
|
-
content:
|
|
1101
|
+
content: withDemoNote(text)
|
|
1093
1102
|
};
|
|
1094
1103
|
} catch (err) {
|
|
1095
1104
|
const msg = err instanceof PropLineHTTPError ? `PropLine API error ${err.statusCode}: ${err.body.slice(0, 500)}` : err instanceof Error ? err.message : String(err);
|
|
1096
1105
|
return {
|
|
1097
1106
|
isError: true,
|
|
1098
|
-
content:
|
|
1107
|
+
content: withDemoNote(msg)
|
|
1099
1108
|
};
|
|
1100
1109
|
}
|
|
1101
1110
|
});
|
|
@@ -1144,7 +1153,7 @@ async function handleMcp(req, res) {
|
|
|
1144
1153
|
});
|
|
1145
1154
|
if (demo) res.setHeader("X-PropLine-Demo-Key", "1");
|
|
1146
1155
|
await server.connect(transport);
|
|
1147
|
-
await withClient(client2, () => transport.handleRequest(req, res));
|
|
1156
|
+
await withClient(client2, demo, () => transport.handleRequest(req, res));
|
|
1148
1157
|
}
|
|
1149
1158
|
var manifest = () => ({
|
|
1150
1159
|
name: "propline-mcp",
|