propline-mcp 0.25.3 → 0.27.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 +4 -2
- package/dist/http.js +146 -1
- package/dist/http.js.map +1 -1
- package/dist/index.js +146 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ The model uses these tools transparently:
|
|
|
30
30
|
| `propline_get_odds` | Live odds — bulk by sport or full props per event. Accepts `period` (q1/h1/p1/f5/…) to scope to game-period markets. |
|
|
31
31
|
| `propline_get_odds_history` | Hobby+: snapshot history per outcome; supports `period` (q1/h1/…) plus time-window filters (from/to, relative_from/relative_to, interval, changes_only) |
|
|
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
|
+
| `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. |
|
|
33
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). |
|
|
34
35
|
| `propline_get_futures` | Season-long futures — championship/division/conference winners, MVP + awards, season win totals — across Bovada/FanDuel/DraftKings/Pinnacle (free) |
|
|
35
36
|
| `propline_get_scores` | Game scores + status (free) |
|
|
@@ -42,6 +43,7 @@ The model uses these tools transparently:
|
|
|
42
43
|
| `propline_get_event_movement` | Line movement + steam detection (sharp-money signal across all books) from the tick history (Hobby+) |
|
|
43
44
|
| `propline_get_event_results` | Pro: graded won/lost/push per prop |
|
|
44
45
|
| `propline_get_player_history` | Player prop history with resolution |
|
|
46
|
+
| `propline_get_player_games` | Player game log — recent games with every raw box-score stat per game, one call instead of one per event; `opponent` gives head-to-head (last N *meetings*). Raw-stat archive, so it includes games no book priced |
|
|
45
47
|
| `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
48
|
| `propline_get_event_ev` | Pro: cross-book +EV with no-vig fair lines |
|
|
47
49
|
| `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 |
|
|
@@ -50,7 +52,7 @@ The model uses these tools transparently:
|
|
|
50
52
|
|
|
51
53
|
## Hosted endpoint (no install)
|
|
52
54
|
|
|
53
|
-
The same
|
|
55
|
+
The same 25 tools are served over **Streamable HTTP** at
|
|
54
56
|
|
|
55
57
|
```
|
|
56
58
|
https://mcp.prop-line.com/mcp
|
|
@@ -113,7 +115,7 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
113
115
|
}
|
|
114
116
|
```
|
|
115
117
|
|
|
116
|
-
Restart Claude Desktop. The hammer icon should show
|
|
118
|
+
Restart Claude Desktop. The hammer icon should show 25 PropLine tools. (Or skip the install and add `https://mcp.prop-line.com/mcp` as a custom connector — see the hosted endpoint above.)
|
|
117
119
|
|
|
118
120
|
#### Claude Code
|
|
119
121
|
|
package/dist/http.js
CHANGED
|
@@ -69,6 +69,40 @@ var PropLineClient = class {
|
|
|
69
69
|
clearTimeout(timer);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* POST with a JSON body. Kept separate from `request` because every other
|
|
74
|
+
* endpoint on this server is a GET with query params; folding a body into
|
|
75
|
+
* that signature would make the common case harder to read.
|
|
76
|
+
*/
|
|
77
|
+
async postRequest(path, body) {
|
|
78
|
+
const url = new URL(this.baseUrl + path);
|
|
79
|
+
const controller = new AbortController();
|
|
80
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
81
|
+
try {
|
|
82
|
+
const r = await fetch(url, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: {
|
|
85
|
+
"X-API-Key": this.apiKey,
|
|
86
|
+
Accept: "application/json",
|
|
87
|
+
"Content-Type": "application/json",
|
|
88
|
+
"User-Agent": "propline-mcp/0.1.0"
|
|
89
|
+
},
|
|
90
|
+
body: JSON.stringify(body),
|
|
91
|
+
signal: controller.signal
|
|
92
|
+
});
|
|
93
|
+
const text = await r.text();
|
|
94
|
+
if (!r.ok) {
|
|
95
|
+
throw new PropLineHTTPError(r.status, text);
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
return JSON.parse(text);
|
|
99
|
+
} catch {
|
|
100
|
+
return text;
|
|
101
|
+
}
|
|
102
|
+
} finally {
|
|
103
|
+
clearTimeout(timer);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
72
106
|
// ----- Discovery -----
|
|
73
107
|
listSports() {
|
|
74
108
|
return this.request("/v1/sports");
|
|
@@ -120,6 +154,14 @@ var PropLineClient = class {
|
|
|
120
154
|
{ markets: opts.markets, bookmakers: opts.bookmakers, period: opts.period }
|
|
121
155
|
);
|
|
122
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Grade placed bets against their closing lines (CLV). Hobby+.
|
|
159
|
+
* See the propline_grade_clv tool description for the semantics that
|
|
160
|
+
* matter when presenting the result.
|
|
161
|
+
*/
|
|
162
|
+
gradeClv(bets) {
|
|
163
|
+
return this.postRequest("/v1/clv/grade", bets);
|
|
164
|
+
}
|
|
123
165
|
// ----- Bulk exports -----
|
|
124
166
|
/**
|
|
125
167
|
* Full line-movement tick history as CSV text (Backfill pass / Enterprise
|
|
@@ -189,6 +231,13 @@ var PropLineClient = class {
|
|
|
189
231
|
{ limit: opts.limit, markets: opts.markets }
|
|
190
232
|
);
|
|
191
233
|
}
|
|
234
|
+
// ----- Player game log / H2H -----
|
|
235
|
+
getPlayerGames(sportKey, playerName, opts = {}) {
|
|
236
|
+
return this.request(
|
|
237
|
+
`/v1/sports/${sportKey}/players/${encodeURIComponent(playerName)}/games`,
|
|
238
|
+
{ limit: opts.limit, opponent: opts.opponent, stat_type: opts.statType }
|
|
239
|
+
);
|
|
240
|
+
}
|
|
192
241
|
// ----- Player trends -----
|
|
193
242
|
getPlayerTrends(sportKey, playerName, opts = {}) {
|
|
194
243
|
return this.request(
|
|
@@ -228,7 +277,7 @@ var PropLineClient = class {
|
|
|
228
277
|
};
|
|
229
278
|
|
|
230
279
|
// src/server.ts
|
|
231
|
-
var VERSION = "0.
|
|
280
|
+
var VERSION = "0.27.0";
|
|
232
281
|
var DEMO_KEY = "be2b8487fcfacb1fbc292a8aa925a84c";
|
|
233
282
|
var apiKey = process.env.PROPLINE_API_KEY;
|
|
234
283
|
var baseUrl = process.env.PROPLINE_BASE_URL;
|
|
@@ -457,6 +506,64 @@ var tools = [
|
|
|
457
506
|
}
|
|
458
507
|
)
|
|
459
508
|
},
|
|
509
|
+
{
|
|
510
|
+
name: "propline_grade_clv",
|
|
511
|
+
title: "Grade bets against the close (CLV)",
|
|
512
|
+
description: "Hobby+ endpoint. Grades PLACED bets against their closing lines. Closing line value is the only durable proxy for whether a bettor has edge: did the price they took beat the number the market settled on? Send the bets and each comes back with its closing price, the de-vigged closing fair probability, CLV, and \u2014 once the game settles \u2014 the graded resolution and actual stat value, plus a portfolio summary. Stateless: nothing is stored. TWO CLV numbers are returned deliberately. clv_pct is price-vs-price: familiar and quotable, but VIG-BLIND, so it flatters a bet taken on the juicy side of a wide market. ev_vs_close_pct scores the price against the DE-VIGGED close and is the honest one \u2014 report that one when the user asks whether they got value. The de-vig anchors to the SHARPEST book quoting that line at close (fair_source), not the book they bet at, because de-vigging their own book always returns a negative number (they paid its hold). Bets whose event has not started carry closing_is_final=false, are counted in summary.pending, and are EXCLUDED from the summary averages: before kickoff the 'closing' price is just the latest price, so CLV is ~0 by construction \u2014 do not present those as results. Matching is fail-closed: a bet that cannot be pinned to exactly one stored outcome returns matched=false with an unmatched_reason instead of a wrong match, so surface those rows rather than silently dropping them. Max 500 bets per request. Free tier returns structure with every number nulled.",
|
|
513
|
+
inputSchema: {
|
|
514
|
+
type: "object",
|
|
515
|
+
properties: {
|
|
516
|
+
bets: {
|
|
517
|
+
type: "array",
|
|
518
|
+
maxItems: 500,
|
|
519
|
+
description: "Placed bets to grade. selection is the subject: player name for a prop, team name for a game line.",
|
|
520
|
+
items: {
|
|
521
|
+
type: "object",
|
|
522
|
+
properties: {
|
|
523
|
+
ref: {
|
|
524
|
+
type: "string",
|
|
525
|
+
description: "Echoed back untouched, so rows can be aligned without relying on order."
|
|
526
|
+
},
|
|
527
|
+
sport_key: { type: "string" },
|
|
528
|
+
event_id: { type: ["string", "number"] },
|
|
529
|
+
market: { type: "string" },
|
|
530
|
+
bookmaker: { type: "string" },
|
|
531
|
+
selection: { type: "string" },
|
|
532
|
+
side: {
|
|
533
|
+
type: "string",
|
|
534
|
+
description: "'Over' or 'Under' for two-way markets. Omit for YES-only props where the player IS the outcome."
|
|
535
|
+
},
|
|
536
|
+
point: { type: "number" },
|
|
537
|
+
period: {
|
|
538
|
+
type: "string",
|
|
539
|
+
description: "Canonical period code (q1, h1, p1, f5). Omit for full-game markets."
|
|
540
|
+
},
|
|
541
|
+
price: {
|
|
542
|
+
type: "number",
|
|
543
|
+
description: "American odds actually taken, e.g. -110 or 145."
|
|
544
|
+
},
|
|
545
|
+
stake: {
|
|
546
|
+
type: "number",
|
|
547
|
+
description: "Defaults to 1 unit when computing profit_units."
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
required: [
|
|
551
|
+
"sport_key",
|
|
552
|
+
"event_id",
|
|
553
|
+
"market",
|
|
554
|
+
"bookmaker",
|
|
555
|
+
"selection",
|
|
556
|
+
"price"
|
|
557
|
+
],
|
|
558
|
+
additionalProperties: false
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
required: ["bets"],
|
|
563
|
+
additionalProperties: false
|
|
564
|
+
},
|
|
565
|
+
handler: (args) => client().gradeClv(args.bets)
|
|
566
|
+
},
|
|
460
567
|
{
|
|
461
568
|
name: "propline_export_odds_history",
|
|
462
569
|
title: "Export odds history",
|
|
@@ -729,6 +836,44 @@ var tools = [
|
|
|
729
836
|
}
|
|
730
837
|
)
|
|
731
838
|
},
|
|
839
|
+
{
|
|
840
|
+
name: "propline_get_player_games",
|
|
841
|
+
title: "Get player game log / H2H",
|
|
842
|
+
description: "A player's recent games with every raw box-score stat per game \u2014 one call instead of one request per event. Use this to answer 'how has X actually performed lately?' and to build L5/L10/L20, season splits and head-to-head yourself. Pass `opponent` for H2H (accepts a full name, nickname or abbreviation \u2014 'Boston Red Sox', 'Red Sox', 'BOS'); the limit applies AFTER that filter, so opponent + limit=10 means the last 10 MEETINGS, not the Boston games among the last 10 games. H2H is not capped to the current season. IMPORTANT: this is the raw box-score archive, NOT graded-prop history \u2014 it covers every game with a box score on file, including games no sportsbook priced, so a 'last 10 games' window here really is the last 10 games (one built from propline_get_player_trends silently skips unpriced games). It carries no line, price or grade; use propline_get_player_trends for hit rates against a posted line. `player_team`/`opponent`/`is_home` are null when the player's side can't be identified, and always for individual sports (tennis, golf, UFC) \u2014 report them as unknown rather than guessing.",
|
|
843
|
+
inputSchema: {
|
|
844
|
+
type: "object",
|
|
845
|
+
properties: {
|
|
846
|
+
sport_key: { type: "string" },
|
|
847
|
+
player_name: {
|
|
848
|
+
type: "string",
|
|
849
|
+
description: "Player name as it appears in box scores \u2014 e.g. 'Aaron Judge', 'Nikola Jokic'"
|
|
850
|
+
},
|
|
851
|
+
limit: {
|
|
852
|
+
type: "number",
|
|
853
|
+
description: "Games to return, 1-100. Default 20."
|
|
854
|
+
},
|
|
855
|
+
opponent: {
|
|
856
|
+
type: "string",
|
|
857
|
+
description: "Optional head-to-head filter \u2014 team name, nickname or abbreviation."
|
|
858
|
+
},
|
|
859
|
+
stat_type: {
|
|
860
|
+
type: "string",
|
|
861
|
+
description: "Optional comma-separated stat names to return; omit for all. Vocabulary is per-sport."
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
required: ["sport_key", "player_name"],
|
|
865
|
+
additionalProperties: false
|
|
866
|
+
},
|
|
867
|
+
handler: (args) => client().getPlayerGames(
|
|
868
|
+
args.sport_key,
|
|
869
|
+
args.player_name,
|
|
870
|
+
{
|
|
871
|
+
limit: args.limit,
|
|
872
|
+
opponent: args.opponent,
|
|
873
|
+
statType: args.stat_type
|
|
874
|
+
}
|
|
875
|
+
)
|
|
876
|
+
},
|
|
732
877
|
{
|
|
733
878
|
name: "propline_get_player_trends",
|
|
734
879
|
title: "Get player trends",
|