Skip to content

docs(054): MCP security gateway hardening — umbrella spec (5 tracks)#521

Open
Dumbris wants to merge 2 commits into
mainfrom
054-mcp-security-gateway
Open

docs(054): MCP security gateway hardening — umbrella spec (5 tracks)#521
Dumbris wants to merge 2 commits into
mainfrom
054-mcp-security-gateway

Conversation

@Dumbris
Copy link
Copy Markdown
Member

@Dumbris Dumbris commented May 24, 2026

Umbrella spec to turn mcpproxy into the reference open-source MCP security gateway, decomposed into 5 independently-shippable tracks. Grounded in a subagent gap-analysis against existing features (not greenfield) — the Context section maps each track to the Spec it extends, with file pointers.

Track Builds on Status today
A (P1) Output-schema validation new; content_forward.go hook the only empty axis — zero output validation today
B (P2) Output sanitisation enforcement Specs 026, 035 signals computed but log-only, discarded before reaching the agent
C (P3) Per-tool / per-arg capability ACLs Spec 028 tokens scope per-server × per-operation only
D (P4) TOFU pinning hardening Spec 032 pins desc+inputSchema; misses outputSchema, annotations, provenance
E (P5) EU AI Act Art. 12-aligned audit logging Specs 016/021/024 strong log, no tamper-evidence; retention < 6-month floor

Key reframe: mcpproxy is already ~70% of a reference security gateway — most tracks close gaps on shipped features. Recommended sequencing A→E by leverage × effort; each track gets its own /speckit.plan when implementation starts.

Non-goals: no legal compliance certification (alignment/support only), no MCP protocol changes, no mandatory content mutation (sanitisation opt-in), personal edition unaffected by default.

Spec-only; no code. Quality checklist passes (no NEEDS CLARIFICATION). Plan to be run later.

Dumbris added 2 commits May 23, 2026 14:30
…g release.yml job

release.yml already has an 'mcp-registry' job that publishes server.json on every
tag via keyless GitHub OIDC (continue-on-error) — it has shipped 44 versions to
registry.modelcontextprotocol.io. The publish-mcp-registry.yml added in #517 was a
redundant duplicate that would have thrown 'cannot publish duplicate version' on
every release. Removes it and corrects docs/mcp-registry-publishing.md to reference
the existing automation instead of claiming publishing was manual.
Related #N/A

Umbrella spec decomposing the 'reference OSS MCP security gateway' roadmap into
5 independently-shippable tracks (output-schema validation, output sanitisation
enforcement, per-tool/per-arg capability ACLs, TOFU pinning hardening, EU AI Act
Article 12-aligned tamper-evident audit logging), each grounded in a gap analysis
against existing features (Specs 026/028/032/035, activity log).
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2134d99
Status: ✅  Deploy successful!
Preview URL: https://a0cdf4cd.mcpproxy-docs.pages.dev
Branch Preview URL: https://054-mcp-security-gateway.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: 054-mcp-security-gateway

