Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
5e35196
fix(l7): relay unframed SSE responses
krishicks Jun 16, 2026
ebe3c4b
test(e2e): add JSON-RPC L7 proxy coverage
krishicks Jun 10, 2026
9fd091a
feat(policy): recognize JSON-RPC L7 endpoints
krishicks Jun 10, 2026
6cc312e
refactor(l7): share HTTP body inspection helper
krishicks Jun 10, 2026
6b2b124
feat(l7): enforce JSON-RPC method rules
krishicks Jun 10, 2026
be8d893
fix(l7): honor JSON-RPC body size config
krishicks Jun 11, 2026
1ebf7e2
feat(l7): match JSON-RPC params in rules
krishicks Jun 10, 2026
c9a5498
feat(l7): support JSON-RPC batch calls
krishicks Jun 10, 2026
d8fbf1f
fix(l7): redact JSON-RPC params in logs
krishicks Jun 10, 2026
783a491
docs(policy): document JSON-RPC L7 rules
krishicks Jun 10, 2026
7152e91
fix(sandbox): fail closed on ambiguous JSON-RPC requests
krishicks Jun 15, 2026
6f8e8e4
ci(e2e): add MCP conformance coverage
krishicks Jun 15, 2026
3730691
fix(l7): port JSON-RPC L7 to supervisor network
krishicks Jun 15, 2026
cda7955
fix(l7): allow JSON-RPC response messages
krishicks Jun 16, 2026
9fe46d5
feat(policy): add MCP policy profile
ddurst-nvidia Jun 16, 2026
692ce87
refactor(mcp): parse calls with tower protocol types
ddurst-nvidia Jun 16, 2026
fe173e8
feat(policy): support nested MCP params matchers
ddurst-nvidia Jun 16, 2026
dcf548c
docs(policy): comment MCP policy internals
ddurst-nvidia Jun 16, 2026
55f962d
fix(policy): satisfy MCP clippy checks
ddurst-nvidia Jun 16, 2026
24f592b
test(mcp): cover relay deny by tool
ddurst-nvidia Jun 16, 2026
51960be
fix(mcp): permit streamable http control traffic
ddurst-nvidia Jun 16, 2026
dd4cedc
fix(mcp): align conformance fixture workarounds
ddurst-nvidia Jun 18, 2026
b5e3936
refactor(policy): align mcp method syntax
ddurst-nvidia Jun 18, 2026
1d8d4af
refactor(policy): align mcp deny rules
ddurst-nvidia Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ jobs:
cmd: "mise run --no-deps --skip-deps e2e:podman:rootless"
apt_packages: "openssh-client podman uidmap"
rootless: true
- suite: mcp
cmd: "mise run --no-deps --skip-deps e2e:mcp"
apt_packages: ""
container:
image: ghcr.io/nvidia/openshell/ci:latest
credentials:
Expand All @@ -66,6 +69,23 @@ jobs:
with:
ref: ${{ inputs['checkout-ref'] || github.sha }}

- name: Check out MCP conformance tests
if: matrix.suite == 'mcp'
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
repository: modelcontextprotocol/conformance
# Pin after v0.1.16 to include the tools_call client scenario fix.
ref: b9041ea41b0188581803459dbae71bc7e02fd995
path: .cache/mcp-conformance

- name: Set up Node.js
if: matrix.suite == 'mcp'
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: "22"
cache: npm
cache-dependency-path: .cache/mcp-conformance/package-lock.json

- name: Install OS test dependencies
if: matrix.apt_packages != ''
env:
Expand Down Expand Up @@ -104,6 +124,7 @@ jobs:
- name: Run tests
env:
OPENSHELL_SUPERVISOR_IMAGE: ${{ format('ghcr.io/nvidia/openshell/supervisor:{0}', inputs.image-tag) }}
OPENSHELL_MCP_CONFORMANCE_CLIENT_IMAGE: ${{ format('openshell-mcp-conformance-client:{0}', inputs.image-tag) }}
E2E_CMD: ${{ matrix.cmd }}
run: |
if [ "${{ matrix.rootless }}" = "true" ]; then
Expand Down
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = ["crates/*"]
[workspace.package]
version = "0.0.0"
edition = "2024"
rust-version = "1.88"
rust-version = "1.90"
license = "Apache-2.0"
repository = "https://github.com/NVIDIA/OpenShell"

Expand Down Expand Up @@ -73,6 +73,7 @@ serde_json = "1"
serde_yml = "0.0.12"
toml = "0.8"
apollo-parser = "0.8.5"
tower-mcp-types = "0.12.0"

# HTTP client
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls-native-roots"] }
Expand Down
9 changes: 9 additions & 0 deletions architecture/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ paths, such as proxy support files or GPU device paths when a GPU is present.
All ordinary agent egress is routed through the sandbox proxy. The proxy
identifies the calling binary, checks trust-on-first-use binary identity, rejects
unsafe internal destinations, and evaluates the active policy.
For inspected HTTP traffic, the proxy can enforce REST method/path rules,
WebSocket upgrade and text-message rules, GraphQL operation rules, and
MCP or generic JSON-RPC method and params rules on sandbox-to-server request
bodies. MCP and JSON-RPC inspection buffers up to the endpoint
`mcp.max_body_bytes` or `json_rpc.max_body_bytes` limit. Literal dotted keys in
JSON-RPC params are rejected before policy evaluation so they cannot be confused
with flattened nested selector paths. JSON-RPC responses and server-to-client
MCP messages on response or SSE streams are relayed but are not currently
parsed for policy enforcement.

`https://inference.local` is special. It bypasses OPA network policy and is
handled by the inference interception path:
Expand Down
4 changes: 4 additions & 0 deletions crates/openshell-cli/src/policy_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ fn group_allow_rules(specs: &[String]) -> Result<BTreeMap<(String, u32), Vec<L7R
operation_type: String::new(),
operation_name: String::new(),
fields: Vec::new(),
rpc_method: String::new(),
params: HashMap::default(),
}),
});
}
Expand All @@ -226,6 +228,8 @@ fn group_deny_rules(specs: &[String]) -> Result<BTreeMap<(String, u32), Vec<L7De
operation_type: String::new(),
operation_name: String::new(),
fields: Vec::new(),
rpc_method: String::new(),
params: HashMap::default(),
});
}
Ok(grouped)
Expand Down
Loading
Loading