Skip to content

fix(mcp): advertise only the collection and selector pairings the server admits - #1792

Closed
yylm-lm wants to merge 3 commits into
morluto:mainfrom
yylm-lm:fix/analysis-view-selector-agreement
Closed

yylm-lm wants to merge 3 commits into
morluto:mainfrom
yylm-lm:fix/analysis-view-selector-agreement

Conversation

@yylm-lm

@yylm-lm yylm-lm commented Oct 11, 2026 •

Copy link
Copy Markdown

Closes #1695

Problem

inspect_analysis_view expressed its collection/selector pairing in a
superRefine. A refinement is absent from the converted JSON Schema, so the
advertised input schema and runtime validation disagreed:

view advertised schema runtime
collection: "modules" + selector: { index } valid rejected
collection: "sections" + selector: { node_id } valid rejected

A client that validates arguments against tools/list therefore sent
arguments the server refused as invalid, before it could interpret whether the
retained Evidence supported the requested collection. Reproduced with the real
stdio MCP transport and Ajv 8.20.0 draft-2020-12 validation.

Change

Express the existing alternatives in the owning contract rather than in a
refinement: modules are selected by path or node_id; sections and symbols
by index or name. This is the direction the report suggested.

  • Runtime validation is unchanged: every pairing rejected before is still
    rejected, now by the schema as well.
  • Selection semantics, Evidence identity, and path ambiguity handling are
    untouched.
  • The advertised schema now carries the alternatives, so a client validating
    against it cannot construct an argument set the server refuses.

Verification

tests/boundary/mcp/analysisViewSelectorAgreement.test.ts drives the real
stdio MCP transport over twelve collection/selector pairings and asserts that
every advertised pairing is admitted and every unadvertised one is rejected as
invalid input. Admission is keyed on the input-validation failure rather than on
call success, so a pairing that passes validation and then fails Evidence
resolution still counts as admitted.

  • npx tsc --noEmit passes.
  • tests/boundary/mcp/analysisViewMcp.test.ts (which exercises this schema
    and its output contract) passes 8/8.
  • npm run mcp-catalog:check passes, so the generated catalog stays fresh.

Note for review

This changes the shape of some validation errors for view.kind: "item", for
example from expected: ["index","name"] to expected: "object", because the
pairing is now a discriminated union. I believe that is the intended trade: the
previous error text described a constraint the schema did not advertise. If you
would rather preserve the old message shape, say so and I will adjust.

Not verified here

The five failures in tests/boundary/cli/analysisViewCli.test.ts and two in
tests/boundary/mcp/analysisSummaryDetailMcp.test.ts are pre-existing on this
checkout (missing stream-chain/stream-json and a stale expected-error
shape); I confirmed each by stashing this change and reproducing it on a clean
tree. No new failures are introduced.

…ver admits

inspect_analysis_view expressed the collection/selector pairing in a
superRefine. A refinement is absent from the converted JSON Schema, so the
advertised input schema accepted `modules` with `index` and `sections` with
`node_id` while runtime validation rejected those same arguments. A client
that validates against tools/list therefore sent arguments the server refused
as invalid, before it could interpret whether the retained Evidence supported
the requested collection.

Express the existing alternatives in the owning contract instead: modules are
selected by path or node_id, sections and symbols by index or name. Runtime
validation, selection semantics, and Evidence identity are unchanged; the
pairings that were rejected before are still rejected, now from the schema too.

Adds tests/boundary/mcp/analysisViewSelectorAgreement.test.ts, which drives the
real stdio MCP transport and asserts that every advertised pairing is admitted
and every unadvertised one is rejected as invalid input.

