Skip to main content

Zero-token, air-gapped deterministic validation engine and MCP skill agent for hybrid AI development workflows

Project description

PyShield

CI Python 3.13+ License: MIT Typed

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? Using uvx --from pyshield pyshield-mcp means the IDE never needs PyShield installed globally — uv fetches 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)

  1. Tracktracker.py runs git diff HEAD and git ls-files --others to build the target file list
  2. Parseparser.py walks the AST to extract every FunctionDef with its parameter names and type annotations (self/cls excluded)
  3. Harvestharvester.py classifies each parameter by name pattern, producing typed boundary arrays; also scans sibling *.py files for UPPERCASE constants
  4. Policycompliance.py statically checks for FIN001 (financial float arithmetic) across all function AST nodes
  5. Executerunner.py sandboxes each mutation in a temp file loaded via importlib, intercepts stdout/logging for PII, mocks all write-mode open() and subprocess calls
  6. Sealcompliance.py writes a SHA-256 .pyshield/<file>.seal.json when 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 is INFO or lower. At the default WARNING level, 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 ModuleNotFoundError during 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 — detect eval() / 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pysheild-0.1.0.tar.gz (89.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pysheild-0.1.0-py3-none-any.whl (21.9 kB view details)

Uploaded Python 3

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

Hashes for pysheild-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e2957f41a33ffae2282e0dcb6f6d3e091daec9f973431fd1715672d9d38c9a8d
MD5 35c225a9c156d646909f5a06fa553f41
BLAKE2b-256 44084c2028e887745a3d757d2260393cf651f60a69f572821bb09940f2ea006b

See more details on using hashes here.

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

Hashes for pysheild-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0f68155ed5e91ec9d3faee133b2e77f3d109841f28edabea6e781195ea3c4011
MD5 3318d2c39b3a4520d1226a874c39004d
BLAKE2b-256 cd39fffb4fc9a106833a9445bce081707eb263625deda33daa2c6e758d131e66

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page