Skip to content

Releases: livetennisapi/livetennisapi-mcp

v1.2.1 — domain-ownership proof for directories

Choose a tag to compare

@bensynapse bensynapse released this 22 Jul 07:01

Serves /.well-known/glama.json on the HTTP transport, so directories that verify maintainership by fetching a file from the server's own domain can confirm ownership.

The address is read from MCP_MAINTAINER_EMAIL rather than committed — this repo is public, and a maintainer's address sitting in the source is a spam target for anyone reading it. The route is registered only when that variable is set, so self-hosters never accidentally serve someone else's claim.

No change to the MCP protocol surface: still 12 tools, same schemas.

v1.2.0 — structured output on every tool

Choose a tag to compare

@bensynapse bensynapse released this 22 Jul 06:17

Every tool now declares an outputSchema and returns structuredContent alongside the existing prose, so a program can read fields directly instead of parsing English back out of a summary. The prose is byte-identical to before — nothing regresses if you are already using it.

Also adds tool annotations (readOnlyHint, destructiveHint: false, idempotentHint, openWorldHint), titles, and complete parameter descriptions on all 12 tools. The annotations are simply true: every tool is a GET against a live external API.

The part worth reviewing

Declaring an output schema changes runtime behaviour, not just metadata: the SDK throws on any non-error result that omits structured content. Three of this server's return paths are deliberately non-error — a tier wall, a missing key, an empty result — because marking a tier wall as an error makes clients retry or bail rather than relay the upgrade path to the user.

Left for each tool to remember, this would have 500'd all twelve tools on their most common failure paths. So guard() emits the structured half itself and no individual tool can forget.

test/tools-output.mjs drives all 12 tools twice — once with no key, once against a stub upstream — asserting structured content is present and valid on both. test/mutate.py now covers src/server.ts too: 13 mutations, all killed, including one that strips structured content from exactly that no-key path.

Fixed

The no-key message assumed stdio, telling users to set an environment variable — wrong advice for anyone calling the hosted HTTP endpoint. It now names both routes.

Full changelog: https://github.com/livetennisapi/livetennisapi-mcp/blob/main/CHANGELOG.md

v1.1.0 — hosted HTTP endpoint

Choose a tag to compare

@bensynapse bensynapse released this 22 Jul 05:38
61d3581

Adds a Streamable-HTTP transport, so the server can be reached over HTTP by clients and directories that cannot run a local stdio process.

https://mcp.livetennisapi.com/mcp

Tools are listable without a key so directories can introspect; calling one needs a key. Send it as Authorization: Bearer twjp_…, X-API-Key:, or ?token=.

Multi-tenant by construction

The stdio server reads one key from the environment into one long-lived client — correct for stdio, catastrophic over HTTP, where it would serve every anonymous caller on the operator's key at the operator's tier, silently and permanently.

So the HTTP transport builds a fresh server per request bound to that request's key, runs stateless so no session map can hand one caller another's server, and has no fallback to LIVETENNISAPI_KEY. The systemd unit clears that variable outright as defence in depth.

Rate limiting

60 req/min anonymous, 300 keyed, tunable via MCP_RATE_LIMIT_ANON / MCP_RATE_LIMIT_KEYED. Keyed on API key rather than IP — behind a Cloudflare tunnel every request arrives from 127.0.0.1, so an IP-keyed limiter would put every user in one bucket.

Tests that can fail

npm run test:mutation reintroduces each bug the tests claim to catch and asserts the suite goes red. The first version of the rate-limit test passed while the limiter bucketed globally; it now reads the RateLimit header and asserts a fresh caller starts with a full budget.

The stdio server is unchanged. If you use npx livetennisapi-mcp, nothing about your setup changes and your key still never leaves your machine.

Full changelog: https://github.com/livetennisapi/livetennisapi-mcp/blob/main/CHANGELOG.md

livetennisapi-mcp 1.0.4

Choose a tag to compare

@bensynapse bensynapse released this 22 Jul 02:05

Fixed

A full MCP introspection pass errored twice.

This server exposes tools only, and the SDK registers the resources/* and prompts/* handlers lazily — so resources/list and prompts/list answered -32601 Method not found. That is spec-correct (the capabilities were never advertised, so a conforming client would not call them), but automated indexers are not conforming clients: Glama documents its introspection pass as tools/list, resources/list, prompts/list run unconditionally, and a pass that errors twice is a plausible way to end up unindexed and unscored.

Both now advertise the capability and return an empty collection — the honest answer, rather than registering a placeholder resource purely to satisfy an indexer.

initialize     -> capabilities: prompts, resources, tools
tools/list     -> 12
resources/list -> 0   (was -32601)
prompts/list   -> 0   (was -32601)

Added

Dockerfile — indexers build and run the server to introspect it. Multi-stage, dev dependencies dropped, runs as the unprivileged node user, and works with no credentials because the no-key path is non-fatal by design.

Full changelog: https://github.com/livetennisapi/livetennisapi-mcp/blob/main/CHANGELOG.md

livetennisapi-mcp 1.0.3

Choose a tag to compare

@bensynapse bensynapse released this 21 Jul 12:36

Fixed

check_api_status reported a valid FREE key as "Could not reach the API".

The tier probe calls listCompletedMatches()/history/matches, which is BASIC-gated — and did not catch UpgradeRequired. On a FREE key that 403 escaped to the outer handler and was rendered as an unreachable API. The probe also started the ladder at BASIC, so a free key could never have been named correctly even had it not thrown.

The history probe is now caught and identifies FREE, and the upward climb is skipped for it (a FREE key cannot hold PRO/ULTRA).

Changed

  • Tier legends in the probe result and the tier-wall explanation list the new FREE tier.
  • No-key prompts point at the no-card signup (https://livetennisapi.com/subscribe/free) rather than the pricing page.
  • glama.json claims maintainership of the Glama listing.

Full changelog: https://github.com/livetennisapi/livetennisapi-mcp/blob/main/CHANGELOG.md

livetennisapi-mcp 1.0.2

Choose a tag to compare

@bensynapse bensynapse released this 19 Jul 00:37

Now listed in the official MCP Registry as io.github.livetennisapi/livetennisapi-mcp, so MCP clients can discover it programmatically.

Fixed

  • check_api_status reported PRO/ULTRA keys as BASIC. The tier ladder treated a NotFound on the events probe as 'not entitled', when it actually proves the call was allowed — a completed match with no recorded events is common. Only UpgradeRequired stops the ladder now. This was the one diagnostic the tool exists to give.
  • Guarded a non-JSON response body decoding to undefined and being dereferenced across five tools.
npx livetennisapi-mcp

livetennisapi-mcp 1.0.1

Choose a tag to compare

@bensynapse bensynapse released this 19 Jul 00:25

Published from CI via npm Trusted Publishing (OIDC), so this release carries a signed provenance attestation. 1.0.0 did not — npm requires a package to exist before OIDC can be configured, so the first release had to go out manually.

Also adds a protocol smoke test that drives the built server over stdio and asserts the handshake, all 12 tools, and that the missing-key path degrades to a helpful message rather than an error.

No functional change to the tools.

npx livetennisapi-mcp