Skip to content

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