feat(policy): add MCP-aware JSON-RPC L7 governance#1938
Conversation
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>
|
@krishicks - Mostly for your review. |
Signed-off-by: ddurst <267424412+ddurst-nvidia@users.noreply.github.com>
a432508 to
8c9606b
Compare
|
I don't have permissions to add the |
| max_body_bytes: 131072 | ||
| rules: | ||
| deny: | ||
| - mcp_method: tools/call |
There was a problem hiding this comment.
protocol is already mcp, so why do we need it to be mcp_method here vs just method?
There was a problem hiding this comment.
Good catch. Easy enough to remove, leaving unresolved until I can.
There was a problem hiding this comment.
Resolved. You'll need to talk to @krishicks regarding rpc_method from #1865, I'm only building on top of that work.
| mcp: | ||
| max_body_bytes: 131072 | ||
| rules: | ||
| deny: |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Particular reasons are:
- Support method-level governance for MCP tool calls (JSON-RPC) in sandbox policy #1793 's proposed design section.
- feat(l7): add JSON-RPC policy enforcement #1865 (comment)
But I'll address it more completely in the other comment.
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>
|
Cleaned up the conformance fixture workarounds to align with modelcontextprotocol/conformance#346. |
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 spelling, 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:
mcp_methodfilters MCP/JSON-RPC method names such astools/call.toolfilters MCP tool calls byparams.name.paramsmatchers allow policy to constrain scalar MCP params such asarguments.scope.Key Differences
protocol: mcpwhile preservingprotocol: json-rpc.mcp.max_body_bytesmcp_methodtoolparamsmaps for MCP readability.tower-mcp-typesto validate known MCP request/notification shapes.Rule / OPA Tightening
mcp_methodnormalizes to the existingrpc_methodfield, so OPA has one method-matching path.toolnormalizes to MCPparams.name, but only whenparams.namewas not explicitly set.protocol: json-rpcorprotocol: mcp.2025-11-25conformance compatibility path after host, path, binary, endpoint protocol, and parse-shape checks. MCP2026-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
1and"1"ortrueand"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: integerortype: booleanwithout weakening the existing JSON-RPC compatibility path.Conformance Notes
The MCP conformance policy template now uses:
Expected-failure carve-outs are limited to one known upstream conformance-client behavior:
Note
elicitation-sep1034-client-defaultsis allowed to fail because the pinned upstream TypeScript conformance client appears to advertiseelicitation.applyDefaults, while@modelcontextprotocol/sdk 1.29.0applies defaults only forelicitation.form.applyDefaults. The failure reproduces with the upstream conformance client directly, without OpenShell in the path. The carve-out should be removed whenOPENSHELL_MCP_CONFORMANCE_REFis bumped to an upstream commit where the bundled client advertises the corrected capability shape.Testing
mise run pre-commitpassesLocal validation run for this branch includes:
mise run e2e:mcpfor the MCP conformance suite.elicitation-sep1034-client-defaults.cargo test -p openshell-supervisor-network mcp_tool_deny_rule_blocks_tools_callcargo clippy -p openshell-supervisor-network --all-targets -- -D warningscargo test -p openshell-policycargo test -p openshell-supervisor-networkChecklist