Skip to content

fix(quarantine): expose output schema in tool diff endpoint (MCP-2085)#638

Merged
Dumbris merged 1 commit into
mainfrom
fix/mcp-2085-tool-diff-output-schema
Jun 12, 2026
Merged

fix(quarantine): expose output schema in tool diff endpoint (MCP-2085)#638
Dumbris merged 1 commit into
mainfrom
fix/mcp-2085-tool-diff-output-schema

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 12, 2026

Copy link
Copy Markdown
Member

Summary

Fixes the backend half of MCP-2085. The tool-quarantine change-detection hash covers description + inputSchema + outputSchema (internal/runtime/tool_quarantine.go), but GET /api/v1/servers/{id}/tools/{tool}/diff only returned previous_description/current_description and previous_schema/current_schema. It omitted the output-schema fields entirely, even though they already exist on ToolApprovalRecord and are part of the hash.

Consequence: when an upstream evolves only its output schema — e.g. Google sqladmin adding "POSTGRES_20" to a database-version enum — the description and input schema stay byte-identical, so the diff the operator sees is empty. The changed flag then reads as a phantom rug-pull false positive and erodes trust in the feature.

Change

  • internal/httpapi/server.gohandleGetToolDiff now also returns previous_output_schema / current_output_schema.
  • docs/api/rest-api.md — document the two new response fields.
  • internal/httpapi/tool_quarantine_test.go — regression test TestHandleGetToolDiff_OutputSchemaOnlyChange (the create_backup case: identical desc + input schema, output schema gains POSTGRES_20), plus assertions on the existing test that input-schema fields are surfaced.

Design decision (Part 1 of the issue)

The issue offered options including auto-approving "additive enum widenings". I deliberately did not weaken the rug-pull hash:

  • Change-detection is technically correct — the upstream schema genuinely changed (the issue confirms the records are internally consistent and both hashes reproduce).
  • Semantic additive-vs-removal diffing of JSON schema is error-prone, and this is a security feature.
  • The real defect is invisibility. Once the diff surfaces "output schema changed (new POSTGRES_20 enum)", the operator can approve in one click with full context.

So the backend fix is transparency, not relaxing security.

Out of scope (delegated)

The frontend rendering half — frontend/src/views/ServerDetail.vue showing three diff sections (Description / Input Schema / Output Schema) and the option-(b) batch "schema updated on N tools" review — is a separate lane and tracked as a child issue for the Frontend engineer. This PR is the enabling backend change it depends on.

Verification

  • go test ./internal/httpapi/ -race
  • go test ./internal/runtime/ -run TestCalculateToolApprovalHash_Stability ✅ (hash canary unchanged)
  • go build ./cmd/mcpproxy
  • ./scripts/run-linter.sh → 0 issues ✅

Related #634
Related MCP-2085

The tool-quarantine change-detection hash covers description, input schema,
and output schema, but GET /api/v1/servers/{id}/tools/{tool}/diff only
returned the description and input schema. When an upstream evolved only its
output schema (e.g. Google sqladmin adding a "POSTGRES_20" enum value), the
visible description was byte-identical, so the flagged tool looked like a
phantom rug-pull false positive.

Add previous_output_schema / current_output_schema (already stored on the
ToolApprovalRecord) to the diff response so an operator can see exactly what
changed before approving. Document the field in docs/api/rest-api.md and add
a regression test for an output-schema-only change.

Note: change-detection itself is correct — the upstream schema genuinely
changed. This deliberately does NOT weaken the rug-pull hash (no
auto-approval of additive enum widenings); the fix is transparency, not
relaxing security.

Related #634
Related MCP-2085
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: d141891
Status: ✅  Deploy successful!
Preview URL: https://ac9a68f6.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-mcp-2085-tool-diff-outpu.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: fix/mcp-2085-tool-diff-output-schema

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (24 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 27401232135 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@Dumbris Dumbris merged commit ac954e5 into main Jun 12, 2026
38 checks passed
Dumbris added a commit that referenced this pull request Jun 12, 2026
…UI (#639)

The tool-quarantine approval UI flagged a tool as "changed" whenever its
description, input schema, OR output schema hash differed from the approved
version, but rendered only the description diff. A schema-only change was
therefore an invisible phantom diff — the operator saw a "changed" badge with
no visible reason.

Render up to three labeled before/after sections (Description, Input Schema,
Output Schema), each shown only when that field actually changed, consuming
the previous_output_schema / current_output_schema fields the diff endpoint
now exposes (PR #638). Schema bodies are pretty-printed before the word-diff
so additive changes (e.g. a new enum value) read clearly. Backend keeps
rug-pull detection strict; this change is transparency only.

Diff-section selection is extracted into computeToolDiffSections (utils/toolDiff)
with a vitest covering the output-schema-only case.

Related MCP-2096, MCP-2085
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.

2 participants