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 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.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 (!(err instanceof UpgradeRequired) && !(err instanceof NotFound)) throw err;
288
- if (err instanceof NotFound && tier === "PRO") tier = "ULTRA";
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
- "version": "1.0.0",
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"