Kevlar is a proxy firewall for Model Context Protocol
LLM [ KEVLAR ] MCP
It sits between (you) and (everyone else)
MCP servers control their own tools.
Those tools (good or bad) go into your LLM blindly, due to trust.
Any MCP server can exploit this to redirect the model, pull credentials, or add any tools they want.
The MCP spec does not prevent any of this.
You do not control what an MCP server puts in its messages.
TL;DR #
Kevlar sits between client and server and does four things:
- Strips all tool descriptions before they reach the LLM. No exceptions.
- Scans tool call responses for injection payloads and either redacts matched spans or blocks the response entirely.
- Blocks outbound calls carrying credentials in their arguments, even if the LLM has already been compromised.
- Locks the tool registry after the initial handshake so the server cannot inject new tools mid-session.
CDR on tools/list #
Kevlar strips every description field to "" unconditionally on every tools/list response, before the message reaches the client. The LLM sees name and inputSchema only. inputSchema is rebuilt from an allowlist: prose fields like description, title, examples, and $comment are stripped there too, and enum values, property keys, $ref targets, and type values are validated against the MCP spec. Score 0 on this pass cannot be achieved by adding description text back, because description text is never forwarded regardless of score.
Analysis on tools/call responses #
Tool call results cannot be stripped without breaking functionality. Kevlar scans them against ~60 indicators sourced from published prompt injection research. Responses scoring above the mode threshold are quarantined (matched spans replaced with [KEVLAR:<ID>] tokens) or rejected (replaced with an isError block). Unicode bypass attempts using characters that normalize to injection text under NFKC are detected before normalization and redacted after.
Egress DLP #
Outbound tools/call arguments are scanned before the call is forwarded to the server. If the LLM has already been compromised and is trying to exfiltrate credentials, the call is blocked at the proxy layer. The server never receives it. Runs in all three modes.
Tool registry lock (strict mode) #
After the initial tools/list handshake, the set of callable tool names is frozen. notifications/tools/list_changed from the server is dropped, not forwarded. Any call to a tool not in the CDR-sanitized set is blocked with a synthetic isError response regardless of call content.
Install #
git clone https://github.com/Ablation-Tool/kevlar.git
cd kevlar
pip install .
Requires Python 3.10+. Linux and macOS only. Depends on os.killpg and asyncio.connect_read_pipe.
Quickstart #
kevlar --mode strict -- npx -y @modelcontextprotocol/server-filesystem /tmp
kevlar --mode audit --log /tmp/kevlar.jsonl -- uvx mcp-server-git
Claude Desktop integration #
In ~/Library/Application Support/Claude/claude_desktop_config.json (macOS):
{
"mcpServers": {
"filesystem": {
"command": "kevlar",
"args": [
"--mode", "strict",
"--log", "/tmp/kevlar-filesystem.jsonl",
"--",
"npx", "-y", "@modelcontextprotocol/server-filesystem",
"/Users/you/Documents"
]
}
}
}
Modes #
| Mode | Reject threshold | Tool registry | list_changed |
|---|---|---|---|
strict |
score ≥ 40 | Locked at handshake | Dropped |
lenient |
score ≥ 80 | Dynamic updates allowed | Forwarded |
audit |
Never blocks | Dynamic | Forwarded |
Use strict in production. Use audit to profile a new server before tightening the threshold. --log produces a JSONL file with every policy decision, indicator ID, score, and masked value snippet.
Architecture #
Two files: policy.py is a pure CDR engine with no I/O, directly unit-testable. kevlar.py is the async stdio proxy. No runtime dependencies, stdlib only. kevlar.py spawns the server with start_new_session=True and kills the process group on shutdown, so npx/ uvx child processes don't linger.
See docs/architecture.md for the full design: proxy lifecycle, CDR pipeline per message type, scoring arithmetic, indicator registries, and audit log format.
Tests #
pip install -e ".[dev]"
pytest
149 tests. tests/test_proxy.py runs integration tests against real subprocess pairs. tests/test_policy.py covers the pure policy engine in unit tests.