propline-mcp 0.33.0 → 0.35.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 -2
- package/dist/http.js +87 -1
- package/dist/http.js.map +1 -1
- package/dist/index.js +87 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@ The model uses these tools transparently:
|
|
|
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
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
|
+
| `propline_price_sgp` | Hobby+: price a same-game parlay at the **book's own** correlated odds (FanDuel today). Returns `sgp_price`, `independent_price` (product of the live single-leg prices) and `correlation_factor` (their ratio). Legs are named exactly as `/odds` names an outcome; matching is fail-closed. |
|
|
34
35
|
| `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
36
|
| `propline_get_futures` | Season-long futures — championship/division/conference winners, MVP + awards, season win totals — across Bovada/FanDuel/DraftKings/Pinnacle/Kalshi (free) |
|
|
36
37
|
| `propline_get_scores` | Game scores + status (free) |
|
|
@@ -53,7 +54,7 @@ The model uses these tools transparently:
|
|
|
53
54
|
|
|
54
55
|
## Hosted endpoint (no install)
|
|
55
56
|
|
|
56
|
-
The same
|
|
57
|
+
The same 27 tools are served over **Streamable HTTP** at
|
|
57
58
|
|
|
58
59
|
```
|
|
59
60
|
https://mcp.prop-line.com/mcp
|
|
@@ -118,7 +119,7 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
118
119
|
}
|
|
119
120
|
```
|
|
120
121
|
|
|
121
|
-
Restart Claude Desktop. The hammer icon should show
|
|
122
|
+
Restart Claude Desktop. The hammer icon should show 27 PropLine tools. (Or skip the install and add `https://mcp.prop-line.com/mcp` as a custom connector — see the hosted endpoint above.)
|
|
122
123
|
|
|
123
124
|
#### Claude Code
|
|
124
125
|
|
package/dist/http.js
CHANGED
|
@@ -162,6 +162,16 @@ var PropLineClient = class {
|
|
|
162
162
|
gradeClv(bets) {
|
|
163
163
|
return this.postRequest("/v1/clv/grade", bets);
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Price a same-game parlay at the book's own correlated odds (FanDuel
|
|
167
|
+
* today). Hobby+. See the propline_price_sgp tool description.
|
|
168
|
+
*/
|
|
169
|
+
priceSgp(sportKey, eventId, legs, bookmaker = "fanduel") {
|
|
170
|
+
return this.postRequest(
|
|
171
|
+
`/v1/sports/${encodeURIComponent(sportKey)}/events/${encodeURIComponent(String(eventId))}/sgp`,
|
|
172
|
+
{ bookmaker, legs }
|
|
173
|
+
);
|
|
174
|
+
}
|
|
165
175
|
// ----- Bulk exports -----
|
|
166
176
|
/**
|
|
167
177
|
* Full line-movement tick history as CSV text (Backfill pass / Enterprise
|
|
@@ -280,10 +290,16 @@ var PropLineClient = class {
|
|
|
280
290
|
before_id: opts.beforeId
|
|
281
291
|
});
|
|
282
292
|
}
|
|
293
|
+
replayWebhookEvents(webhookId, opts = {}) {
|
|
294
|
+
return this.request(`/v1/webhooks/${webhookId}/replay`, {
|
|
295
|
+
since_seq: opts.sinceSeq,
|
|
296
|
+
limit: opts.limit
|
|
297
|
+
});
|
|
298
|
+
}
|
|
283
299
|
};
|
|
284
300
|
|
|
285
301
|
// src/server.ts
|
|
286
|
-
var VERSION = "0.
|
|
302
|
+
var VERSION = "0.35.0";
|
|
287
303
|
var DEMO_KEY = "be2b8487fcfacb1fbc292a8aa925a84c";
|
|
288
304
|
var apiKey = process.env.PROPLINE_API_KEY;
|
|
289
305
|
var baseUrl = process.env.PROPLINE_BASE_URL;
|
|
@@ -578,6 +594,48 @@ var tools = [
|
|
|
578
594
|
},
|
|
579
595
|
handler: (args) => client().gradeClv(args.bets)
|
|
580
596
|
},
|
|
597
|
+
{
|
|
598
|
+
name: "propline_price_sgp",
|
|
599
|
+
title: "Price a same-game parlay at the book's own odds",
|
|
600
|
+
description: "Hobby+ endpoint. Prices a same-game parlay at the BOOK'S OWN correlated odds \u2014 the price a FanDuel customer would be offered for that exact slip right now, not a model of it. FanDuel is the only book today (bookmaker defaults to fanduel). Send 2-10 legs from ONE event, each named exactly as propline_get_odds names an outcome: market key, name (team / Over / Under / player for YES-only props), description (the player on a two-way prop, '' for game lines), point (omit for h2h and YES-only props) and period (omit for full game) \u2014 or book_outcome_id from includeBookIds. The response carries sgp_price (the book's parlay price), independent_price (the product of the live single-leg prices) and correlation_factor = their ratio: below 1 the book is charging for correlation, above 1 it is paying for anti-correlation \u2014 say which when presenting it. Matching is fail-closed: a leg that does not pin to exactly one stored outcome is a 422 leg_unmatched naming the leg (an Over with no point on an event with two total lines is refused, not guessed) \u2014 fix the leg, do not retry blindly. quoted=false means the book will not offer that combination as an SGP; refused legs carry the book's own failure_code. Free tier returns the matched legs with every price nulled.",
|
|
601
|
+
inputSchema: {
|
|
602
|
+
type: "object",
|
|
603
|
+
properties: {
|
|
604
|
+
sport_key: { type: "string", description: "e.g. baseball_mlb" },
|
|
605
|
+
event_id: { type: ["string", "number"] },
|
|
606
|
+
bookmaker: {
|
|
607
|
+
type: "string",
|
|
608
|
+
description: "Book to price at. Only fanduel today."
|
|
609
|
+
},
|
|
610
|
+
legs: {
|
|
611
|
+
type: "array",
|
|
612
|
+
minItems: 2,
|
|
613
|
+
maxItems: 10,
|
|
614
|
+
description: "Legs named exactly as /odds names an outcome.",
|
|
615
|
+
items: {
|
|
616
|
+
type: "object",
|
|
617
|
+
properties: {
|
|
618
|
+
market: { type: "string" },
|
|
619
|
+
name: { type: "string" },
|
|
620
|
+
description: { type: "string" },
|
|
621
|
+
point: { type: ["number", "null"] },
|
|
622
|
+
period: { type: ["string", "null"] },
|
|
623
|
+
book_outcome_id: { type: ["string", "null"] }
|
|
624
|
+
},
|
|
625
|
+
additionalProperties: false
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
required: ["sport_key", "event_id", "legs"],
|
|
630
|
+
additionalProperties: false
|
|
631
|
+
},
|
|
632
|
+
handler: (args) => client().priceSgp(
|
|
633
|
+
args.sport_key,
|
|
634
|
+
args.event_id,
|
|
635
|
+
args.legs,
|
|
636
|
+
args.bookmaker ?? "fanduel"
|
|
637
|
+
)
|
|
638
|
+
},
|
|
581
639
|
{
|
|
582
640
|
name: "propline_export_odds_history",
|
|
583
641
|
title: "Export odds history",
|
|
@@ -1055,6 +1113,34 @@ var tools = [
|
|
|
1055
1113
|
limit: args.limit,
|
|
1056
1114
|
beforeId: args.before_id
|
|
1057
1115
|
})
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
name: "propline_replay_webhook_events",
|
|
1119
|
+
title: "Replay missed webhook events",
|
|
1120
|
+
description: "Re-read a webhook subscription's events in order from a cursor \u2014 answers 'my endpoint was down, what did I miss?'. Every delivery carries an X-PropLine-Sequence header, a counter monotonic WITHIN one subscription; pass the highest one the user processed as since_seq. Do NOT use the delivery id as the cursor: that id is global across all subscriptions, so gaps in it are other customers' traffic and mean nothing. Events come back OLDEST FIRST (the opposite of propline_get_webhook_deliveries, which is a newest-first debugging log). Page by passing next_seq back as since_seq while has_more is true. ALWAYS check `truncated`: true means events after the cursor already aged out of retention (2 days, max 5,000 deliveries per subscription) and are unrecoverable \u2014 tell the user to resync from the REST endpoints rather than reporting them caught up. latest_seq is not subject to retention, so latest_seq - next_seq is an honest 'how far behind' even when the rows are gone. Sequence numbers always increase and never repeat but are NOT guaranteed to be dense \u2014 a skipped number is normal and is not evidence of loss.",
|
|
1121
|
+
inputSchema: {
|
|
1122
|
+
type: "object",
|
|
1123
|
+
properties: {
|
|
1124
|
+
webhook_id: {
|
|
1125
|
+
type: "number",
|
|
1126
|
+
description: "Webhook id (from propline_list_webhooks)."
|
|
1127
|
+
},
|
|
1128
|
+
since_seq: {
|
|
1129
|
+
type: "number",
|
|
1130
|
+
description: "Read events after this sequence. Default 0 = from the oldest retained event (which on an established subscription will correctly report truncated: true)."
|
|
1131
|
+
},
|
|
1132
|
+
limit: {
|
|
1133
|
+
type: "number",
|
|
1134
|
+
description: "Events per page, 1-500. Default 100."
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
required: ["webhook_id"],
|
|
1138
|
+
additionalProperties: false
|
|
1139
|
+
},
|
|
1140
|
+
handler: (args) => client().replayWebhookEvents(args.webhook_id, {
|
|
1141
|
+
sinceSeq: args.since_seq,
|
|
1142
|
+
limit: args.limit
|
|
1143
|
+
})
|
|
1058
1144
|
}
|
|
1059
1145
|
];
|
|
1060
1146
|
function withDemoNote(text) {
|