Skip to main content

SENTINEL

A provenance-aware security proxy for AI agents. It sits between your agent and your MCP tool servers, tracks where every byte of context came from, and refuses actions whose data originated in untrusted content — even when the attack slipped past your content filter.

pip install sentinel-prox    # imports and CLI are both `sentinel`
sentinel init             # write sentinel.yaml
sentinel check            # validate config, connect to your servers, vet their tools
sentinel scaffold > policy.yaml
sentinel serve            # your agent points at http://127.0.0.1:8765/mcp

Your agent needs no code changes: point its MCP endpoint at SENTINEL instead of directly at your tool servers. Interception is guaranteed by topology, not by asking the agent to cooperate.


The problem

AI agents don't just answer questions any more — they send email, query business systems, and read the open web. That makes indirect prompt injection an action problem, not a text problem: an attacker hides an instruction inside content the agent will read, and it executes with the agent's full privileges. "Summarize this pricing page" quietly becomes "email this customer's SSN to the attacker."

Content filters scan the words. Microsoft's own Prompt Shields documentation says it "may not catch all attack vectors" and recommends additional validation layers. The gap: security is applied to the words, while the damage is done by the actions.

SENTINEL closes it by judging an action on where its data came from, not on how the request was phrased.

What it protects against

Attack How SENTINEL stops it
Indirect prompt injection Actions whose lineage includes untrusted content are denied — regardless of phrasing, so obfuscation doesn't help
Data exfiltration A send_email built from a retrieved page is refused before it executes
Tool poisoning Tool descriptions and input schemas are scanned at connect; a poisoned catalogue is refused
Cross-server shadowing Two servers claiming one tool name fails closed — SENTINEL won't guess which is authoritative
Rug pulls The catalogue is fingerprinted at approval and re-checked; post-approval mutation is detected
Privilege escalation Unknown tools are default-denied until you write a rule
Repeated abuse A trust score degrades on blocked calls and quarantines the agent

Every decision becomes an immutable, replayable forensic record, exportable as SIEM-ready JSONL.

How it works

 your agent  ──MCP──▶  SENTINEL  ──MCP──▶  your MCP servers
                          │
       1. trace    label the origin of everything the agent has seen
       2. authorize  policy decides each call using that lineage (deny-overrides)
       3. contain   trust score + automatic quarantine
       4. record    immutable spans → replay + SOC export

Provenance is a set of trust labels (SYSTEM > USER > AGENT > RETRIEVED_CONTENT) unioned over an action's transitive derived_from ancestry — computed by a real cycle-safe graph walk, not a mutable flag. An action is tainted iff RETRIEVED_CONTENT is in that set. Taint clears only through an explicit, auditable StructuredExtractor (strict schema validation produces a fresh SYSTEM-trust value with no inherited ancestry), and a sanitized value cannot launder a tainted sibling — recombination re-taints.

Policy compiles to a typed condition AST and is evaluated by tree-walk; there is no eval anywhere in the codebase. Rules are deny-only with deny-overrides, and unknown tools are default-denied.

Configuration

sentinel.yaml (created by sentinel init):

servers:                      # YOUR MCP servers — SENTINEL ships no tools
  - name: github
    url: https://mcp.example/gh
policy: ./policy.yaml         # your rules; generate with `sentinel scaffold`
host: 127.0.0.1
port: 8765
dashboard: false              # the bundled UI is a DEMO, opt-in only
catalogue_strict: true        # refuse catalogues containing injection markers

Precedence is CLI flag > environment variable > config file > default, so a container can override a checked-in file. Every key has an env equivalent (SENTINEL_MCP_SERVERS, SENTINEL_POLICY_FILE, …) — see .env.example.

Writing policy

Rules are deny-only: a call is allowed when no deny rule matches. sentinel scaffold emits every discovered tool explicitly denied, with its description and a recommended starting rule, so you edit rather than invent.

policy_version: 1
tools:
  send_email:
    rules:
      - id: block-untrusted-origin
        deny_if: "RETRIEVED_CONTENT in effective_provenance"
      - id: domain-allowlist
        deny_if: "recipient_domain not in allowed_domains"
  delete_record:
    rules:
      - id: never
        deny_always: true

Predicates support == != < >= in "not in", set literals ({USER}), tool arguments, and config values.

Deployment

docker build -t sentinel -f deploy/Dockerfile .
docker run --rm -p 8765:8765 -v $(pwd)/sentinel.yaml:/app/sentinel.yaml sentinel

