propline-mcp 0.32.0 → 0.34.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 +56 -13
- package/dist/http.js.map +1 -1
- package/dist/index.js +52 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ The model uses these tools transparently:
|
|
|
53
53
|
|
|
54
54
|
## Hosted endpoint (no install)
|
|
55
55
|
|
|
56
|
-
The same
|
|
56
|
+
The same 27 tools are served over **Streamable HTTP** at
|
|
57
57
|
|
|
58
58
|
```
|
|
59
59
|
https://mcp.prop-line.com/mcp
|
|
@@ -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
|
|
@@ -116,7 +118,7 @@ Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) o
|
|
|
116
118
|
}
|
|
117
119
|
```
|
|
118
120
|
|
|
119
|
-
Restart Claude Desktop. The hammer icon should show
|
|
121
|
+
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.)
|
|
120
122
|
|
|
121
123
|
#### Claude Code
|
|
122
124
|
|
package/dist/http.js
CHANGED
|
@@ -280,26 +280,40 @@ var PropLineClient = class {
|
|
|
280
280
|
before_id: opts.beforeId
|
|
281
281
|
});
|
|
282
282
|
}
|
|
283
|
+
replayWebhookEvents(webhookId, opts = {}) {
|
|
284
|
+
return this.request(`/v1/webhooks/${webhookId}/replay`, {
|
|
285
|
+
since_seq: opts.sinceSeq,
|
|
286
|
+
limit: opts.limit
|
|
287
|
+
});
|
|
288
|
+
}
|
|
283
289
|
};
|
|
284
290
|
|
|
285
291
|
// src/server.ts
|
|
286
|
-
var VERSION = "0.
|
|
292
|
+
var VERSION = "0.34.0";
|
|
287
293
|
var DEMO_KEY = "be2b8487fcfacb1fbc292a8aa925a84c";
|
|
288
294
|
var apiKey = process.env.PROPLINE_API_KEY;
|
|
289
295
|
var baseUrl = process.env.PROPLINE_BASE_URL;
|
|
296
|
+
var usingDemoKey = !apiKey;
|
|
290
297
|
var requestClient = new AsyncLocalStorage();
|
|
291
|
-
function withClient(c, fn) {
|
|
292
|
-
return requestClient.run(c, fn);
|
|
298
|
+
function withClient(c, demo, fn) {
|
|
299
|
+
return requestClient.run({ client: c, demo }, fn);
|
|
293
300
|
}
|
|
294
301
|
var _client = null;
|
|
295
302
|
function client() {
|
|
296
303
|
const scoped = requestClient.getStore();
|
|
297
|
-
if (scoped) return scoped;
|
|
304
|
+
if (scoped) return scoped.client;
|
|
298
305
|
if (!_client) {
|
|
299
306
|
_client = new PropLineClient({ apiKey: apiKey ?? DEMO_KEY, baseUrl });
|
|
300
307
|
}
|
|
301
308
|
return _client;
|
|
302
309
|
}
|
|
310
|
+
function demoKeyNote() {
|
|
311
|
+
const scoped = requestClient.getStore();
|
|
312
|
+
const demo = scoped ? scoped.demo : usingDemoKey;
|
|
313
|
+
if (!demo) return null;
|
|
314
|
+
const ref = scoped ? "mcp-hosted" : "mcp";
|
|
315
|
+
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.`;
|
|
316
|
+
}
|
|
303
317
|
function filterByMinEv(res, minEvPct) {
|
|
304
318
|
if (typeof minEvPct !== "number" || Number.isNaN(minEvPct)) return res;
|
|
305
319
|
if (!res || typeof res !== "object") return res;
|
|
@@ -1047,8 +1061,42 @@ var tools = [
|
|
|
1047
1061
|
limit: args.limit,
|
|
1048
1062
|
beforeId: args.before_id
|
|
1049
1063
|
})
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
name: "propline_replay_webhook_events",
|
|
1067
|
+
title: "Replay missed webhook events",
|
|
1068
|
+
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.",
|
|
1069
|
+
inputSchema: {
|
|
1070
|
+
type: "object",
|
|
1071
|
+
properties: {
|
|
1072
|
+
webhook_id: {
|
|
1073
|
+
type: "number",
|
|
1074
|
+
description: "Webhook id (from propline_list_webhooks)."
|
|
1075
|
+
},
|
|
1076
|
+
since_seq: {
|
|
1077
|
+
type: "number",
|
|
1078
|
+
description: "Read events after this sequence. Default 0 = from the oldest retained event (which on an established subscription will correctly report truncated: true)."
|
|
1079
|
+
},
|
|
1080
|
+
limit: {
|
|
1081
|
+
type: "number",
|
|
1082
|
+
description: "Events per page, 1-500. Default 100."
|
|
1083
|
+
}
|
|
1084
|
+
},
|
|
1085
|
+
required: ["webhook_id"],
|
|
1086
|
+
additionalProperties: false
|
|
1087
|
+
},
|
|
1088
|
+
handler: (args) => client().replayWebhookEvents(args.webhook_id, {
|
|
1089
|
+
sinceSeq: args.since_seq,
|
|
1090
|
+
limit: args.limit
|
|
1091
|
+
})
|
|
1050
1092
|
}
|
|
1051
1093
|
];
|
|
1094
|
+
function withDemoNote(text) {
|
|
1095
|
+
const blocks = [{ type: "text", text }];
|
|
1096
|
+
const note = demoKeyNote();
|
|
1097
|
+
if (note) blocks.push({ type: "text", text: note });
|
|
1098
|
+
return blocks;
|
|
1099
|
+
}
|
|
1052
1100
|
function createServer() {
|
|
1053
1101
|
const server = new Server(
|
|
1054
1102
|
{ name: "propline-mcp", version: VERSION },
|
|
@@ -1077,25 +1125,20 @@ function createServer() {
|
|
|
1077
1125
|
if (!tool) {
|
|
1078
1126
|
return {
|
|
1079
1127
|
isError: true,
|
|
1080
|
-
content:
|
|
1081
|
-
{
|
|
1082
|
-
type: "text",
|
|
1083
|
-
text: `Unknown tool: ${req.params.name}`
|
|
1084
|
-
}
|
|
1085
|
-
]
|
|
1128
|
+
content: withDemoNote(`Unknown tool: ${req.params.name}`)
|
|
1086
1129
|
};
|
|
1087
1130
|
}
|
|
1088
1131
|
try {
|
|
1089
1132
|
const data = await tool.handler(req.params.arguments ?? {});
|
|
1090
1133
|
const text = typeof data === "string" ? data : JSON.stringify(data, null, 2);
|
|
1091
1134
|
return {
|
|
1092
|
-
content:
|
|
1135
|
+
content: withDemoNote(text)
|
|
1093
1136
|
};
|
|
1094
1137
|
} catch (err) {
|
|
1095
1138
|
const msg = err instanceof PropLineHTTPError ? `PropLine API error ${err.statusCode}: ${err.body.slice(0, 500)}` : err instanceof Error ? err.message : String(err);
|
|
1096
1139
|
return {
|
|
1097
1140
|
isError: true,
|
|
1098
|
-
content:
|
|
1141
|
+
content: withDemoNote(msg)
|
|
1099
1142
|
};
|
|
1100
1143
|
}
|
|
1101
1144
|
});
|
|
@@ -1144,7 +1187,7 @@ async function handleMcp(req, res) {
|
|
|
1144
1187
|
});
|
|
1145
1188
|
if (demo) res.setHeader("X-PropLine-Demo-Key", "1");
|
|
1146
1189
|
await server.connect(transport);
|
|
1147
|
-
await withClient(client2, () => transport.handleRequest(req, res));
|
|
1190
|
+
await withClient(client2, demo, () => transport.handleRequest(req, res));
|
|
1148
1191
|
}
|
|
1149
1192
|
var manifest = () => ({
|
|
1150
1193
|
name: "propline-mcp",
|