MCPGuard-Dynamic

May 27, 2026 · View on GitHub

Kernel-level sandboxing for LLM agent tool calls made through the Model Context Protocol (MCP).

MCPGuard sits as a transparent proxy between an MCP client (the agent / runner) and an MCP server subprocess, applying three layered defenses to every tool invocation. The lowest layer is implemented in eBPF and enforces capability policies at the system-call boundary, so a malicious MCP server cannot bypass policy by hardcoding sensitive behavior inside its own implementation.

This repository contains the proxy, the eBPF programs, the 14-server / 82-case benchmark, and the evaluation harness used in the accompanying paper Kernel-Level Sandboxing for LLM Agent Tool Calls via eBPF.

Architecture

LayerComponentPurpose
L1proxy/policy_engine.pyPer-server capability policy derived from each tool's MCP schema; allowlists for paths, network destinations, processes, env vars.
L2proxy/argument_validator.pyApplication-level inspection of tool-call arguments: path canonicalization, URL validation, prompt-injection detection, env-leak / command-injection detection, sensitive-key scanning, response sanitization.
L3ebpf/*.bpf.c + proxy/ebpf_sandbox.pyOS-level enforcement: three BPF LSM programs (file_guard, net_guard, proc_guard) intercept open() / connect() / execve(), and one tracepoint program (fork_guard) tracks child processes via sched_process_fork so policy carries across forks.

Six switchable defense configurations (proxy/proxy_base.py) cover the ablation space used in the paper: C0 (passthrough), C-AB (AgentBound baseline), C-app (L1 + L2), C-ebpf (L3 only), C-full (L1 + L2 + L3), C-AB+ebpf (AgentBound + L3).

Repository Layout

.
├── proxy/        L1 policy engine, L2 argument validator, L3 eBPF controller, AgentBound baseline
├── ebpf/         BPF C sources for file/net/proc/fork guards + Makefile + vmlinux.h
├── policies/     Per-server JSON capability policies (defaults + overrides)
├── servers/      14 MCP servers: 11 Python (filesystem, notes, weather, shell, sqlite, git, env + malicious/trojan variants) + 3 JavaScript (servers/js/)
├── test_cases/   82 benchmark scenarios across 7 categories (file_read, exfiltration, env_leak, sandbox_escape, priv_escalation, cross_language, benign)
├── notes_data/   170 valid synthetic notes JSON fixtures used by notes_server
├── runner/       evaluate.py, aggregate.py, agentbound_check.py, ebpf_edge_tests.py, latency_benchmark.py, override_workflow.py, smoke_test.py
└── EXECUTION_PLAN.md   Phase-by-phase reproduction instructions

Requirements

  • Linux kernel 6.x with BPF LSM enabled (CONFIG_BPF_LSM=y, lsm=bpf in kernel cmdline)
  • clang 21 or newer with BPF target
  • bpftool for loading BPF programs and maps
  • Python 3.12 (standard library only — no third-party deps)
  • Node.js 16+ (only for the JavaScript MCP servers under servers/js/)

Quick Start

# Build the eBPF programs
cd ebpf && make && cd ..

# Smoke test (one server, a handful of cases)
python3 runner/smoke_test.py

# Full benchmark for one configuration
python3 runner/evaluate.py --config C-full --run-id trial

# Aggregate a reproduced run
python3 runner/aggregate.py --run-id trial

# Reproduce the steady-state latency table after installing eBPF
sudo python3 runner/latency_benchmark.py --run-id codex_20260523_latency --iterations 100 --warmup 20

# Reproduce the audit/override workflow
python3 runner/override_workflow.py --run-id codex_20260523_override

# Run focused eBPF edge tests after installing eBPF
sudo python3 runner/ebpf_edge_tests.py --run-id codex_20260523_ebpf_edges

# Run AgentBound-style baseline conformance checks
python3 runner/agentbound_check.py --run-id codex_20260523_agentbound

C-ebpf, C-full, and C-AB+ebpf now fail closed if the BPF LSM programs and pinned maps are unavailable. Run them only after installing the eBPF layer with root privileges.

Headline Results

Attack Prevention Rate (APR), viable-attack APR (V-APR), and False Positive Rate (FPR) across the paper-pinned codex_20260523_full 14-server, 82-case benchmark:

ConfigAPRV-APRViable blockedFPR
C021.3%0.0%0/480/21
C-AB37.7%20.8%10/480/21
C-app42.6%27.1%13/480/21
C-ebpf60.7%50.0%24/480/21
C-full68.9%60.4%29/480/21
C-AB+ebpf67.2%58.3%28/480/21

APR includes attacks that fail intrinsically under C0. V-APR uses only attacks that succeed under C0 as the denominator, which is the more conservative measure of defense coverage.

The repeated benign-call latency benchmark is stored in the internal paper workspace as results/latency/codex_20260523_latency.md. It measures BN-01 (filesystem_server.read_file) with 20 warmup calls and 100 measured calls per configuration; C-full adds +0.488 ms median latency relative to C0 and remains at 1.141 ms p95 for this fixed benign call. The public CodeSync tree excludes results/, so external users regenerate this artifact with the command above.

The audit/override workflow is stored internally as results/audit/codex_20260523_override.md. It demonstrates how a benign denial is recorded as a structured audit event and resolved by adding a scoped operator override under policies/overrides-compatible layout.

Focused eBPF edge tests are stored internally as results/ebpf_edges/codex_20260523_ebpf_edges.md. They cover fail-closed map availability, workspace write allow behavior, directory-prefix boundary denial, /tmp staging denial, and localhost exfiltration denial.

The AgentBound-style baseline check is stored internally as results/agentbound/codex_20260523_agentbound.md. It documents that C-AB is a per-server, application-level reproduction and verifies the expected argument-visible coverage and implementation-hidden blind spot.

Threat Model

The MCP server is treated as untrusted. It may be benign-but-misconfigured, intentionally malicious, or a trojanized fork of a legitimate server. The agent / runner and the host OS are trusted. The eBPF layer assumes the kernel is uncompromised; in-kernel privilege escalations (CWE-269 in kernel modules) are out of scope.

Citation

A BibTeX entry will be added here once the paper is published.

Contributing

See CONTRIBUTING.md. All contributors must sign Meta's CLA.

Security

To report a security issue, see SECURITY.md. Please do not file public GitHub issues for security reports.

License

MIT — see LICENSE.