feat(policy): add MCP-aware JSON-RPC L7 governance#1938
Conversation
Add a reusable MCP conformance workflow that runs upstream client scenarios through an OpenShell sandbox. Add a client image, wrapper, policy template, and expected-failures baseline for expanding MCP conformance coverage. Remove stale JSON-RPC e2e policy fields that are no longer accepted. Signed-off-by: Kris Hicks <khicks@nvidia.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
MCP elicitation sends JSON-RPC result messages from the client back to the server without a method field. Treat those as valid response-only POSTs once the endpoint has already matched so method allowlists keep applying to request calls. Also wire the MCP conformance runner into the e2e task and keep known upstream client gaps in the expected-failure list. Signed-off-by: Kris Hicks <khicks@nvidia.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>
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. |
0e4d7af to
fb9c396
Compare
|
Label |
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:
methodfilters MCP method names such asinitialize,tools/list, andtools/callwhenprotocol: mcp.toolfilters MCP tool calls byparams.name.paramsmatchers allow policy to constrain scalar MCP params such asarguments.scope.deny_rulescan block specific MCP calls even when broader allow rules match.Key Differences
protocol: mcpwhile preservingprotocol: json-rpc.mcp.max_body_bytesmethodtoolparamsmaps for MCP readability.rulesplus siblingdeny_rulespolicy shape for MCP, aligned with the other L7 protocols.tower-mcp-typesto validate known MCP request/notification shapes.Rule / OPA Tightening
methodnormalizes to the existing internal method-matching field, so OPA has one JSON-RPC-family method 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 empty:
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-defaultsandsse-retryexercise the intended MCP paths through OpenShell. Remove this workaround whenOPENSHELL_MCP_CONFORMANCE_REFpoints at an upstream release containing that fix.Testing
mise run pre-commitpassesLocal validation run for this branch includes:
mise run pre-commitcargo test -p openshell-policycargo test -p openshell-supervisor-network mcp_tool_deny_rule_blocks_tools_callcargo test -p openshell-supervisor-network l7_mcpOPENSHELL_DOCKER_SUPERVISOR_BIN=deploy/docker/.build/prebuilt-binaries/amd64/openshell-sandbox mise run e2e:mcpinitialize,tools_call,elicitation-sep1034-client-defaults,sse-retryChecklist