propline-mcp 0.1.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/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/index.js +395 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PropLine
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# propline-mcp
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/propline-mcp)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**Model Context Protocol server** for the [PropLine](https://prop-line.com) player props betting odds API. Plug it into Claude Desktop, Claude Code, or any MCP-compatible client and ask natural-language questions about live odds, prop resolution, cross-book +EV, scores, and box-score stats — the model picks the right tool, calls the API, and answers from real data.
|
|
7
|
+
|
|
8
|
+
> No more "I'd need an API for that" deflections from your AI assistant. PropLine MCP turns sports-betting research into a chat.
|
|
9
|
+
|
|
10
|
+
## What you can ask
|
|
11
|
+
|
|
12
|
+
Once installed, you can prompt the model with things like:
|
|
13
|
+
|
|
14
|
+
- *"What's the +EV on Yankees vs Red Sox tonight across all books?"*
|
|
15
|
+
- *"Pull Aaron Judge's last 20 prop-bet history with hit/miss outcomes."*
|
|
16
|
+
- *"List today's MLB pitcher strikeout props from DraftKings and Pinnacle side by side."*
|
|
17
|
+
- *"Did Nikola Jokic's points prop hit last night? What was the line?"*
|
|
18
|
+
- *"Compare PrizePicks DFS projections to Bovada lines for tonight's NBA slate."*
|
|
19
|
+
|
|
20
|
+
The model uses these tools transparently:
|
|
21
|
+
|
|
22
|
+
| Tool | What it does |
|
|
23
|
+
|------|--------------|
|
|
24
|
+
| `propline_list_sports` | Discover what sports PropLine polls (24 today) |
|
|
25
|
+
| `propline_list_events` | Upcoming events for a sport, with ids |
|
|
26
|
+
| `propline_list_event_markets` | Available market types for an event |
|
|
27
|
+
| `propline_get_odds` | Live odds — bulk by sport or full props per event |
|
|
28
|
+
| `propline_get_odds_history` | Pro: full snapshot history per outcome |
|
|
29
|
+
| `propline_get_scores` | Game scores + status (free) |
|
|
30
|
+
| `propline_get_event_stats` | Raw box-score stats (free, book-agnostic) |
|
|
31
|
+
| `propline_get_event_results` | Pro: graded won/lost/push per prop |
|
|
32
|
+
| `propline_get_player_history` | Player prop history with resolution |
|
|
33
|
+
| `propline_get_event_ev` | Pro: cross-book +EV with no-vig fair lines |
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
### 1. Get a PropLine API key
|
|
38
|
+
|
|
39
|
+
[prop-line.com](https://prop-line.com) — free tier is 1,000 requests/day, no credit card. Pro at $19/mo unlocks resolution, history, and +EV.
|
|
40
|
+
|
|
41
|
+
### 2. Add to your MCP client
|
|
42
|
+
|
|
43
|
+
#### Claude Desktop
|
|
44
|
+
|
|
45
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mcpServers": {
|
|
50
|
+
"propline": {
|
|
51
|
+
"command": "npx",
|
|
52
|
+
"args": ["-y", "propline-mcp"],
|
|
53
|
+
"env": {
|
|
54
|
+
"PROPLINE_API_KEY": "YOUR_KEY_HERE"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Restart Claude Desktop. The hammer icon should show 10 PropLine tools.
|
|
62
|
+
|
|
63
|
+
#### Claude Code
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
claude mcp add propline \
|
|
67
|
+
--env PROPLINE_API_KEY=YOUR_KEY \
|
|
68
|
+
-- npx -y propline-mcp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### Any other MCP client
|
|
72
|
+
|
|
73
|
+
Run `propline-mcp` as a stdio server. Most clients accept a command + env. See the [MCP spec](https://modelcontextprotocol.io/) for client-specific config.
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
| Env var | Required | Default | Notes |
|
|
78
|
+
|--------|:--------:|---------|-------|
|
|
79
|
+
| `PROPLINE_API_KEY` | yes | — | Get a free key at [prop-line.com](https://prop-line.com) |
|
|
80
|
+
| `PROPLINE_BASE_URL` | no | `https://api.prop-line.com` | Override for self-hosted setups |
|
|
81
|
+
|
|
82
|
+
## Comparison with the-odds-api
|
|
83
|
+
|
|
84
|
+
PropLine is API-compatible at the response level (same `bookmakers[].markets[].outcomes[]` shape) and adds three things the-odds-api doesn't offer at any tier:
|
|
85
|
+
|
|
86
|
+
1. **Prop resolution** — every Over/Under graded against the actual box-score stat after the game
|
|
87
|
+
2. **Cross-book +EV** — Pinnacle-anchored no-vig fair lines per book, sorted with +EV plays at the top
|
|
88
|
+
3. **Webhooks** — push delivery on Streaming tier, not pull-only
|
|
89
|
+
|
|
90
|
+
Pricing: free at 1,000 req/day (vs their 500/month), Pro at $19/mo for 25,000 req/day, Streaming at $79/mo for 1,000,000 req/day. No credit math.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import {
|
|
7
|
+
CallToolRequestSchema,
|
|
8
|
+
ListToolsRequestSchema
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
|
|
11
|
+
// src/client.ts
|
|
12
|
+
var DEFAULT_BASE_URL = "https://api.prop-line.com";
|
|
13
|
+
var PropLineHTTPError = class extends Error {
|
|
14
|
+
constructor(statusCode, body) {
|
|
15
|
+
super(`PropLine HTTP ${statusCode}: ${body.slice(0, 200)}`);
|
|
16
|
+
this.statusCode = statusCode;
|
|
17
|
+
this.body = body;
|
|
18
|
+
this.name = "PropLineHTTPError";
|
|
19
|
+
}
|
|
20
|
+
statusCode;
|
|
21
|
+
body;
|
|
22
|
+
};
|
|
23
|
+
var PropLineClient = class {
|
|
24
|
+
apiKey;
|
|
25
|
+
baseUrl;
|
|
26
|
+
timeoutMs;
|
|
27
|
+
constructor(opts) {
|
|
28
|
+
if (!opts.apiKey) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"PROPLINE_API_KEY is required. Get one at https://prop-line.com"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
this.apiKey = opts.apiKey;
|
|
34
|
+
this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
35
|
+
this.timeoutMs = opts.timeoutMs ?? 2e4;
|
|
36
|
+
}
|
|
37
|
+
async request(path, query = {}) {
|
|
38
|
+
const url = new URL(this.baseUrl + path);
|
|
39
|
+
for (const [k, v] of Object.entries(query)) {
|
|
40
|
+
if (v !== void 0 && v !== null && v !== "") {
|
|
41
|
+
url.searchParams.set(k, String(v));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
46
|
+
try {
|
|
47
|
+
const r = await fetch(url, {
|
|
48
|
+
headers: {
|
|
49
|
+
"X-API-Key": this.apiKey,
|
|
50
|
+
Accept: "application/json",
|
|
51
|
+
"User-Agent": "propline-mcp/0.1.0"
|
|
52
|
+
},
|
|
53
|
+
signal: controller.signal
|
|
54
|
+
});
|
|
55
|
+
const text = await r.text();
|
|
56
|
+
if (!r.ok) {
|
|
57
|
+
throw new PropLineHTTPError(r.status, text);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(text);
|
|
61
|
+
} catch {
|
|
62
|
+
return text;
|
|
63
|
+
}
|
|
64
|
+
} finally {
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ----- Discovery -----
|
|
69
|
+
listSports() {
|
|
70
|
+
return this.request("/v1/sports");
|
|
71
|
+
}
|
|
72
|
+
listEvents(sportKey, opts = {}) {
|
|
73
|
+
return this.request(`/v1/sports/${sportKey}/events`, {
|
|
74
|
+
live: opts.live ? "true" : void 0
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
listEventMarkets(sportKey, eventId) {
|
|
78
|
+
return this.request(`/v1/sports/${sportKey}/events/${eventId}/markets`);
|
|
79
|
+
}
|
|
80
|
+
// ----- Odds -----
|
|
81
|
+
getOdds(sportKey, opts = {}) {
|
|
82
|
+
if (opts.eventId) {
|
|
83
|
+
return this.request(
|
|
84
|
+
`/v1/sports/${sportKey}/events/${opts.eventId}/odds`,
|
|
85
|
+
{
|
|
86
|
+
markets: opts.markets,
|
|
87
|
+
bookmakers: opts.bookmakers
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return this.request(`/v1/sports/${sportKey}/odds`, {
|
|
92
|
+
markets: opts.markets,
|
|
93
|
+
bookmakers: opts.bookmakers
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
getOddsHistory(sportKey, eventId, opts = {}) {
|
|
97
|
+
return this.request(
|
|
98
|
+
`/v1/sports/${sportKey}/events/${eventId}/odds/history`,
|
|
99
|
+
{ markets: opts.markets }
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
// ----- Scores + stats + resolution -----
|
|
103
|
+
getScores(sportKey, opts = {}) {
|
|
104
|
+
return this.request(`/v1/sports/${sportKey}/scores`, {
|
|
105
|
+
daysFrom: opts.daysFrom
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
getEventStats(sportKey, eventId) {
|
|
109
|
+
return this.request(`/v1/sports/${sportKey}/events/${eventId}/stats`);
|
|
110
|
+
}
|
|
111
|
+
getEventResults(sportKey, eventId) {
|
|
112
|
+
return this.request(`/v1/sports/${sportKey}/events/${eventId}/results`);
|
|
113
|
+
}
|
|
114
|
+
// ----- Player history -----
|
|
115
|
+
getPlayerHistory(sportKey, playerName, opts = {}) {
|
|
116
|
+
return this.request(
|
|
117
|
+
`/v1/sports/${sportKey}/players/${encodeURIComponent(playerName)}/history`,
|
|
118
|
+
{ limit: opts.limit, markets: opts.markets }
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
// ----- Cross-book +EV -----
|
|
122
|
+
getEventEv(sportKey, eventId, opts = {}) {
|
|
123
|
+
return this.request(`/v1/sports/${sportKey}/events/${eventId}/ev`, {
|
|
124
|
+
markets: opts.markets,
|
|
125
|
+
min_ev_pct: opts.minEvPct
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// src/index.ts
|
|
131
|
+
var VERSION = "0.1.0";
|
|
132
|
+
var apiKey = process.env.PROPLINE_API_KEY;
|
|
133
|
+
var baseUrl = process.env.PROPLINE_BASE_URL;
|
|
134
|
+
if (!apiKey) {
|
|
135
|
+
console.error(
|
|
136
|
+
"[propline-mcp] PROPLINE_API_KEY not set. Get a free key at https://prop-line.com and set it in your MCP client config."
|
|
137
|
+
);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
var client = new PropLineClient({ apiKey, baseUrl });
|
|
141
|
+
var tools = [
|
|
142
|
+
{
|
|
143
|
+
name: "propline_list_sports",
|
|
144
|
+
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.",
|
|
145
|
+
inputSchema: {
|
|
146
|
+
type: "object",
|
|
147
|
+
properties: {},
|
|
148
|
+
additionalProperties: false
|
|
149
|
+
},
|
|
150
|
+
handler: () => client.listSports()
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "propline_list_events",
|
|
154
|
+
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.",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: "object",
|
|
157
|
+
properties: {
|
|
158
|
+
sport_key: {
|
|
159
|
+
type: "string",
|
|
160
|
+
description: "Sport key from propline_list_sports \u2014 e.g. baseball_mlb, basketball_nba"
|
|
161
|
+
},
|
|
162
|
+
live: {
|
|
163
|
+
type: "boolean",
|
|
164
|
+
description: "If true, only return in-progress (live) events. Defaults to false."
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
required: ["sport_key"],
|
|
168
|
+
additionalProperties: false
|
|
169
|
+
},
|
|
170
|
+
handler: (args) => client.listEvents(args.sport_key, {
|
|
171
|
+
live: args.live
|
|
172
|
+
})
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
name: "propline_list_event_markets",
|
|
176
|
+
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.",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {
|
|
180
|
+
sport_key: { type: "string" },
|
|
181
|
+
event_id: { type: ["string", "number"] }
|
|
182
|
+
},
|
|
183
|
+
required: ["sport_key", "event_id"],
|
|
184
|
+
additionalProperties: false
|
|
185
|
+
},
|
|
186
|
+
handler: (args) => client.listEventMarkets(
|
|
187
|
+
args.sport_key,
|
|
188
|
+
args.event_id
|
|
189
|
+
)
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "propline_get_odds",
|
|
193
|
+
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 with all 5 books + PrizePicks DFS.",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
type: "object",
|
|
196
|
+
properties: {
|
|
197
|
+
sport_key: { type: "string" },
|
|
198
|
+
event_id: {
|
|
199
|
+
type: ["string", "number"],
|
|
200
|
+
description: "Optional. If set, returns props for this event."
|
|
201
|
+
},
|
|
202
|
+
markets: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "Comma-separated market keys. Defaults to h2h on bulk; all on event."
|
|
205
|
+
},
|
|
206
|
+
bookmakers: {
|
|
207
|
+
type: "string",
|
|
208
|
+
description: "Comma-separated subset of book keys (bovada, draftkings, fanduel, pinnacle, unibet, prizepicks). Default returns all available."
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
required: ["sport_key"],
|
|
212
|
+
additionalProperties: false
|
|
213
|
+
},
|
|
214
|
+
handler: (args) => client.getOdds(args.sport_key, {
|
|
215
|
+
eventId: args.event_id,
|
|
216
|
+
markets: args.markets,
|
|
217
|
+
bookmakers: args.bookmakers
|
|
218
|
+
})
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "propline_get_odds_history",
|
|
222
|
+
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.",
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: "object",
|
|
225
|
+
properties: {
|
|
226
|
+
sport_key: { type: "string" },
|
|
227
|
+
event_id: { type: ["string", "number"] },
|
|
228
|
+
markets: { type: "string" }
|
|
229
|
+
},
|
|
230
|
+
required: ["sport_key", "event_id"],
|
|
231
|
+
additionalProperties: false
|
|
232
|
+
},
|
|
233
|
+
handler: (args) => client.getOddsHistory(
|
|
234
|
+
args.sport_key,
|
|
235
|
+
args.event_id,
|
|
236
|
+
{ markets: args.markets }
|
|
237
|
+
)
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
name: "propline_get_scores",
|
|
241
|
+
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'.",
|
|
242
|
+
inputSchema: {
|
|
243
|
+
type: "object",
|
|
244
|
+
properties: {
|
|
245
|
+
sport_key: { type: "string" },
|
|
246
|
+
days_from: {
|
|
247
|
+
type: "number",
|
|
248
|
+
description: "How many past days of completed games to include. Defaults to 1."
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
required: ["sport_key"],
|
|
252
|
+
additionalProperties: false
|
|
253
|
+
},
|
|
254
|
+
handler: (args) => client.getScores(args.sport_key, {
|
|
255
|
+
daysFrom: args.days_from
|
|
256
|
+
})
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: "propline_get_event_stats",
|
|
260
|
+
description: "Book-agnostic raw box-score stats for a completed event. Returns per-player stats (e.g. strikeouts, hits, points, rebounds, shots-on-goal) decoupled from any sportsbook's lines. Free tier.",
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: "object",
|
|
263
|
+
properties: {
|
|
264
|
+
sport_key: { type: "string" },
|
|
265
|
+
event_id: { type: ["string", "number"] }
|
|
266
|
+
},
|
|
267
|
+
required: ["sport_key", "event_id"],
|
|
268
|
+
additionalProperties: false
|
|
269
|
+
},
|
|
270
|
+
handler: (args) => client.getEventStats(
|
|
271
|
+
args.sport_key,
|
|
272
|
+
args.event_id
|
|
273
|
+
)
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
name: "propline_get_event_results",
|
|
277
|
+
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.",
|
|
278
|
+
inputSchema: {
|
|
279
|
+
type: "object",
|
|
280
|
+
properties: {
|
|
281
|
+
sport_key: { type: "string" },
|
|
282
|
+
event_id: { type: ["string", "number"] }
|
|
283
|
+
},
|
|
284
|
+
required: ["sport_key", "event_id"],
|
|
285
|
+
additionalProperties: false
|
|
286
|
+
},
|
|
287
|
+
handler: (args) => client.getEventResults(
|
|
288
|
+
args.sport_key,
|
|
289
|
+
args.event_id
|
|
290
|
+
)
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
name: "propline_get_player_history",
|
|
294
|
+
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.",
|
|
295
|
+
inputSchema: {
|
|
296
|
+
type: "object",
|
|
297
|
+
properties: {
|
|
298
|
+
sport_key: { type: "string" },
|
|
299
|
+
player_name: {
|
|
300
|
+
type: "string",
|
|
301
|
+
description: "Player name as it appears in box scores \u2014 e.g. 'Aaron Judge', 'Nikola Jokic'"
|
|
302
|
+
},
|
|
303
|
+
limit: {
|
|
304
|
+
type: "number",
|
|
305
|
+
description: "Max number of past games (default 20, max 100)"
|
|
306
|
+
},
|
|
307
|
+
markets: {
|
|
308
|
+
type: "string",
|
|
309
|
+
description: "Comma-separated subset of markets (e.g. 'player_points,player_rebounds')"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
required: ["sport_key", "player_name"],
|
|
313
|
+
additionalProperties: false
|
|
314
|
+
},
|
|
315
|
+
handler: (args) => client.getPlayerHistory(
|
|
316
|
+
args.sport_key,
|
|
317
|
+
args.player_name,
|
|
318
|
+
{
|
|
319
|
+
limit: args.limit,
|
|
320
|
+
markets: args.markets
|
|
321
|
+
}
|
|
322
|
+
)
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: "propline_get_event_ev",
|
|
326
|
+
description: "Pro-tier endpoint. Returns cross-book +EV per outcome for an event. We anchor on Pinnacle's sharp line, 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).",
|
|
327
|
+
inputSchema: {
|
|
328
|
+
type: "object",
|
|
329
|
+
properties: {
|
|
330
|
+
sport_key: { type: "string" },
|
|
331
|
+
event_id: { type: ["string", "number"] },
|
|
332
|
+
markets: { type: "string" },
|
|
333
|
+
min_ev_pct: {
|
|
334
|
+
type: "number",
|
|
335
|
+
description: "Filter to outcomes with EV \u2265 this percent (e.g. 2.0)."
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
required: ["sport_key", "event_id"],
|
|
339
|
+
additionalProperties: false
|
|
340
|
+
},
|
|
341
|
+
handler: (args) => client.getEventEv(
|
|
342
|
+
args.sport_key,
|
|
343
|
+
args.event_id,
|
|
344
|
+
{
|
|
345
|
+
markets: args.markets,
|
|
346
|
+
minEvPct: args.min_ev_pct
|
|
347
|
+
}
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
];
|
|
351
|
+
var server = new Server(
|
|
352
|
+
{ name: "propline-mcp", version: VERSION },
|
|
353
|
+
{ capabilities: { tools: {} } }
|
|
354
|
+
);
|
|
355
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
356
|
+
tools: tools.map((t) => ({
|
|
357
|
+
name: t.name,
|
|
358
|
+
description: t.description,
|
|
359
|
+
inputSchema: t.inputSchema
|
|
360
|
+
}))
|
|
361
|
+
}));
|
|
362
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
363
|
+
const tool = tools.find((t) => t.name === req.params.name);
|
|
364
|
+
if (!tool) {
|
|
365
|
+
return {
|
|
366
|
+
isError: true,
|
|
367
|
+
content: [
|
|
368
|
+
{
|
|
369
|
+
type: "text",
|
|
370
|
+
text: `Unknown tool: ${req.params.name}`
|
|
371
|
+
}
|
|
372
|
+
]
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const data = await tool.handler(req.params.arguments ?? {});
|
|
377
|
+
const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
378
|
+
return {
|
|
379
|
+
content: [{ type: "text", text }]
|
|
380
|
+
};
|
|
381
|
+
} catch (err) {
|
|
382
|
+
const msg = err instanceof PropLineHTTPError ? `PropLine API error ${err.statusCode}: ${err.body.slice(0, 500)}` : err instanceof Error ? err.message : String(err);
|
|
383
|
+
return {
|
|
384
|
+
isError: true,
|
|
385
|
+
content: [{ type: "text", text: msg }]
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
var transport = new StdioServerTransport();
|
|
390
|
+
await server.connect(transport);
|
|
391
|
+
console.error(`[propline-mcp ${VERSION}] connected via stdio`);
|
|
392
|
+
export {
|
|
393
|
+
VERSION
|
|
394
|
+
};
|
|
395
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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.1.0\";\n\nconst apiKey = process.env.PROPLINE_API_KEY;\nconst baseUrl = process.env.PROPLINE_BASE_URL;\n\nif (!apiKey) {\n console.error(\n \"[propline-mcp] PROPLINE_API_KEY not set. Get a free key at \" +\n \"https://prop-line.com and set it in your MCP client config.\",\n );\n process.exit(1);\n}\n\nconst client = new PropLineClient({ apiKey, baseUrl });\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 with all 5 books + \" +\n \"PrizePicks DFS.\",\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; all on event.\",\n },\n bookmakers: {\n type: \"string\",\n description:\n \"Comma-separated subset of book keys (bovada, draftkings, fanduel, pinnacle, unibet, prizepicks). 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_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`);\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 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
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "propline-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"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.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"claude",
|
|
9
|
+
"claude-desktop",
|
|
10
|
+
"anthropic",
|
|
11
|
+
"sports",
|
|
12
|
+
"betting",
|
|
13
|
+
"odds",
|
|
14
|
+
"player-props",
|
|
15
|
+
"api",
|
|
16
|
+
"sportsbook",
|
|
17
|
+
"mlb",
|
|
18
|
+
"nba",
|
|
19
|
+
"nhl",
|
|
20
|
+
"nfl",
|
|
21
|
+
"soccer",
|
|
22
|
+
"ufc",
|
|
23
|
+
"bovada",
|
|
24
|
+
"draftkings",
|
|
25
|
+
"fanduel",
|
|
26
|
+
"pinnacle",
|
|
27
|
+
"prizepicks",
|
|
28
|
+
"dfs"
|
|
29
|
+
],
|
|
30
|
+
"homepage": "https://prop-line.com",
|
|
31
|
+
"bugs": "https://github.com/proplineapi/propline-mcp/issues",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/proplineapi/propline-mcp.git"
|
|
35
|
+
},
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"author": "PropLine <support@prop-line.com>",
|
|
38
|
+
"type": "module",
|
|
39
|
+
"main": "./dist/index.js",
|
|
40
|
+
"bin": {
|
|
41
|
+
"propline-mcp": "./dist/index.js"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"start": "node dist/index.js",
|
|
55
|
+
"dev": "tsup --watch",
|
|
56
|
+
"prepublishOnly": "npm run build"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^20.19.39",
|
|
63
|
+
"tsup": "^8.3.0",
|
|
64
|
+
"typescript": "^5.6.0"
|
|
65
|
+
}
|
|
66
|
+
}
|