[PR #1373] [MERGED] fix(mcp): advertise object roots for union input schemas #2835

Closed
opened 2026-10-17 21:10:50 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1373
Author: @fermanzolido
Created: 10/9/2026
Status: ✅ Merged
Merged: 10/9/2026
Merged by: @morluto

Base: main ← Head: fix/mcp-object-root-input-schemas


📝 Commits (4)

  • b859d22 fix(mcp): advertise object roots for union input schemas
  • afb8ce2 test(browser): keep comparison input boundary within lint limits
  • 476a212 fix(mcp): preserve input group dependencies in object schemas
  • 6133b26 chore: merge main into MCP object input schema fix

📊 Changes

5 files changed (+282 additions, -101 deletions)

View changed files

📝 docs/mcp-contracts.md (+15 -10)
📝 src/contracts/inputSchemaPresentation.ts (+122 -3)
📝 src/contracts/toolInputReferences.test.ts (+66 -40)
📝 tests/boundary/mcp/browserComparisonInput.test.ts (+71 -43)
📝 tests/boundary/mcp/toolSchemaValidity.test.ts (+8 -5)

📄 Description

Summary

Anthropic tool input schemas reject anyOf, oneOf, and allOf at the root. Since 63bf9140 removed root-union flattening from presentInputJsonSchema, four tools advertise a root anyOf:

  • compare_web_captures
  • inspect_web_page
  • inspect_electron_page
  • project_managed_application_graph

Observed on Windows 11 with REA 6.2.0:

  • Claude Desktop shows "Claude can't use Compare Web Captures from REA because of a problem with that connector."
  • Claude Code loads all four tools with an empty parameter schema ({"type":"object","properties":{}}), so the model cannot see any of their inputs.

Contract change

presentInputJsonSchema presents a root union of plain-reference object branches as one object:

  • properties are the union of branch properties; a property that differs across branches becomes a property-level anyOf;
  • required is the intersection of branch requirements, and minProperties is the smallest branch requirement count;
  • additionalProperties: false is kept when every branch is strict;
  • when branches differ in their property groups, the root description lists the accepted groups, e.g. Provide exactly one input group: before + after; before_scenario + after_scenario + [normalization].

Runtime validation is unchanged: the SDK still validates every call against the canonical Zod union, so mixed groups (for example before with after_scenario) are rejected before the handler runs. The trade-off is that the advertised schema alone can no longer express cross-group exclusion; tests now assert that explicitly instead of advertised/runtime parity for those inputs.

A branch whose $ref has sibling keywords keeps the root alternatives, because merging siblings into the referenced object would accept inputs the branch rejects under 2020-12.

Verification

  • npm run check:fast passes.
  • npx vitest run tests/boundary/mcp/ src/contracts/: 324 passed, 5 failed, 4 skipped. The same 5 tests fail on the unmodified base on this Windows host (dylibResolutionMcp x2, electronMcp x2, toolErrorSchemaDelivery); they are unrelated to this change.
  • npm run test:focused could not run here: scripts/run-development-tests.mjs spawns with shell: true and breaks on C:\Program Files\nodejs.
  • Against the built server over stdio, no tool advertises a root anyOf, oneOf, or allOf.
  • Not yet verified: reconnecting Claude Desktop to the rebuilt server to confirm the connector warning is gone.
  • No real Hopper or Ghidra verification was performed (not affected).

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1373 **Author:** [@fermanzolido](https://github.com/fermanzolido) **Created:** 10/9/2026 **Status:** ✅ Merged **Merged:** 10/9/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/mcp-object-root-input-schemas` --- ### 📝 Commits (4) - [`b859d22`](https://github.com/morluto/rea/commit/b859d2246fed96a0b6d3f86784da16b46162eca1) fix(mcp): advertise object roots for union input schemas - [`afb8ce2`](https://github.com/morluto/rea/commit/afb8ce24f1043ee759d4188765f67562262751c5) test(browser): keep comparison input boundary within lint limits - [`476a212`](https://github.com/morluto/rea/commit/476a212df3e6d9289e3f1e55c90e9731b55c9aa8) fix(mcp): preserve input group dependencies in object schemas - [`6133b26`](https://github.com/morluto/rea/commit/6133b26239083bc1c21f00bbd555283d9ced51d3) chore: merge main into MCP object input schema fix ### 📊 Changes **5 files changed** (+282 additions, -101 deletions) <details> <summary>View changed files</summary> 📝 `docs/mcp-contracts.md` (+15 -10) 📝 `src/contracts/inputSchemaPresentation.ts` (+122 -3) 📝 `src/contracts/toolInputReferences.test.ts` (+66 -40) 📝 `tests/boundary/mcp/browserComparisonInput.test.ts` (+71 -43) 📝 `tests/boundary/mcp/toolSchemaValidity.test.ts` (+8 -5) </details> ### 📄 Description ## Summary Anthropic tool input schemas reject `anyOf`, `oneOf`, and `allOf` at the root. Since 63bf9140 removed root-union flattening from `presentInputJsonSchema`, four tools advertise a root `anyOf`: - `compare_web_captures` - `inspect_web_page` - `inspect_electron_page` - `project_managed_application_graph` Observed on Windows 11 with REA 6.2.0: - Claude Desktop shows "Claude can't use Compare Web Captures from REA because of a problem with that connector." - Claude Code loads all four tools with an empty parameter schema (`{"type":"object","properties":{}}`), so the model cannot see any of their inputs. ## Contract change `presentInputJsonSchema` presents a root union of plain-reference object branches as one object: - properties are the union of branch properties; a property that differs across branches becomes a property-level `anyOf`; - `required` is the intersection of branch requirements, and `minProperties` is the smallest branch requirement count; - `additionalProperties: false` is kept when every branch is strict; - when branches differ in their property groups, the root `description` lists the accepted groups, e.g. `Provide exactly one input group: before + after; before_scenario + after_scenario + [normalization].` Runtime validation is unchanged: the SDK still validates every call against the canonical Zod union, so mixed groups (for example `before` with `after_scenario`) are rejected before the handler runs. The trade-off is that the advertised schema alone can no longer express cross-group exclusion; tests now assert that explicitly instead of advertised/runtime parity for those inputs. A branch whose `$ref` has sibling keywords keeps the root alternatives, because merging siblings into the referenced object would accept inputs the branch rejects under 2020-12. ## Verification - `npm run check:fast` passes. - `npx vitest run tests/boundary/mcp/ src/contracts/`: 324 passed, 5 failed, 4 skipped. The same 5 tests fail on the unmodified base on this Windows host (`dylibResolutionMcp` x2, `electronMcp` x2, `toolErrorSchemaDelivery`); they are unrelated to this change. - `npm run test:focused` could not run here: `scripts/run-development-tests.mjs` spawns with `shell: true` and breaks on `C:\Program Files\nodejs`. - Against the built server over stdio, no tool advertises a root `anyOf`, `oneOf`, or `allOf`. - Not yet verified: reconnecting Claude Desktop to the rebuilt server to confirm the connector warning is gone. - No real Hopper or Ghidra verification was performed (not affected). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:10:50 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2835
No description provided.