Closes morluto#1695
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T11:18:53.103967Z 4cfb8d0 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c21de4c6ce

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +151 to +152
for (const entry of selectorCases) {
const rejected = await wasRejectedAsInvalidInput(client, entry.view);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate the schema returned by tools/list

When SDK conversion still advertises a forbidden pairing while the server's Zod parser rejects it—the exact regression this change addresses—this loop still passes because advertised.inputSchema is never compiled or applied and the helper only checks that the eventual tool response is an input error. In fact, retaining this test while reverting the production schema to the parent superRefine implementation would remain green. Compile the advertised schema with Ajv2020 and assert every case against it before separately checking runtime parity.

AGENTS.md reference: AGENTS.md:L23-L23

Useful? React with 👍 / 👎.

…dation

The previous test only inspected the final tool response, so it could not
detect the defect it was added for: a client-side schema rejection and a
server-side validation rejection both surface as an input error. Reverting the
contract to the parent superRefine implementation left the test passing.

Compile the input schema from tools/list with Ajv and compare its verdict with
runtime validation for each collection/selector pairing, then assert the
expected verdicts separately. The test now fails when the contract regresses:
`modules + index: advertised schema and runtime validation disagree`.
@yylm-lm

yylm-lm commented Oct 11, 2026

Copy link
Copy Markdown
Author

Thanks, this is correct and I verified it. Reverting the contract to the parent superRefine left the previous test passing, because a client-side schema rejection and a server-side validation rejection both surface as an input error.

The test now compiles the tools/list input schema with Ajv and compares its verdict with runtime validation per pairing, asserting each layer's expected verdict separately. Reverting the production contract now fails with:

modules + index: advertised schema and runtime validation disagree: expected true to be false

Pushed as 60954a7.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60954a7166

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +157 to +160
expect(
advertisedAcceptsCase,
`${entry.label}: advertised schema and runtime validation disagree`,
).toBe(runtimeAcceptsCase);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore the schema fix before asserting parity

For the modules + index case, this expectation is deterministically false: fresh evidence after the earlier review is that this revision now compiles advertised.inputSchema, but the final tree leaves itemViewSchema in src/domain/analysisView/analysisView.ts unchanged as the independent collection/selector union plus superRefine, which SDK conversion omits. Ajv therefore accepts this case while safeParse rejects it, so every focused/CI run fails here and the advertised-contract bug remains; retain a JSON-Schema-expressible production contract (such as the discriminated collection alternatives) alongside this test.

AGENTS.md reference: AGENTS.md:L23-L23

Useful? React with 👍 / 👎.

The previous commit staged the production file while it still held the
reverted superRefine implementation used to check that the new test fails
without the fix, so the committed tree dropped the contract change and the
advertised schema regressed to the state this pull request exists to repair.

Restore the discriminated collection alternatives. The strengthened test
fails again when this contract regresses, which is the property the revert
check established.
@yylm-lm

yylm-lm commented Oct 11, 2026

Copy link
Copy Markdown
Author

The P1 is correct and was my mistake. Commit 60954a71 staged analysisView.ts while it still held the reverted superRefine implementation I used to confirm the new test fails without the fix, so the committed tree dropped the contract change and the advertised schema regressed to exactly the state this pull request repairs.

Restored in 4cfb8d00. Verified on the committed tree, not the working tree:

  • git diff c21de4c6 HEAD -- src/domain/analysisView/analysisView.ts is empty, so the contract change is back
  • tsc --noEmit passes
  • the focused tests pass 9/9
  • reverting the production contract makes the new test fail with modules + index: advertised schema and runtime validation disagree, which is the property the revert check established

Lesson taken: a revert experiment must be undone before staging, and verification has to run against the committed tree rather than the working tree.

@yylm-lm

yylm-lm commented Oct 11, 2026

Copy link
Copy Markdown
Author

Closing: superseded by #1705, which merged today (a8a9c4d8) with the same fix — discriminatedUnion("collection", …) for inspect_analysis_view's item view, plus a tools/list + Ajv agreement test.

I verified the two contracts are semantically identical over 56 collection/selector cases (0 verdict differences), so the production change here would conflict with main and add nothing.

One thing this PR's test covers that #1705's does not: the sections + node_id and modules + empty path pairings. If a maintainer wants them, I can rebase and resubmit this as a test-only PR against #1705's toolSchemaValidity.test.ts instead.

@yylm-lm yylm-lm closed this Oct 11, 2026
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.

[Bug] inspect_analysis_view advertises selector combinations rejected by input validation

1 participant