Zero-token, air-gapped deterministic validation engine and MCP skill agent for hybrid AI development workflows
Project description
PyShield
A zero-token, air-gapped, deterministic validation engine and MCP Skill Agent — built to partner with external AI coding agents (Claude Code, Cursor, GitHub Copilot) in a hybrid development lifecycle.
The Problem
Modern AI coding agents are powerful but probabilistic. They generate plausible code, not guaranteed-correct code. When they operate in a loop — generate → test → fix → generate — they need a grounding signal they can fully trust. A signal that is:
- Deterministic — same input, same result, every time
- Fast — no network calls, no token budgets, no LLM inference
- Precise — exact crash vectors, parameter values, and policy violations
- Honest — never guesses at fixes; only reports what it observed
PyShield is that signal.
It sits between your codebase and your AI agent. The agent writes code; PyShield runs the ground truth; PyShield hands the exact failure contract back to the agent. The agent uses its probabilistic intelligence to heal the code. Neither side tries to do the other's job.
How It Works — The Hybrid Boundary
┌─────────────────────────────────────────────────────────────────┐
│ Your Development Loop │
│ │
│ AI Agent (Claude / Cursor / Copilot) │
│ │ writes/edits Python code │
│ ▼ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ PyShield (deterministic) │ │
│ │ │ │
│ │ git diff → AST parse → Policy check (FIN001) │ │
│ │ ↓ ↓ ↓ │ │
│ │ Constants Param classifier SHA-256 seal │ │
│ │ scanner (email/id/money) │ │
│ │ ↓ ↓ │ │
│ │ Sandboxed mutation tests + PII leak detection │ │
│ │ ↓ │ │
│ │ Provenance Ledger (JSON / Markdown) │ │
│ └───────────────────────┬────────────────────────────┘ │
│ │ failure contract │
│ ▼ │
│ AI Agent interprets exact errors → edits code → re-runs │
│ │
└─────────────────────────────────────────────────────────────────┘
PyShield enforces the hybrid boundary rule: zero probabilistic components inside (no local LLMs, no embeddings, no heuristic guesswork). The engine speaks only in hard runtime truths — crashes, exceptions, policy violations, PII leaks. The AI agent does the interpretation.
Features
| Capability | Description |
|---|---|
| Git-aware | Automatically detects changed / untracked .py files via git diff |
| AST parsing | Extracts function signatures, parameter names, and type annotations |
| Mutation testing | Injects boundary values per parameter type (email, id, price, …) |
| Constants discovery | Scans sibling files for UPPERCASE constants and injects them as mutations |
| FIN001 policy | Flags price / balance / amount variables used in float arithmetic (must use decimal.Decimal) |
| PII/PHI fuzzer | Seeds ssn, patient_id, credit_card params with realistic mock data; intercepts stdout + logging for unmasked leaks |
| Sandboxing | Mocks all open("w") and subprocess calls during test execution |
| Circuit-breaker | Stops retrying after 3 consecutive failures on the same (func, param, value) triple |
| SHA-256 seal | Cryptographically attests validated files with a local .pyshield/*.seal.json |
| MCP tool | run_shield_check() exposes the full pipeline as an MCP tool over stdio |
| CLI | pyshield [files] [--format json|markdown] — exits 0 on pass, 1 on failure |
Installation
Prerequisites
- Python 3.13+
- uv (recommended) or pip
Install as a tool with uv (recommended for agentic CLI / IDE use)
Install once — both the pyshield CLI and pyshield-mcp server are added to your PATH:
uv tool install pyshield
Run without installing at all (perfect for CI or one-off checks):
uvx pyshield [files...]
uvx --from pyshield pyshield-mcp # launch the MCP server
Install a specific version:
uv tool install pyshield==0.1.0
Install from PyPI with pip
pip install pyshield
Install directly from GitHub (before first PyPI release)
uv tool install git+https://github.com/biswa-mohapatro/PySheild
# or
pip install git+https://github.com/biswa-mohapatro/PySheild.git
From source (development)
git clone https://github.com/biswa-mohapatro/PySheild.git
cd pyshield
uv sync
uv run pyshield --version
Quick Start — CLI
# Validate all git-changed Python files in the current repo
pyshield
# Validate specific files
pyshield src/payments.py src/orders.py
# Output machine-readable JSON (for CI pipelines)
pyshield --format json
# Exit code: 0 = all validations passed, 1 = failures detected
pyshield src/payments.py || echo "Validation failed — check output"
Example output
# PyShield Provenance Ledger — [FAILED]
**Timestamp:** 2026-05-22T12:28:24+00:00
**Git Files Scanned:** 1
## `src/payments.py`
### Policy Violations (FIN001)
- `FIN001` line 27 in `calculate_total`: Financial variable 'price' used in float arithmetic. Use decimal.Decimal.
#### `calculate_total` (line 22)
- [FAIL] param=`price` value=`0.0`
```
TypeError: unsupported operand type(s) for *: 'float' and 'NoneType'
```
- [FAIL] param=`price` value=`nan`
...
#### `display_patient_record` (line 50)
- [PII LEAK] param=`patient_id` value=`'PATIENT-00042'`
```
PII LEAK DETECTED: Sensitive value 'PATIEN…' appeared unmasked in captured output.
```
MCP Server Setup
PyShield ships a FastMCP server that exposes run_shield_check() as a tool over stdio JSON-RPC. The pyshield-mcp binary is included in every install.
Test the MCP server with MCP Inspector (no IDE needed)
npx @modelcontextprotocol/inspector uvx --from pyshield pyshield-mcp
This opens an interactive browser UI where you can call run_shield_check and inspect tool schemas.
Claude Desktop
Add to ~/.config/claude/claude_desktop_config.json (macOS/Linux) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"pyshield": {
"command": "uvx",
"args": ["--from", "pyshield", "pyshield-mcp"],
"cwd": "/path/to/your/project"
}
}
}
Cursor (.cursor/mcp.json)
{
"mcpServers": {
"pyshield": {
"command": "uvx",
"args": ["--from", "pyshield", "pyshield-mcp"],
"cwd": "${workspaceFolder}"
}
}
}
VS Code Copilot (.vscode/mcp.json)
{
"servers": {
"pyshield": {
"type": "stdio",
"command": "uvx",
"args": ["--from", "pyshield", "pyshield-mcp"],
"cwd": "${workspaceFolder}"
}
}
}
Why
uvx? Usinguvx --from pyshield pyshield-mcpmeans the IDE never needs PyShield installed globally —uvfetches and caches it automatically in an isolated environment on first launch.
Tool signature
run_shield_check(files: list[str] | None = None) -> str
When files is None, PyShield resolves targets from git diff HEAD. Returns a Markdown failure contract for the calling agent to interpret and fix — or a clean pass message when everything passes.
Engine Architecture
pyshield/
├── pyshield/
│ ├── __init__.py
│ ├── cli.py # argparse entry point; Provenance Ledger output
│ ├── mcp_server.py # FastMCP stdio server; run_shield_check() tool
│ └── engine/
│ ├── tracker.py # git diff + ls-files → changed .py files
│ ├── parser.py # ast.parse → FunctionInfo (name, lineno, params, annotations)
│ ├── harvester.py # param-name classifiers → boundary mutation arrays
│ ├── compliance.py # FIN001 static policy + SHA-256 seal/store
│ └── runner.py # sandboxed exec, PII fuzzer, circuit-breaker
└── tests/
├── test_tracker.py # 6 tests
├── test_parser.py # 7 tests
├── test_harvester.py # 12 tests
├── test_compliance.py # 10 tests
└── test_runner.py # 6 tests
Engine Stages (per file)
- Track —
tracker.pyrunsgit diff HEADandgit ls-files --othersto build the target file list - Parse —
parser.pywalks the AST to extract everyFunctionDefwith its parameter names and type annotations (self/clsexcluded) - Harvest —
harvester.pyclassifies each parameter by name pattern, producing typed boundary arrays; also scans sibling*.pyfiles forUPPERCASEconstants - Policy —
compliance.pystatically checks for FIN001 (financial float arithmetic) across all function AST nodes - Execute —
runner.pysandboxes each mutation in a temp file loaded viaimportlib, interceptsstdout/logging for PII, mocks all write-modeopen()and subprocess calls - Seal —
compliance.pywrites a SHA-256.pyshield/<file>.seal.jsonwhen all stages pass for a file
Mutation Classifier Rules
| Param name contains | Mutations injected |
|---|---|
email |
"a@b", "@@", " ", "", "not-an-email", "@domain.com" |
id, count, index |
0, -1, 1, 2147483647, -2147483648 |
price, balance, amount |
0.0, -0.01, float('nan'), float('inf'), -float('inf') |
ssn, patient_id, credit_card, … |
PII seed + default mutations |
| (anything else) | None, "", 0, -1 |
Running Tests
# Full test suite
uv run pytest
# With verbose output
uv run pytest -v
# Linting
uv run ruff check .
# Type checking
uv run mypy pyshield/
CI Integration
PyShield is designed as a native CI gate:
# GitHub Actions example
- name: Run PyShield
run: uv run pyshield --format json > pyshield-report.json
continue-on-error: false # exit 1 blocks the pipeline
- name: Upload report
uses: actions/upload-artifact@v4
with:
name: pyshield-report
path: pyshield-report.json
Limitations & Known Behaviour
- Sandboxed execution scope — mutations are injected one parameter at a time; remaining parameters receive
None. Functions with strict co-parameter validation will fail on unrelated params (this is expected; use it to find missing guard clauses). - Log-level PII detection — PII leaks via
logger.info()are only detected when the root log level isINFOor lower. At the defaultWARNINGlevel,info()calls are suppressed before reaching the capture stream. - Import-dependent functions — functions that import external packages not present in the current environment will fail with
ModuleNotFoundErrorduring sandboxed execution. Ensure your project environment is fully installed before running PyShield. - Policy rules are additive — currently only FIN001 is shipped. Additional rule IDs (
SEC001,PII001, etc.) are planned.
Roadmap
-
SEC001— detecteval()/exec()on user-supplied input -
PII001— detect unmasked PII in return values (not just stdout) - Per-file policy configuration via
.pyshield.toml - HTML report output
- Parallel file processing
- VS Code extension with inline violation markers
Contributing
See CONTRIBUTING.md for the development workflow, code standards, and how to add a new policy rule.
Security
Please do not open public issues for security vulnerabilities. Read SECURITY.md for the responsible disclosure process.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pysheild-0.1.0.tar.gz.
File metadata
- Download URL: pysheild-0.1.0.tar.gz
- Upload date:
- Size: 89.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2957f41a33ffae2282e0dcb6f6d3e091daec9f973431fd1715672d9d38c9a8d
|
|
| MD5 |
35c225a9c156d646909f5a06fa553f41
|
|
| BLAKE2b-256 |
44084c2028e887745a3d757d2260393cf651f60a69f572821bb09940f2ea006b
|
File details
Details for the file pysheild-0.1.0-py3-none-any.whl.
File metadata
- Download URL: pysheild-0.1.0-py3-none-any.whl
- Upload date:
- Size: 21.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.16 {"installer":{"name":"uv","version":"0.11.16","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f68155ed5e91ec9d3faee133b2e77f3d109841f28edabea6e781195ea3c4011
|
|
| MD5 |
3318d2c39b3a4520d1226a874c39004d
|
|
| BLAKE2b-256 |
cd39fffb4fc9a106833a9445bce081707eb263625deda33daa2c6e758d131e66
|