livetennisapi-mcp 1.0.0 → 1.0.2
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/CHANGELOG.md +55 -0
- package/dist/index.js +17 -6
- package/package.json +4 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes are documented here.
|
|
4
|
+
This project adheres to [Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## [1.0.2] — 2026-07-19
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- Listed in the [official MCP Registry](https://registry.modelcontextprotocol.io)
|
|
10
|
+
as `io.github.livetennisapi/livetennisapi-mcp`, so MCP clients can discover the
|
|
11
|
+
server programmatically. Adds `server.json` and the `mcpName` ownership marker,
|
|
12
|
+
published from CI via GitHub OIDC.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- `check_api_status` reported a PRO or ULTRA key as BASIC whenever the events
|
|
16
|
+
probe hit a match with no recorded events: `NotFound` was treated as "not
|
|
17
|
+
entitled" when it actually proves the call was allowed. Only `UpgradeRequired`
|
|
18
|
+
now stops the tier ladder.
|
|
19
|
+
- Guarded against a non-JSON response body decoding to `undefined` and being
|
|
20
|
+
dereferenced in `get_match`, `get_match_score`, `get_match_odds`,
|
|
21
|
+
`get_match_analysis` and `get_player`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- `CHANGELOG.md` is now included in the published tarball.
|
|
25
|
+
|
|
26
|
+
## [1.0.1] — 2026-07-19
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- Published from CI via npm Trusted Publishing (OIDC), so this release carries a
|
|
30
|
+
signed provenance attestation. 1.0.0 did not: npm requires a package to exist
|
|
31
|
+
before OIDC can be configured, so it had to be published manually.
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- `test/protocol.mjs` — drives the built server over stdio and asserts the
|
|
35
|
+
handshake, all 12 tools, and that the missing-key path degrades to a helpful
|
|
36
|
+
message rather than an error.
|
|
37
|
+
- Release workflow, security policy, contributing guide, issue templates.
|
|
38
|
+
|
|
39
|
+
## [1.0.0] — 2026-07-19
|
|
40
|
+
|
|
41
|
+
First release.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- 12 read-only MCP tools over the Live Tennis API: live and upcoming matches,
|
|
45
|
+
match detail and score, player search and profile, fixtures, recent results,
|
|
46
|
+
match events, odds, model analysis, and an API status check.
|
|
47
|
+
- Tier-aware responses: a `403 upgrade_required` becomes a plain-English
|
|
48
|
+
explanation naming the required plan, returned as a normal result rather than
|
|
49
|
+
an error.
|
|
50
|
+
- `check_api_status` probes upward to report which plan the configured key is on.
|
|
51
|
+
|
|
52
|
+
### Notes
|
|
53
|
+
- Published without a provenance attestation. npm requires a package to exist
|
|
54
|
+
before OIDC trusted publishing can be configured, so 1.0.0 had to be published
|
|
55
|
+
manually. Later releases are published from CI with provenance.
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
formatScore
|
|
13
13
|
} from "livetennisapi";
|
|
14
14
|
import { z } from "zod";
|
|
15
|
-
var VERSION = "1.0.
|
|
15
|
+
var VERSION = "1.0.2";
|
|
16
16
|
var apiKey = (process.env.LIVETENNISAPI_KEY ?? "").trim();
|
|
17
17
|
var client = new LiveTennisAPI({
|
|
18
18
|
apiKey,
|
|
@@ -99,6 +99,7 @@ server.tool(
|
|
|
99
99
|
{ match_id: z.number().int().describe("Match id, from get_live_matches or search") },
|
|
100
100
|
({ match_id }) => guard(async () => {
|
|
101
101
|
const match = await client.getMatch(match_id);
|
|
102
|
+
if (!match) return "No data returned for that match id.";
|
|
102
103
|
let out = summarise(match);
|
|
103
104
|
if (match.market) {
|
|
104
105
|
out += `
|
|
@@ -130,6 +131,7 @@ server.tool(
|
|
|
130
131
|
{ match_id: z.number().int() },
|
|
131
132
|
({ match_id }) => guard(async () => {
|
|
132
133
|
const score = await client.getMatchScore(match_id);
|
|
134
|
+
if (!score) return "No score available for that match yet.";
|
|
133
135
|
const parts = [`Score: ${formatScore(score)}`];
|
|
134
136
|
if (score.sets) parts.push(`Sets: ${score.sets.join("-")}`);
|
|
135
137
|
if (score.server) parts.push(`Serving: player ${score.server}`);
|
|
@@ -161,6 +163,7 @@ server.tool(
|
|
|
161
163
|
{ player_id: z.number().int().describe("Player id, from search_players") },
|
|
162
164
|
({ player_id }) => guard(async () => {
|
|
163
165
|
const p = await client.getPlayer(player_id);
|
|
166
|
+
if (!p) return "No data returned for that player id.";
|
|
164
167
|
const rows = [
|
|
165
168
|
`${p.name ?? "Unknown"} [${p.id}]`,
|
|
166
169
|
p.country ? `Country: ${p.country}` : null,
|
|
@@ -211,6 +214,7 @@ server.tool(
|
|
|
211
214
|
{ match_id: z.number().int(), limit: z.number().int().min(1).max(200).default(10) },
|
|
212
215
|
({ match_id, limit }) => guard(async () => {
|
|
213
216
|
const market = await client.getMarketPrices(match_id, { limit });
|
|
217
|
+
if (!market) return "No market data for that match.";
|
|
214
218
|
const lines = [`Market: ${market.question ?? "-"}`];
|
|
215
219
|
if (market.status) lines.push(`Status: ${market.status}`);
|
|
216
220
|
if (market.volume != null) lines.push(`24h volume: ${market.volume}`);
|
|
@@ -230,7 +234,7 @@ server.tool(
|
|
|
230
234
|
{ match_id: z.number().int() },
|
|
231
235
|
({ match_id }) => guard(async () => {
|
|
232
236
|
const analysis = await client.getMatchAnalysis(match_id);
|
|
233
|
-
if (!analysis.thesis && !analysis.profile) return "No model analysis exists for this match yet.";
|
|
237
|
+
if (!analysis || !analysis.thesis && !analysis.profile) return "No model analysis exists for this match yet.";
|
|
234
238
|
const lines = [];
|
|
235
239
|
if (analysis.profile) {
|
|
236
240
|
const p = analysis.profile;
|
|
@@ -281,11 +285,18 @@ No API key is configured, so only this check will work. Set LIVETENNISAPI_KEY in
|
|
|
281
285
|
try {
|
|
282
286
|
await client.listMatchEvents(id, { limit: 1 });
|
|
283
287
|
tier = "PRO";
|
|
284
|
-
await client.getMatchAnalysis(id);
|
|
285
|
-
tier = "ULTRA";
|
|
286
288
|
} catch (err) {
|
|
287
|
-
if (
|
|
288
|
-
if (err instanceof
|
|
289
|
+
if (err instanceof NotFound) tier = "PRO";
|
|
290
|
+
else if (!(err instanceof UpgradeRequired)) throw err;
|
|
291
|
+
}
|
|
292
|
+
if (tier === "PRO") {
|
|
293
|
+
try {
|
|
294
|
+
await client.getMatchAnalysis(id);
|
|
295
|
+
tier = "ULTRA";
|
|
296
|
+
} catch (err) {
|
|
297
|
+
if (err instanceof NotFound) tier = "ULTRA";
|
|
298
|
+
else if (!(err instanceof UpgradeRequired)) throw err;
|
|
299
|
+
}
|
|
289
300
|
}
|
|
290
301
|
}
|
|
291
302
|
return text(
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livetennisapi-mcp",
|
|
3
|
-
"
|
|
3
|
+
"mcpName": "io.github.livetennisapi/livetennisapi-mcp",
|
|
4
|
+
"version": "1.0.2",
|
|
4
5
|
"description": "MCP server for the Live Tennis API \u2014 give Claude, Cursor and other LLM agents real-time tennis scores, players, odds and model win-probability.",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"mcp",
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
"files": [
|
|
37
38
|
"dist",
|
|
38
39
|
"README.md",
|
|
39
|
-
"LICENSE"
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"CHANGELOG.md"
|
|
40
42
|
],
|
|
41
43
|
"engines": {
|
|
42
44
|
"node": ">=18"
|