Abstract
As enterprise AI systems move from single-turn retrieval-augmented generation (RAG) toward agents that plan, call tools, and modify external state, safety can no longer be treated only as text filtering.
This article presents a practical control-plane approach using PolicyAware, an open-source Python framework for deny-by-default policy, PII and secret handling, MCP tool governance, model routing, runtime evaluation, repository scanning, and audit evidence.
The goal is not to promise "zero overhead." The goal is to keep the common enforcement path local, deterministic, measurable, and separate from optional heavyweight ML integrations. Teams should benchmark median, p95, and p99 latency in their own request path before setting production objectives.
1. Why Text Guardrails Are Not Enough
First-generation AI safety tools often treat security as an input/output problem: inspect the prompt, validate the response, and block suspicious text.
That remains useful, but an autonomous workflow has a wider attack surface. An agent can:
- choose a model or provider,
- retrieve untrusted context,
- call an MCP server,
- read or write files,
- execute database operations,
- create pull requests,
- deploy software,
- or trigger an external business process.
The important question is therefore not only "Is this text safe?" It is also:
Is this actor allowed to perform this action, against this resource, in this tenant and region, with these arguments, at this risk level, and does it require human approval?
A control plane answers that question before the side effect occurs.
2. Text Validation vs. an AI Control Plane
| Dimension | Text-focused guardrail | PolicyAware control-plane approach |
|---|---|---|
| Primary role | Prompt/output inspection | Request, model, tool, evaluation, and audit governance |
| Decision context | Mostly text | Role, tenant, region, risk, connector, action, arguments, and policy |
| MCP awareness | Usually application-specific | JSON-RPC tools/call inspection and connector/action mapping |
| Decisions | Commonly allow or block | Deny, require approval, allow, conditional allow, and transform |
| Data handling | Detection or rejection | Detect and optionally redact PII, PHI, secrets, and sensitive fields |
| Deployment | Inline validator | Embedded SDK, middleware, CLI, MCP proxy, or sidecar |
| Runtime foundation | Product-dependent | Local, deterministic Python rules in the base package |
| Heavy detection | Often bundled | Optional Presidio, Transformers, NeMo Guardrails, and Guardrails AI extras |
| Performance claim | Frequently generalized | Benchmark locally; do not assume a universal latency number |
At the time of writing, the repository declares PolicyAware version 0.4.4 and Python 3.10+. The base package is intentionally lightweight, with Pydantic, PyYAML, Typer, and Rich as core dependencies.
3. The Runtime Architecture
PolicyAware separates governance into explicit engines:
AI App / RAG Pipeline / Agent
|
v
PolicyAware SDK / CLI / Middleware / Callback
|
v
Data Protection -> Risk Classification -> Policy Decision
| |
deny/approval |
v
Model Routing or Tool Governance
|
v
Runtime Evaluation
|
v
Audit Trace / Evidence
The request lifecycle is:
- Construct a request with identity, tenant, application, region, task, and risk context.
- Inspect prompts and arguments for PII, PHI, secrets, and sensitive categories.
- Assign a deterministic risk tier: low, medium, high, or critical.
- Evaluate deny, approval, allow, and transform rules.
- Stop denied or approval-gated work before model or tool execution.
- Route allowed model requests or authorize a connector/action pair.
- Evaluate outputs for leakage, citations, and policy consistency.
- Emit traceable reason codes, matched policy IDs, decisions, and audit evidence.
A key semantic rule is that transform rules do not grant access. A redaction transform modifies an otherwise permitted request; it does not turn a denied request into an allowed one.
4. Quick Start: Govern a Raw Model Call
Install the base package:
pip install policyaware
Create a gateway and inspect a prompt before sending it to a provider:
import policyaware
from openai import OpenAI
gateway = policyaware.Gateway.from_policy_file("policyaware.yaml")
client = OpenAI()
safe_prompt, metadata = gateway.inspect_and_mutate(
prompt="Email jane@example.com about claim ACME-42.",
context={
"user_role": "billing_admin",
"session_id": "99x-delta",
"risk": "low",
},
app="claims-assistant",
)
print(metadata["decision"])
print(metadata["actions"])
response = client.responses.create(
model="gpt-4.1-mini",
input=safe_prompt,
)
print(response.output_text)
inspect_and_mutate(...) fails closed with PermissionError for denied or approval-required requests. If policy permits redaction, it returns the transformed prompt and audit metadata through the same runtime path.
5. Governing MCP Tool Calls Before Execution
MCP servers can expose powerful operations. A filesystem server, GitHub connector, database connector, or deployment tool should not receive a request until policy has evaluated the intended action.
A deny-by-default MCP policy can distinguish read, write, and delete operations:
id: mcp_proxy_policy
schema_version: "0.2"
default: deny
connectors:
- id: filesystem
type: mcp
actions:
read_file:
effect: allow
risk: low
side_effect: none
when:
user.role_in: [developer, security_engineer]
write_file:
effect: require_approval
risk: high
side_effect: write
when:
user.role_in: [developer]
delete_file:
effect: deny
risk: critical
side_effect: delete
PolicyAware can inspect a raw JSON-RPC request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "filesystem.read_file",
"arguments": {
"path": "README.md",
"query": "email jane@example.com"
}
}
}
Use the CLI for a deterministic policy check:
policyaware mcp check policyaware.yaml mcp-request.json
Or place a live stdio proxy in front of an MCP server:
policyaware mcp proxy policyaware.yaml --connector filesystem --agent coding_agent --role developer --server-command "python filesystem_mcp_server.py"
For a denied tool call, the proxy returns a structured JSON-RPC error to the client instead of forwarding the request to the real server. Non-tool protocol messages such as initialize pass through normally.
Repository guide: MCP Policy Proxy
6. LangGraph-Style Node and Tool Governance
PolicyAware includes a dependency-free PolicyAwareNodeGuard for graph-style agent workflows:
from policyaware import PolicyAwareNodeGuard
guard = PolicyAwareNodeGuard(
config="policyaware.yaml",
tool_policy="tool-governance.yaml",
)
def summarize_customer(state):
return {
"messages": [
{
"role": "assistant",
"content": "Customer record summarized safely.",
}
]
}
guarded_node = guard.guard_node(summarize_customer)
The guard can inspect graph state before a node runs, block denied states, return approval-required decisions for high-risk work, evaluate MCP-style tool calls, and attach governance metadata back to state.
This is more useful than throwing an opaque exception because the orchestrator receives a structured decision that can be logged, displayed, or routed to a durable approval workflow.
Runnable example: LangGraph Agent Governance
7. Performance Engineering Without Benchmark Theater
Policy enforcement adds work. The engineering objective is to make that work predictable and proportionate to risk.
PolicyAware's base path is local and rules-based. Policy loading can be cached so YAML is not parsed on every request. Optional ML and external guardrail engines are intentionally separated because Presidio, Transformers, Torch, ONNX, NeMo Guardrails, and Guardrails AI can add model-loading time, container size, and request latency.
The repository includes reproducible local benchmarks:
python benchmarks/benchmark_policy_engine.py --requests 1000 --concurrency 1
python benchmarks/benchmark_policy_engine.py --requests 1000 --concurrency 20 --json
The benchmark reports:
- median latency in microseconds,
- p95 latency,
- p99 latency,
- total runtime,
- and requests per second.
Repository scanning can also be measured:
python benchmarks/benchmark_scan.py . --iterations 3
A responsible latency statement should include the PolicyAware version, Python version, hardware, operating system, policy size, input distribution, concurrency, enabled extras, and whether external services were called.
Do not copy a single development-machine number into a universal production SLA.
Benchmark guide: Lightweight Benchmarks
8. Choose the Right Enforcement Boundary
| Mode | Best use | Important boundary |
|---|---|---|
| Embedded SDK | Fast adoption in Python apps and prototypes | Code executing in the same process may bypass local checks |
Gateway.chat(...) |
Central model request control, routing, evaluation, and audit | The application must send controlled requests through the gateway |
| Tool policy / MCP proxy | Connector and action enforcement before side effects | Approved tools still need OS-level isolation and least privilege |
| LangChain/LlamaIndex callbacks | Observation, streamed-token accounting, and reporting | Callbacks observe; they are not a hard execution boundary |
| HTTP sidecar | Polyglot services and stronger process separation | Requires authentication, private networking, and operational ownership |
policyaware scan |
Local and CI detection of governance gaps | Static analysis does not prove runtime exploitability |
For stronger enterprise separation, run PolicyAware as an internal sidecar or gateway:
set POLICYAWARE_SIDECAR_TOKEN=replace-with-secret-token
policyaware up --policy policyaware.yaml --tool-policy tool-governance.yaml --require-auth
A sidecar can have separate process memory, service identity, deployment lifecycle, audit stream, and network policy. It should still be combined with TLS or mTLS, IAM, secret management, and least-privilege tool credentials.
9. Shift Governance Left with Repository Scanning
Runtime checks are only one layer. PolicyAware also includes an offline governance linter:
policyaware scan . --format html,json,sarif,markdown
The scanner can identify likely risks such as:
- PII, PHI, and secret exposure,
- direct model calls without governance,
- unmapped MCP tools,
- weak or missing tool policies,
- routing and audit gaps,
- and invalid policy YAML.
For pull requests, the repository links an official GitHub Action:
- uses: ktirupati/policyaware-action@v1
Static findings are signals, not proof of exploitability. They should feed secure code review, policy tests, and CI decisions rather than replace them.
10. Production Checklist
Before adopting an AI control plane in production:
- Model real roles, tenants, regions, resources, and business actions in policy.
- Keep tool governance deny-by-default.
- Separate read-only identities from write, delete, deploy, and payment identities.
- Require approval for high-impact side effects.
- Store approval state durably and verify the approver's identity before resuming.
- Use short-lived credentials and keep secrets out of prompts and agent state.
- Sandbox untrusted execution with containers, Wasm, gVisor, Firecracker, or Kubernetes isolation.
- Run golden datasets for allow, deny, redact, and approval outcomes.
- Benchmark the exact production policy stack and optional integrations.
- Export policy decisions and execution results to audit, metrics, SIEM, or GRC systems.
- Run policy contract checks so YAML actions do not drift from tool function signatures.
- Treat prompt-injection defense as layered: deterministic policy, restricted tools, optional semantic signals, and human review.
11. What PolicyAware Is—and Is Not
PolicyAware is designed for LLM applications, RAG pipelines, MCP workflows, autonomous agents, and AI governance scans.
It is not a replacement for:
- IAM and authorization,
- WAF or API gateway controls,
- application security testing,
- secrets management,
- container or process isolation,
- endpoint security,
- or secure software development practices.
The embedded Python SDK is not a secure-memory boundary. PolicyAware can decide whether a tool call should run, but it does not itself sandbox an approved command. Optional semantic classifiers can improve detection, but deterministic policy remains the auditable enforcement base.
These limitations are a strength when stated clearly: the framework owns AI governance decisions and evidence, while the platform continues to own identity, isolation, credentials, networking, and execution security.
Read the full security boundaries and limitations.
12. Conclusion
Text filtering remains part of AI safety, but autonomous systems require action-level governance.
A useful AI control plane should answer:
- who is acting,
- what model or tool is being requested,
- which data and resources are involved,
- whether the action is allowed,
- whether sensitive values must be transformed,
- whether a human must approve,
- and what evidence must be retained.
PolicyAware provides an open-source implementation of that pattern through a lightweight Python package, deny-by-default YAML policy, MCP JSON-RPC interception, model routing, evaluation hooks, repository scanning, and audit traces.
The credible path to low latency is not an unmeasured "zero-overhead" claim. It is a local deterministic fast path, selective use of heavier detectors, cached policy state, reproducible benchmarks, and continuous measurement in the real deployment.
- GitHub: ktirupati/policyaware
- Documentation: PolicyAware docs
- PyPI: policyaware
- Examples: Runnable examples
Top comments (0)