Skip to content

Keep tool output schemas open and additive for hosts that cache them - #13

Merged
markmnl merged 1 commit into
mainfrom
open-output-schemas
Oct 9, 2026
Merged

markmnl merged 1 commit into
mainfrom
open-output-schemas

Conversation

@markmnl

@markmnl markmnl commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Problem

Some MCP chat hosts cache each tool's outputSchema when the connector is added and validate every structuredContent result against that cached copy. Our zod z.object(...) output schemas published additionalProperties: false, required lists and closed enums, so the additive changes in 0.2.6 (new fields such as untrusted_content_notice, thread_topic, list items' reaction/attachment type), widened values (whoami.transport, wait_for_message.status) and the newly optional whoami.directory_names made the read tools fail for existing connections until users reconnected.

Change

  • Open objects everywhere: every output object, nested ones included, is built with z.looseObject via a shared outputObject helper. The JSON Schema in tools/list now has additionalProperties: {} at every level and never false.
  • No closed value sets in outputs: values that may grow (delivery status/via, resolve_address.resolution, get_thread.source, wait_for_message status/transport/skip reason, whoami.transport, download authentication) are plain strings with their current values in the description (openEnum helper). Input schemas keep their enums.
  • Required set frozen at 0.2.5: only fields that have been required since 0.2.5 are still required. Fields added later are optional in the schema but still returned. The required sets now match the published 0.2.5 schemas exactly.
  • whoami matches its 0.2.5 shape again: transport is stdio / http (the text still says "Streamable HTTP"), and directory_names is always present ([] when there is no directory).
  • Docs: README "Output schema compatibility", docs/http-deployment.md "Upgrades and cached tool schemas", the CHANGELOG (Unreleased) and a convention line in AGENTS.md.

Compatibility audit against 0.2.5

I compared the tools/list output schemas of v0.2.4, v0.2.5 and v0.2.6 (all tools, including the HTTP-only get_attachment_download_url and the stdio-only save_attachment):

  • v0.2.4 → v0.2.5: identical.
  • v0.2.5 → v0.2.6: no output field was removed or renamed. add_recipients kept add_to next to the new recipients. The only regressions apart from added fields were in whoami: directory_names became optional and was omitted, and transport changed from http to streamable-http. Both are restored here. wait_for_message gained the status interrupted, which is only returned during a server restart and can't be avoided.
  • With this branch, a host still holding the 0.2.5 schemas validates whoami, resolve_address, mark_read and react as published. Every other tool fails only on additionalProperties: false against fields added in 0.2.6; only a reconnect fixes that.
  • Trade-off: a host that cached the 0.2.6 schemas (for example a user who already reconnected after 0.2.6) validates every tool except whoami over HTTP: its cached enum is stdio | streamable-http, and the result is now http again. Over stdio it validates. Those users need one more reconnect. If that matters more than whoami for 0.2.5-era connections, revert just the transport value in src/tools/identity.ts (and the tests).

Tests

New test/output-schemas.test.ts checks:

  • Every published output schema (stdio and HTTP registrations, all depths) has no additionalProperties: false / unevaluatedProperties: false and no enum/const. Exceptions go in a commented allowlist, which is currently empty.
  • Required fields per object path equal the frozen v0.2.5 tools/list schemas in test/fixtures/output-schemas-0.2.5.json (descriptions stripped).
  • Representative calls of all 16 tools validate with the SDK's bundled Ajv validator against the published schema and against the 0.2.5 schema with unknown properties allowed. The four unchanged tools also validate against the 0.2.5 schema as published.

A mutation check, putting z.object/z.enum back, fails the first test with 53 findings. Updated the existing transport/directory_names assertions. npm run typecheck, npm run build and npm test all pass (184 tests).

🤖 Generated with Claude Code

Some MCP hosts cache each tool's outputSchema when the connector is
added and validate every later result against it. Closed zod objects
(additionalProperties: false), required lists and enums turned every
additive field, widened value set and newly optional field in 0.2.6
into validation failures for existing connections.

- Build every output object with z.looseObject (outputObject helper),
  so no published object, at any depth, rejects unknown properties.
- Publish values that may grow (statuses, transports, resolutions,
  delivery status/via, skip reasons, authentication) as strings with
  their current values in the description (openEnum helper).
- Require only fields present since 0.2.5; later fields are optional
  in the schema but still returned.
- whoami matches its 0.2.5 shape again: transport is "stdio" or
  "http" (the text keeps the Streamable HTTP label) and
  directory_names is always present.
- Test that published output schemas are open with no enum/const,
  that required fields match 0.2.5, and that representative results
  of every tool validate against the published schemas and the 0.2.5
  schemas apart from unknown properties.
- Document output schema compatibility and the one-time reconnect for
  hosts that cached closed schemas from 0.2.6 and earlier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@markmnl
markmnl merged commit a1478d7 into main Oct 9, 2026
3 of 4 checks passed
@markmnl
markmnl deleted the open-output-schemas branch October 9, 2026 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant