feat(056): output-schema validation for proxied tool calls (Spec 054 Track A)#525
Open
Dumbris wants to merge 3 commits into
Open
feat(056): output-schema validation for proxied tool calls (Spec 054 Track A)#525Dumbris wants to merge 3 commits into
Dumbris wants to merge 3 commits into
Conversation
Related #521 Carve Track A of the Spec 054 security-gateway umbrella into its own feature: validate a tool's structuredContent against its declared outputSchema at the proxy boundary before it reaches the agent. ## Changes - spec.md: FR-A1..A12, 3 prioritized user stories, edge cases, success criteria - plan.md: pure internal/outputvalidation pkg + forwardContentResult hook design - research.md: santhosh-tekuri/jsonschema/v6, capture point, modes, cache decisions - data-model.md, contracts/validator.md, quickstart.md - tasks.md: 24 TDD-first tasks organized by user story
…Track A) Related #521 Validate a tool's structured response against its declared outputSchema at the proxy boundary before it reaches the agent, so a buggy or compromised upstream cannot inject malformed/oversized/unexpected data into the agent's context. Track A of the Spec 054 security-gateway umbrella. ## Changes - internal/outputvalidation: new pure package — Validator with a per-tool compiled-schema sync.Map cache (santhosh-tekuri/jsonschema/v6), byte-size and nesting-depth guards run before validation, uncompilable schemas degrade to a no-op. Never mutates the payload. - internal/config: OutputValidationConfig (mode off/warn/strict, default warn; max_bytes; max_depth; missing_structured_content) + ToolMetadata.OutputSchemaJSON. - internal/upstream/core/client.go: capture tool.RawOutputSchema/OutputSchema at discovery into ToolMetadata.OutputSchemaJSON (FR-A1). - internal/runtime/{stateview,supervisor}: propagate OutputSchemaJSON onto the in-memory ToolInfo snapshot for cheap call-time lookup. - internal/server: applyOutputValidation wired into both handleCallToolVariant forward sites; pure evaluateOutputValidation decision core; strict blocks with an error result, warn forwards + records a policy_decision audit entry (reuses emitActivityPolicyDecision). No build-tag-specific behaviour. - promote santhosh-tekuri/jsonschema/v6 to a direct dependency. - docs/features/output-schema-validation.md; e2e stub MCP server. Design note: validation runs in mcp.go on forwardContentResult's output (StructuredContent is unaffected by truncation) rather than inside forwardContentResult, keeping that function pure. ## Testing - Unit: internal/outputvalidation (19 tests, validator + guards, -race); internal/config (8 tests); internal/server output_validation (11 tests covering every decision branch incl. ContextForge #4042 trap, guard breach). - E2E (curl + CLI, fresh data-dir, stub MCP server declaring an outputSchema): strict blocks a violating structuredContent with "at '/id': got string, want integer" + a blocked policy_decision; conforming passes; text-only (no structured) passes under strict+allow; warn mode forwards the violation unchanged + a warning policy_decision. Both editions build.
Related #521 make swagger-verify regenerates oas/ from struct annotations; the new config.OutputValidationConfig model and the output_validation field on the Config schema are now documented.
Deploying mcpproxy-docs with
|
| Latest commit: |
45e0f47
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://10c0f4c3.mcpproxy-docs.pages.dev |
| Branch Preview URL: | https://056-output-schema-validation.mcpproxy-docs.pages.dev |
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
📦 Build ArtifactsWorkflow Run: View Run Available Artifacts
How to DownloadOption 1: GitHub Web UI (easiest)
Option 2: GitHub CLI gh run download 26416222217 --repo smart-mcp-proxy/mcpproxy-go
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Track A of the Spec 054 MCP-security-gateway umbrella, carved into its own feature (Spec 056). When an upstream tool declares an
outputSchema, mcpproxy now validates the tool's structured response against that schema at the proxy boundary before it reaches the agent — closing the only completely-empty axis of the security story ("validated data out"). A buggy or compromised server can no longer inject malformed/oversized/unexpected structured data into the agent's context.Spec/plan/tasks:
specs/056-output-schema-validation/.Behaviour (backward-compatible by default)
offwarn(default)policy_decisionauditstrictmissing_structured_content)structuredContentis forwarded byte-for-byte (validate a read-only view, never strip-then-validate).Key changes
internal/outputvalidation— new pure package:Validatorwith a per-tool compiled-schemasync.Mapcache (santhosh-tekuri/jsonschema/v6), guards,Verdict. No server/storage deps.internal/config—OutputValidationConfig(mode/max_bytes/max_depth/missing_structured_content, nil-safe helpers, default warn) +ToolMetadata.OutputSchemaJSON.internal/upstream/core/client.go— captureRawOutputSchema/OutputSchemaat discovery (FR-A1).internal/runtime/{stateview,supervisor}— propagateOutputSchemaJSONonto the in-memory snapshot for cheap call-time lookup (no per-call index query).internal/server—applyOutputValidationwired into bothhandleCallToolVariantforward sites; pureevaluateOutputValidationdecision core; reusesemitActivityPolicyDecision. Identical in personal + server editions.Design note: validation runs in
mcp.goonforwardContentResult's output (itsStructuredContentis untouched by truncation) rather than insideforwardContentResult, keeping that function pure. So the test coverage lives ininternal/server/output_validation_test.gorather than acontent_forward_test.go.Testing
internal/outputvalidation(19, validator + guards,-race);internal/config(8);internal/serveroutput-validation (11 — every decision branch incl. #4042 trap, guard breach, uncompilable schema).golangci-lintclean on new files; both editions build.e2e/stubs/outputschema):bad_outputblocked:output schema validation failed: ... at '/id': got string, want integer, with ablockedpolicy_decisionrecord (verified viaGET /api/v1/activity?type=policy_decision)conformingpasses;text_only(no structured, allow) passesbad_outputforwarded unchanged ({"id":"not-an-int"}, isError:false) + awarningpolicy_decisionrecordOut of scope (other Spec 054 tracks)
Output sanitisation (B), per-tool ACLs (C), TOFU pinning of schemas/annotations (D), audit hash chain (E).
Related #521