Available Artifacts

  • archive-darwin-amd64 (27 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (27 MB)
  • archive-windows-arm64 (24 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (20 MB)
  • installer-dmg-darwin-arm64 (18 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 26351797912 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

Dumbris added a commit that referenced this pull request May 28, 2026
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
Dumbris added a commit that referenced this pull request May 28, 2026
…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.

- 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.

- 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.
Dumbris added a commit that referenced this pull request May 28, 2026
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.
Dumbris added a commit that referenced this pull request May 28, 2026
…Track A) (#525)

* docs(056): output-schema-validation spec, plan, tasks (Spec 054 Track A)

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

* feat(056): output-schema validation for proxied tool calls (Spec 054 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.

- 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.

- 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.

* docs(056): regenerate OpenAPI spec for output_validation config

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.
Dumbris added a commit that referenced this pull request May 29, 2026
Make mcpproxy's existing-but-discarded content-trust classification
(Spec 035) and secret detector (Spec 026) actually contain untrusted
tool output, instead of only logging. All behaviour is fully opt-in;
default config forwards every response byte-for-byte.

- spotlight: wrap untrusted (open-world) tool text in source-identifying
  «untrusted:server/tool» delimiters, escaping the sentinel so content
  cannot forge the wrapper (FR-B1/B2). Applied post-truncation, not cached.
- redact: mask detected secrets as [REDACTED:<category>] reusing the
  Spec 026 detector (FR-B3).
- strip: neutralise ANSI / C0-C1 / zero-width / bidi sequences on
  untrusted text, per-class toggles (FR-B4).
- block: replace the payload with a remediation error on a critical
  detection (FR-B7).

Redact/strip/block run on the raw result BEFORE forwardContentResult
truncates and caches it, so read_cache never stores an unredacted secret
and a blocked response is never cached. Non-text blocks are untouched
(FR-B5). Mutating actions emit a policy_decision activity record.

New OutputSanitisationConfig mirrors OutputValidationConfig (Track A).
Verified end-to-end: curl/MCP roundtrip (spotlight/redact/strip/block +
read_cache), API E2E (65/65), and the Web UI activity view.

Relates to Spec 054 Track B (#521).
Dumbris added a commit that referenced this pull request May 29, 2026
…nt (#535)

Make mcpproxy's existing-but-discarded content-trust classification
(Spec 035) and secret detector (Spec 026) actually contain untrusted
tool output, instead of only logging. All behaviour is fully opt-in;
default config forwards every response byte-for-byte.

- spotlight: wrap untrusted (open-world) tool text in source-identifying
  «untrusted:server/tool» delimiters, escaping the sentinel so content
  cannot forge the wrapper (FR-B1/B2). Applied post-truncation, not cached.
- redact: mask detected secrets as [REDACTED:<category>] reusing the
  Spec 026 detector (FR-B3).
- strip: neutralise ANSI / C0-C1 / zero-width / bidi sequences on
  untrusted text, per-class toggles (FR-B4).
- block: replace the payload with a remediation error on a critical
  detection (FR-B7).

Redact/strip/block run on the raw result BEFORE forwardContentResult
truncates and caches it, so read_cache never stores an unredacted secret
and a blocked response is never cached. Non-text blocks are untouched
(FR-B5). Mutating actions emit a policy_decision activity record.

New OutputSanitisationConfig mirrors OutputValidationConfig (Track A).
Verified end-to-end: curl/MCP roundtrip (spotlight/redact/strip/block +
read_cache), API E2E (65/65), and the Web UI activity view.

Relates to Spec 054 Track B (#521).
@Dumbris
Copy link
Copy Markdown
Member Author

Dumbris commented Jun 1, 2026

Critic (Codex) review — Dumbris's PR #521
Verdict: accept
Strengths: The workflow deletion is backed by the existing mcp-registry job in .github/workflows/release.yml, and the added Spec 054 umbrella remains planning-only with independently shippable security tracks. All checks are green at head 2134d99b0ad746032de68cac275a97d262277efb.
Findings: none.
Provenance check: ok

@Dumbris
Copy link
Copy Markdown
Member Author

Dumbris commented Jun 1, 2026

Critic (Codex) review — Dumbris's PR #521
Verdict: request_changes
Head: 2134d99b0ad746032de68cac275a97d262277efb

Strengths: The specs/054-mcp-security-gateway/ umbrella spec is well decomposed into independently shippable tracks, with explicit non-goals and a quality checklist.

Findings:

  1. The PR scope is not limited to the advertised security-gateway umbrella spec. Alongside specs/054-mcp-security-gateway/spec.md, the diff also updates MCP registry publishing documentation (docs/mcp-registry-publishing.md:5, docs/mcp-registry-publishing.md:95) and deletes .github/workflows/publish-mcp-registry.yml in the PR diff. Those release/registry-pipeline changes are unrelated to Track A–E security hardening and should be split into their own PR or explicitly tied to the approved design gate for spec 054. This is especially important because deleting a workflow is operationally meaningful even if .github/workflows/release.yml:1659 already contains an mcp-registry job.

Checks: gh pr checks 521 --watch=false is green for all non-skipped checks.

Provenance check: ok

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