Skip to content

feat(policy): add MCP-aware JSON-RPC L7 governance#1938

Open
ddurst-nvidia wants to merge 10 commits into
NVIDIA:hicks/push-nvuozlywzuwufrom
ddurst-nvidia:dev/ddurst/mcp-tower-types-variant-3
Open

feat(policy): add MCP-aware JSON-RPC L7 governance#1938
ddurst-nvidia wants to merge 10 commits into
NVIDIA:hicks/push-nvuozlywzuwufrom
ddurst-nvidia:dev/ddurst/mcp-tower-types-variant-3

Conversation

@ddurst-nvidia

@ddurst-nvidia ddurst-nvidia commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Builds on #1865 by keeping its shared JSON-RPC-family L7 enforcement foundation and adding an MCP-specific policy surface on top of it.

PR #1865 introduces generic JSON-RPC method/params enforcement. This variant preserves generic JSON-RPC as a first-class supported protocol for non-MCP services, while adding MCP-specific policy syntax, MCP validation via tower-mcp-types, and conformance-oriented e2e behavior.

Related Issue

Addresses #1793.

Builds on #1865.

Changes

Relationship to #1865

PR #1865 adds the shared JSON-RPC-family L7 enforcement layer. This branch builds on that layer with an MCP-specific policy surface and MCP message validation via tower-mcp-types, while preserving generic JSON-RPC as a first-class supported protocol for non-MCP services.

How This Addresses #1793

Issue #1793 asks for method-level governance for MCP tool calls because MCP traffic is carried as JSON-RPC over a single allowed connection, making network-only policy all-or-nothing.

This branch addresses that by allowing policy to distinguish MCP requests inside the connection:

  • method filters MCP method names such as initialize, tools/list, and tools/call when protocol: mcp.
  • tool filters MCP tool calls by params.name.
  • Nested params matchers allow policy to constrain scalar MCP params such as arguments.scope.
  • deny_rules can block specific MCP calls even when broader allow rules match.
  • Allow and deny rules are evaluated before forwarding to the MCP server.
  • MCP traffic remains in OpenShell's existing policy/audit path instead of requiring an external governance gateway.

Key Differences

  • Adds protocol: mcp while preserving protocol: json-rpc.
  • Adds MCP policy fields:
    • mcp.max_body_bytes
    • method
    • tool
    • nested params maps for MCP readability.
  • Uses the existing L7 rules plus sibling deny_rules policy shape for MCP, aligned with the other L7 protocols.
  • Uses tower-mcp-types to validate known MCP request/notification shapes.
  • Keeps generic JSON-RPC parsing intentionally protocol-neutral for non-MCP JSON-RPC servers.
  • Keeps enforcement on the existing OPA path by compiling MCP policy syntax back to the current JSON-RPC-family rule model.

Rule / OPA Tightening

  • MCP method normalizes to the existing internal method-matching field, so OPA has one JSON-RPC-family method path.
  • tool normalizes to MCP params.name, but only when params.name was not explicitly set.
  • Nested MCP params are flattened before OPA evaluation, preserving the existing scalar matcher behavior.
  • Literal dotted request param keys are rejected to avoid ambiguous collisions with flattened nested paths.
  • JSON-RPC/MCP params matching remains restricted to protocol: json-rpc or protocol: mcp.
  • MCP request parsing validates known MCP method params before policy evaluation.
  • Methodless MCP Streamable HTTP receive streams and client JSON-RPC responses are allowed only as a narrow 2025-11-25 conformance compatibility path after host, path, binary, endpoint protocol, and parse-shape checks. MCP 2026-07-28 (draft) removes the GET stream endpoint and client-sent JSON-RPC responses, so this allowance should be version-gated or removed when OpenShell enforces that transport version.

Type Enforcement Follow-up

This branch does not add typed MCP argument enforcement. The current L7 JSON-RPC-family policy input flattens scalar params into string values before OPA evaluation, so 1 and "1" or true and "true" are not distinguishable by policy today.

Typed argument enforcement would be better handled as a wider L7 policy input change, for example by preserving scalar JSON type metadata alongside the existing flattened matcher keys. That would let a future MCP argument policy express type: integer or type: boolean without weakening the existing JSON-RPC compatibility path.

Conformance Notes

The MCP conformance policy template now uses:

protocol: mcp
mcp:
  max_body_bytes: 131072
rules:
  - allow:
      method: "*"

Expected-failure carve-outs are empty:

client: []
server: []

Note

The wrapper still carries a temporary workaround for modelcontextprotocol/conformance#345 while pinned to an upstream ref where the bundled TypeScript client fixtures drift from the runner scenarios. The wrapper patches the local checkout before building the client image so elicitation-sep1034-client-defaults and sse-retry exercise the intended MCP paths through OpenShell. Remove this workaround when OPENSHELL_MCP_CONFORMANCE_REF points at an upstream release containing that fix.

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

Local validation run for this branch includes:

  • mise run pre-commit
  • cargo test -p openshell-policy
  • cargo test -p openshell-supervisor-network mcp_tool_deny_rule_blocks_tools_call
  • cargo test -p openshell-supervisor-network l7_mcp
  • OPENSHELL_DOCKER_SUPERVISOR_BIN=deploy/docker/.build/prebuilt-binaries/amd64/openshell-sandbox mise run e2e:mcp
    • Passed: initialize, tools_call, elicitation-sep1034-client-defaults, sse-retry
    • Failed: none

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
@ddurst-nvidia

Copy link
Copy Markdown
Contributor Author

@krishicks - Mostly for your review.

Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
@ddurst-nvidia ddurst-nvidia force-pushed the dev/ddurst/mcp-tower-types-variant-3 branch from a432508 to 8c9606b Compare June 16, 2026 23:27
@ddurst-nvidia

Copy link
Copy Markdown
Contributor Author

I don't have permissions to add the test:e2e label, but it would be appreciated if someone could add it.

Comment thread docs/reference/policy-schema.mdx Outdated
max_body_bytes: 131072
rules:
deny:
- mcp_method: tools/call

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

protocol is already mcp, so why do we need it to be mcp_method here vs just method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch. Easy enough to remove, leaving unresolved until I can.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Resolved. You'll need to talk to @krishicks regarding rpc_method from #1865, I'm only building on top of that work.

Comment thread docs/reference/policy-schema.mdx Outdated
mcp:
max_body_bytes: 131072
rules:
deny:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this would be a deviation from a separate deny_rules block that sits at the same level as rules, is there a particular reason for that?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Particular reasons are:

But I'll address it more completely in the other comment.

Comment thread docs/reference/policy-schema.mdx Outdated
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@ddurst-nvidia

Copy link
Copy Markdown
Contributor Author

Cleaned up the conformance fixture workarounds to align with modelcontextprotocol/conformance#346.

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