Put your tool servers on an internal network reachable only by SENTINEL — that topology is what makes interception unbypassable. An Azure Container Apps blueprint (internal-ingress tool servers, KEDA scaling, managed identity, Cosmos persistence) is in deploy/.

Gate the endpoint on any public deploy with SENTINEL_API_TOKEN.

Try the demo

A bundled demo shows the whole pipeline on a scripted attack — useful for seeing what a block looks like, but not the product surface:

sentinel serve --dashboard     # → http://localhost:8765

Hosted: https://sentinel-i63x.onrender.com (free tier — first load may take ~50 s to wake). A poisoned page induces the agent to email a synthetic customer record to an attacker; the Layer-1 filter misses the obfuscated variant and authorization blocks it anyway. All demo data is synthetic — the record is a labelled fake (SSN 000-00-0000, a non-functional sk-synthetic-DO-NOT-USE key) and send_email writes to an in-memory sink. Nothing is ever sent.

With a real model

The default agent is a real LLM whenever a credential is present — set OPENAI_API_KEY, or AZURE_OPENAI_ENDPOINT + AZURE_OPENAI_DEPLOYMENT. With no credential it falls back to a deterministic scripted transcript so CI stays key-free. For a free local model, point OPENAI_BASE_URL at any OpenAI-compatible endpoint:

ollama serve && ollama pull llama3.2
export OPENAI_BASE_URL=http://localhost:11434/v1 OPENAI_MODEL=llama3.2

What SENTINEL does not protect against

Stating the boundary precisely is what separates a security product from a demo.

  • Provenance is tracked at message / tool-result granularity, not token-level inside model reasoning. Taint spreads conservatively unless a sanitizer clears it.
  • It secures the action layer, not the model's cognition. It does not stop a model being persuaded — it stops the resulting unauthorized action.
  • The proxy and the policy store are trusted components.
  • One trace is handled by one proxy instance; horizontal scaling is across traces.
  • Conservative tainting is intentional. Some benign workflows will need explicit sanitization. Taint saturation is the correct bias for action-layer security.
  • Sanitization is syntactic, not semantic. A schema-valid {"price": 999999} is well-formed but still subject to argument-level rules such as an amount cap.

Development

python -m venv .venv
.venv/bin/python -m pip install -e ".[dev]" -c versions.lock   # Windows: .venv\Scripts\python.exe
.venv/bin/python -m pytest        # 370 tests
.venv/bin/python -m ruff check src tests
.venv/bin/python -m mypy src      # strict

Install -c versions.lock so local matches CI and the container — a floating dependency is how a production deploy once broke. Requires Python 3.11+.

See CONTRIBUTING.md for the security invariants a change must preserve, and CHANGELOG.md for release notes. Design notes live in docs/: the scoping analysis for proxying arbitrary MCP servers, and the competitive/threat-landscape research behind the roadmap.

License & credits

MIT — see LICENSE.

Built on the Model Context Protocol Python SDK, FastAPI, Starlette, sse-starlette, Uvicorn, Pydantic, OpenTelemetry, PyYAML, httpx, pytest, ruff, mypy, gitleaks, and the Azure SDKs for Python. Thank you to their maintainers.

AI tools used in development: Claude Code (Anthropic) and GitHub Copilot. SENTINEL also integrates Azure OpenAI (attack classification) and Azure AI Content Safety / Prompt Shields (Layer-1 screening) as optional components.

Originally built for the Microsoft Build AI Hackathon 2026 — Security in the Agentic Future — by Pali Krishna Harshith.

Release files for sentinel-prox 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sentinel-prox 0.1.1
File Size Uploaded
sentinel_prox-0.1.1.tar.gz 326.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sentinel-prox 0.1.1
File Interpreter ABI Platform
sentinel_prox-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 618.0 kB

Release files / sentinel_prox-0.1.1.tar.gz

Download URL sentinel_prox-0.1.1.tar.gz
Size 326.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b6609ab2631fc0936c4e357c80556e1784b86505da1a7206fe37642e42028b62
BLAKE2b-256 checksum
How to use checksums
b179019b02fd812c27304cc8fe6134381642d84ba0e1c80c8addea7e04cc2f13
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release files / sentinel_prox-0.1.1-py3-none-any.whl

Download URL sentinel_prox-0.1.1-py3-none-any.whl
Size 291.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fc87aa1da46ca004e73b2a6b971d53a93e6530a3cd86ce816d9e3902617f5b55
BLAKE2b-256 checksum
How to use checksums
1d4d04f51260e849e27a4c5cf4b769ed5a3d291d09eed87bf93fa2dcbc38db13
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page