trifectaguard
Stop AI agents from leaking your data or acting for an attacker, whatever the injected instruction says.
An agent that can read untrusted content (web pages, issues, emails), read your private data, and send things out can be turned against you by one injected instruction (the "lethal trifecta"). Detectors try to spot the injection's wording and can be evaded. trifectaguard tracks what the session has read and where each tool call sends it, and asks or blocks before private data, credentials, money or access go somewhere an injection chose.
untrusted input → reads private data / credentials → sends it out ✗ blocked
(issue, web page, email) (post, email, curl, fetch, PR)
Works as Claude Code hooks, as a proxy in front of any MCP server (Claude Desktop, Cursor, …), or as a Python library for your own agents (LangChain/LangGraph, OpenAI Agents SDK, plain loops).
Quick start
pip install "trifectaguard[mcp]" # Python 3.10–3.13; the [mcp] extra is only needed for the proxy
trifectaguard scan # read-only: what could an injection make your AI apps do?
trifectaguard scan reads the MCP configs of Claude Code, Claude Desktop, Cursor,
Windsurf and VS Code and explains every risky combination:
Claude Code (not protected)
HIGH An injected instruction could make the agent send your credentials out
untrusted input: WebFetch, WebSearch → then reads credentials: Read, Bash → then sends via: WebFetch, Bash
It changes nothing, starts no servers, never opens credential files and never
prints tokens from your configs. --json for tooling; it exits 1 when an
unprotected high-risk combination exists, so it can gate CI.
Protect your agents
| For | Verified in | |
|---|---|---|
| Claude Code hooks | Claude Code, including Read, WebFetch, Bash, Write and every MCP server |
a real Claude Code session |
| MCP proxy | any MCP app; local (stdio) and remote (HTTP/SSE) servers | a real Claude Desktop chat |
| Python library | LangChain/LangGraph, OpenAI Agents SDK, your own loop | a live LangGraph agent |
Start any of them in mode: monitor to see what it would stop before it stops anything,
then trifectaguard suggest -c <config> proposes config (trusted sites, remembered
approvals) that removes repeat prompts without weakening the rules.
Claude Code
curl -O https://raw.githubusercontent.com/Ekaansh-Jain/trifectaguard/main/hooks.example.yaml
cp hooks.example.yaml hooks.yaml # list your MCP servers; starts in monitor mode
trifectaguard hooks-snippet -c hooks.yaml # paste the output into ~/.claude/settings.json
The engine runs as UserPromptSubmit / PreToolUse / PostToolUse hooks. It
sees your request (so addresses and links you typed are trusted), tracks the
whole session across built-in tools and MCP servers, and asks through Claude
Code's normal approval prompt. It only ever answers "ask" or "deny", never
"allow", so it can't loosen your permission settings. Bash is classified from
its text (commands that can send data out or hide what they do get the
scrutiny); that can't be complete, so keep Claude Code's own Bash permissions
on, and for strong guarantees on shell commands use Claude Code's /sandbox
(OS-level network limits) alongside trifectaguard. With remember_approvals: project, a destination you approve in a project isn't asked again there.
Any MCP app (Claude Desktop, Cursor, …)
curl -O https://raw.githubusercontent.com/Ekaansh-Jain/trifectaguard/main/gateway.example.yaml
cp gateway.example.yaml gateway.yaml # your servers + which repos are public/private
trifectaguard inspect -c gateway.yaml # how every tool is classified
In the app's MCP config, replace your servers with one entry running
trifectaguard run -c /abs/path/gateway.yaml. One gateway fronts all of them, so a
web page read through one server and a file read through another are one flow.
Risky calls ask the user through MCP elicitation where the app supports it,
and are blocked otherwise. Works with mcp 1.26+ and 2.x.
Your own agent (Python)
from trifectaguard import Guard, ask_in_terminal
from langchain_core.tools import tool # or the OpenAI Agents SDK's @function_tool
guard = Guard({"tools": {
"read_inbox": {"reads": ["untrusted", "private"]},
"send_email": {"writes": "external", "destination": ["to"]},
}}, on_ask=ask_in_terminal) # default: anything needing approval is denied
guard.user_message(user_request) # destinations the user typed are trusted
@tool
@guard.tool
def send_email(to: str, body: str) -> str:
"""Send an email."""
...
A refused call doesn't run; the tool returns a short explanation the model can
relay (or raises Blocked with blocked="raise"). One Guard per conversation.
How it works
- Labels. Tool results add labels to the session:
untrusted(issues, web pages, chat),private(private repos, local files),secret(credentials). - Flow rules. Before a call that sends something: untrusted + credentials → outside is blocked; untrusted + private → public or outside asks; untrusted content steering writes to CI/shell/agent config, access changes or deletions asks.
- Destination provenance. For a recipient, IBAN, URL or user, it checks where the value came from: your request or trusted data (fine) vs only untrusted content (an injection chose it: ask). Matching is by whole token, and anything the agent itself wrote after reading untrusted content stays untrusted.
- DLP. A secret the session read can't leave, even base64/hex/URL-encoded, reversed or spaced out.
- Rug-pull pins (proxy). Tool definitions are pinned; a changed one is quarantined until you re-pin.
- Policies are small YAML files. Presets ship for GitHub, filesystem, fetch, Slack and Claude Code's built-in tools; unknown tools are treated as untrusted with an unknown destination.
Results
On AgentDojo v1.2.2 with a worst-case agent that obeys every injection it reads:
| Attacker's goal | Still succeeds with trifectaguard |
|---|---|
| steal data (299 attacks) | 0.3% |
| hijack payments, access or contact (153) | 0% |
| deletions (39) | 0% |
| steer the agent among legitimate options (100) | 60% (not what it controls) |
About 31% of benign tasks need one approval (44% as an MCP proxy, which can't see your request). It passes an adaptive red-team suite written against its own rules (20/20). With live models, attacks succeeded 0/12 times per model on AgentDojo banking (7/12 and 6/12 unprotected) and 0/20 in a LangGraph agent (11/20 unprotected), and it blocked exfiltration in real Claude Code and Claude Desktop sessions. Prompt-injection detectors (including this repo's own) hid clean data on 39–74% of benign tasks. Method, disclosed post-hoc changes and every number: RESULTS.md.
Limits
- It controls where data and access go, not which legitimate option an agent picks, what text it writes to a legitimate recipient, or its final answer.
- Security depends on the policies being right; a misclassified tool is a hole.
trifectaguard inspectandtrifectaguard scanshow what each tool is treated as. Bashclassification in Claude Code is pattern-based: pair it with Claude Code's/sandboxfor shell commands.- GitHub repo visibility comes from your config, not the API.
- The proxy forwards tools (not MCP resources or prompts).
- Nobody outside this project has tried to break it yet: please do (SECURITY.md).
Related projects
Other open-source guards for Claude Code, described from their own READMEs and source at the commit read (2026-09-28). Not measured side by side: this is how each is documented to work, not a benchmark.
| Acts on | Blocks or warns | Session memory | Notes from their docs/source | |
|---|---|---|---|---|
lasso-security/claude-hooks (8fbfd14) |
tool output (PostToolUse) | warns only | no | ~96 regex patterns in 4 categories (instruction override, role-play, encoding, context manipulation) |
dwarvesf/claude-guardrails (b3c3e15) |
tool calls (PreToolUse), prompts, output | blocks (deny rules, command checks); output scan warns | no | mostly Claude Code permission deny rules for sensitive paths; its README notes Bash reads aren't covered by Read deny rules. Its output scanner reads a tool_output field; Claude Code's documented field is tool_response |
slavaspitsyn/claude-code-security-hooks (c4f126a) |
tool calls (PreToolUse) | blocks | no | per-command rules: credential path + network tool in the same command, read guards for credential directories, POST domain whitelist, canary files |
| trifectaguard | tool calls and output, across the whole session | asks or blocks | yes | labels what the session has read and checks where each call sends data and who chose the destination; also an MCP proxy and a Python library |
The others judge each call or output on its own (patterns, paths, command shapes); trifectaguard judges a call by what the session has already read and where the call sends it. They are complementary: path deny rules and trifectaguard can run together.
Research and reproducing the results
This repo started as a prompt-injection lab: a leak-rate benchmark of models
against documented 2026 agent attacks (mock MCP server, fake canary secret),
and a benchmark of injection detectors on tool outputs, including a fine-tuned
ModernBERT (scripts/, data/, results and corrections in RESULTS.md).
git clone https://github.com/Ekaansh-Jain/trifectaguard && cd trifectaguard
pip install ".[mcp]" agentdojo openai python-dotenv
cp .env.example .env # Groq / NVIDIA NIM / Gemini keys, for live-model runs only
python run_all_tests.py --no-llm # every test, no API calls
python eval/redteam/adaptive.py # attacks on trifectaguard's own rules
python eval/agentdojo/worst_case.py --hook # AgentDojo, model-independent (~3 min)
python eval/agentdojo/report.py # tables
python run_pilot.py --all --runs 10 # original model leak-rate benchmark
Inside this checkout the package is also importable as src.gateway
(python -m src.gateway …), which the research scripts use.
Repository layout
src/gateway/: the package (trifectaguardwhen installed):engine.py(labels + flow rules),rules.py(YAML policies),policies/(presets),dlp.py,hook.py(Claude Code),gateway.py(MCP proxy),guard.py(library),scan.py,pins.py.proxy.py/policy.pyare the original research proxy.eval/: AgentDojo, red-team, live-agent, Claude Code and Claude Desktop checks.tests/: unit and end-to-end tests.src/servers/github_mock.py,attacks/,src/harness/,run_pilot.py: the research benchmark (sandbox server with a fake secret).
Ethics
The benchmarks measure the vulnerability of your own agent setup in a sandbox so a defense can block it. Attack payloads recreate the structure of public disclosures and target only fake in-repo secrets.
Metadata
Release files for trifectaguard 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| trifectaguard-0.1.3.tar.gz | 76.6 kB | Without provenance | Details for trifectaguard-0.1.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| trifectaguard-0.1.3-py3-none-any.whl | Python 3 | none | any | Details for trifectaguard-0.1.3-py3-none-any.whl |
Total release size: 140.4 kB
Release files / trifectaguard-0.1.3.tar.gz
| Download URL | trifectaguard-0.1.3.tar.gz |
|---|---|
| Size | 76.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0879e2c938856cc0241840ffa320376d79258a3a0cf16377eb88c8d4978852b7
|
|
BLAKE2b-256 checksum How to use checksums |
212cbe995e7bb493ad8b0a6326533bcdb131126702d0cf5f72b55937522ffa1b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.10
|
Release files / trifectaguard-0.1.3-py3-none-any.whl
| Download URL | trifectaguard-0.1.3-py3-none-any.whl |
|---|---|
| Size | 63.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cf40c8d725a2f7f99cb5a326e0ae1513922579b738e3c87eea0dc5bce62b6502
|
|
BLAKE2b-256 checksum How to use checksums |
d8cd8bfc11f71f900e9489e2b215cfe711042f62a46dffd6c408103d742dcad5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.10
|
Security for trifectaguard 0.1.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.