Runtime security wrapper for AI coding agents — intercept, analyze, and enforce MCP and process-tree policies.
Project description
ai-agent (Agent Shield wrapper)
ai-agent is a Python wrapper around Codex. It launches Codex as a subprocess, watches its process tree and established TCP connections, runs risk analysis through Ollama, and asks a backend approval API whether suspicious activity should continue.
This wrapper is runtime enforcement, not a full sandbox. It can block risky child processes and network connections, but it does not prevent filesystem writes by itself. If you want hard write restrictions, pass Codex a read-only sandbox through AGENT_SHIELD_CODEX_ARGS.
Overview
At a high level the wrapper does this:
- Loads local environment from
.envwhen present. - Resolves the Codex command from config, with a runtime fallback to
npx -y @openai/codexifcodexis not installed. - Refreshes known MCP targets from
codex mcp list --json, with the samenpxfallback when needed. - Starts Codex with inherited stdio so the TUI behaves normally.
- Polls the Codex process tree for new descendants and established TCP connections.
- Classifies suspicious activity, analyzes it with Ollama, and submits an intercept to the backend.
- Enforces the backend decision:
approve: allow the process or connection to continue.deny: kill the relevant process tree.
- Reports the final session outcome to the backend.
Important runtime semantics:
- Intercept decisions are fail-closed. Submission failures, missing
intercept_id, or polling timeout all resolve todeny. - Local risk triage runs first. Low-risk
approveresults are allowed locally, high-riskdenyresults are blocked locally, and only the uncertain review band is escalated to the backend. - Review-band events can fail gracefully when the backend is degraded. By default, backend submit errors, backend
502s, and unresolved review timeouts fall back to localapprovefor a short cooldown window instead of terminating the session. - Backend health checks are best effort. Startup continues even if
GET /api/healthfails. - Final session reporting is best effort. A failed
POST /api/session/resultdoes not change the already computed wrapper result. - A denied intercept can intentionally terminate Codex. In that case the raw child exit may be a signal such as
-9, but the wrapper reports the session asblockedrather thanfailed. - Denied root-process network events are soft-blocked and audited without killing Codex. Denied child-process network events and denied process spawns still enforce hard termination of the offending process tree.
- Backend and Ollama traffic is auto-whitelisted by hostname, localhost alias, and resolved IP address so the wrapper does not self-intercept its own approval and risk-analysis calls.
- Logs go to
.agent-shield.logby default so Codex TUI output stays clean unless--verboseis used.
Intercept modes
AGENT_SHIELD_INTERCEPT_MODE controls how aggressively process and connection events are intercepted.
mcp-targets
Only intercept activity that looks MCP-related:
- A descendant command matches configured MCP patterns:
mcp_process_spawn - A command references a configured MCP host or port, or a known proxy port:
mcp_shell_target_access - An established connection matches an MCP process, configured MCP host or port, or a known proxy port:
mcp_connection_attempt
process-tree (default)
Intercept the full Codex process tree except explicitly whitelisted items:
- Any non-whitelisted child process spawn:
child_process_spawn - Any non-whitelisted established TCP connection from Codex or a descendant:
agent_connection_attempt - Root Codex outbound HTTPS to a non-local host is classified as model-provider traffic:
responses_api_connection_attempt
strict
Intercept all non-whitelisted descendants more aggressively:
- Any descendant shell process spawn:
shell_process_spawn - Any established TCP connection:
agent_connection_attempt
Requirements
- Python 3.9+
- Python packages from
requirements.txt - POSIX-style monitoring tools available on the host:
pgrep,ps,lsof,kill - A backend service that implements the contract in
BACKEND_API.md - Ollama running with an available model
- Either a
codexbinary onPATHornpx
Default toolchain values:
- Ollama host:
http://localhost:11434 - Ollama model:
qwen3.5:0.8b - Backend URL:
http://localhost:3000
Local setup
-
Install Python dependencies:
python3 -m pip install -r requirements.txt
-
Copy the example environment and adjust it for your machine:
cp .env-example .env
-
Start Ollama and make sure the configured model is available.
-
Start the approval backend.
Environment loading behavior:
config.pyloads.envfrom the module directory first, then from the current working directory if it is different.- Existing process environment variables always win over
.envvalues. - Inside the same
.envfile, later assignments override earlier ones. .env-exampledefaults Codex to-s read-only, so the wrapper starts with filesystem writes sandboxed unless you change it.
For AWS or API Gateway deployments:
- Set
AGENT_SHIELD_BACKEND_URLto the API host root such ashttps://...execute-api...amazonaws.com - A value ending in
/apialso works; the client normalizes both forms
Run
Direct examples:
python3 cli.py
python3 cli.py --backend http://localhost:3000
python3 cli.py --backend http://localhost:3000 -- --help
AGENT_SHIELD_BACKEND_URL=https://fmu6gbm7t8.execute-api.ap-northeast-1.amazonaws.com python3 cli.py
npm script wrappers:
npm run dev -- --help
npm test
package.json does not add extra runtime behavior. The npm scripts are thin wrappers around the same Python entrypoints.
CLI
python3 cli.py [--backend URL] [--verbose] [codex_args ...]
Arguments:
--backend: backend API base URL, overridingAGENT_SHIELD_BACKEND_URL--verbose/-v: also mirror interceptor logs to stderrcodex_args: forwarded to the resolved Codex command
Startup output:
- Prints the backend URL and active intercept mode
- Probes
GET /api/healthand prints eitherBackend health: okor a warning
Exit codes:
0: session completed with statussuccessorblocked1: startup failure, runtime error, or user interrupt
Configuration
Core environment variables
AGENT_SHIELD_OLLAMA_HOST: Ollama base URL, defaulthttp://localhost:11434AGENT_SHIELD_OLLAMA_MODEL: Ollama model name, defaultqwen3.5:0.8bAGENT_SHIELD_BACKEND_URL: backend base URL, defaulthttp://localhost:3000AGENT_SHIELD_CODEX_COMMAND: shell-split command used to start Codex, defaultcodexAGENT_SHIELD_CODEX_ARGS: default shell-split arguments passed to Codex
Codex argument compatibility:
- Legacy
--dangerously-skip-possible-errorsis automatically rewritten to--dangerously-bypass-approvals-and-sandbox - If
AGENT_SHIELD_CODEX_COMMANDresolves tocodexand no local binary exists, runtime falls back tonpx -y @openai/codex
Interception tuning
AGENT_SHIELD_INTERCEPT_MODE:mcp-targets,process-tree, orstrictAGENT_SHIELD_PROCESS_SCAN_INTERVAL_SEC: descendant scan cadence in seconds, default0.1, minimum0.05AGENT_SHIELD_LOCAL_APPROVE_MAX_SCORE: highest score eligible for local auto-approve, default25AGENT_SHIELD_LOCAL_DENY_MIN_SCORE: lowest score eligible for local auto-deny, default75AGENT_SHIELD_CLOUD_REVIEW_FAILURE_MODE: fallback decision for review-band backend failures,approveordeny, defaultapproveAGENT_SHIELD_BACKEND_DEGRADED_COOLDOWN_SEC: how long the wrapper suppresses backend escalation after a review-path backend failure, default30AGENT_SHIELD_RESPONSES_API_HOSTS: comma-separated hostnames treated as model-provider API destinations, defaultapi.openai.com
Logging and allowlists
AGENT_SHIELD_LOG_FILE: log path, default.agent-shield.logAGENT_SHIELD_WHITELIST_HOSTS: comma-separated host allowlist, case-insensitiveAGENT_SHIELD_WHITELIST_PORTS: comma-separated numeric port allowlistAGENT_SHIELD_WHITELIST_COMMAND_PATTERNS: comma-separated case-insensitive command substrings
Whitelist behavior:
- Backend and Ollama endpoints are auto-whitelisted, including localhost aliases such as
localhost,127.0.0.1, and::1 - For non-local backend or Ollama hosts, resolved IP addresses are also auto-whitelisted so established TLS sockets still bypass interception after DNS resolution
- Connection allowlisting checks both local and remote endpoints on each established socket, which prevents self-intercepts on the server side of backend or Ollama listener sockets
- Explicit whitelist environment variables extend the auto-whitelist
- Whitelisted commands skip process-spawn interception, and whitelisted endpoints skip connection interception
Monitoring scope:
- The wrapper watches descendant process spawns and established TCP connections only
- Connection inspection is based on
lsof; there is no UDP or packet-level capture MCPProxyServerexists as an optional helper for HTTP payload interception, butcli.pydoes not start it by default
Project layout
cli.py: command-line entrypoint and startup diagnosticsconfig.py: environment loading and configuration normalization.env-example: example local wrapper configurationmcp_interceptor.py: main process-tree monitor, connection classifier, and enforcement logicmcp_detector.py: MCP-related string and command detection helpersrisk_engine.py: Ollama request and risk result normalizationapi_client.py: backend health, intercept, polling, and session-result clienttests/: unit tests for config, backend client, risk parsing, and interceptor helpersBACKEND_API.md: backend contract consumed by this wrapper
Testing
Run the unit test suite with either command:
npm test
python3 -m unittest discover -s tests -p 'test_*.py'
The current tests are mock-heavy unit tests. They validate configuration parsing, request shaping, intercept classification, and result handling, but they do not spin up a live Codex process, Ollama instance, or backend service.
Backend API
See BACKEND_API.md for the exact request and response contract used by the wrapper.
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 vadian_shield-1.0.2.tar.gz.
File metadata
- Download URL: vadian_shield-1.0.2.tar.gz
- Upload date:
- Size: 44.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40cfe6d7b909392fbf7a240456076121eb85bc2d221fd2da579af831065f4d14
|
|
| MD5 |
a07cc8ecda8de7a12b2f786d29c06420
|
|
| BLAKE2b-256 |
713fc94c80b63429c3342a3e062b40a20909e30d8b554bf4f99018d89953ade4
|
File details
Details for the file vadian_shield-1.0.2-py3-none-any.whl.
File metadata
- Download URL: vadian_shield-1.0.2-py3-none-any.whl
- Upload date:
- Size: 54.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68753377bda984b8a985507bc7b38f4d80a6cfea9bdb6971ccccdeaa1f8b909d
|
|
| MD5 |
e39b9e9a59582010ac61ce60e14432b3
|
|
| BLAKE2b-256 |
27827a71643f8b9861c6016e77cda2f48a0bfc1a48eb0ad2e47e36c2ec1083b9
|