Skip to main content

fullcourtdefense-agent

FullCourtDefense checks every tool call of an AI agent against your organisation's action policy before the tool runs. This package is the small agent hook: standard library only (no httpx, requests or openai), so adding it to a deployed agent does not change the versions of anything the agent already uses.

Install

Pin it exactly. Do not add --hash: pip's hash checking is all-or-nothing, so one hashed line makes every other line of the file need a hash too, and an agent's unpinned requirements stop building.

fullcourtdefense-agent==0.2.2

Configure

Variable Meaning
FCD_SHIELD_ID The agent's shield (sh_...). Required.
FCD_SHIELD_KEY The shield key.
FCD_API_URL FullCourtDefense API (default https://api.fullcourtdefense.ai).
FCD_AGENT_ID The agent's identity in the console (also read: FULLCOURTDEFENSE_AGENT_ID, FCD_AGENT_NAME, FCD_WORKLOAD_NAME). Unset: the name of the service the process runs as (OTEL_SERVICE_NAME, Cloud Run K_SERVICE, AWS_LAMBDA_FUNCTION_NAME, Azure CONTAINER_APP_NAME / WEBSITE_SITE_NAME, ...), else the main script, so agents never collapse into unknown-agent. Every call says which (agentIdSource: config, env or default). A name passed in code (agent_id= or agent_name= on the call or on protect()) is declared: config.
FCD_FAIL_MODE What a block-mode shield does while FullCourtDefense cannot be reached. tiered (default when unset; owner decision 2026-10-27, replaces closed): only the agent's never-allowed rules (cached from the runtime bundle) stop a call; any other action runs, is logged as a warning and is reported to FullCourtDefense as "ran without a check" (ranDuringOutage) once it answers again, and a rule that needs a manager runs unattended with wouldHaveNeeded: "manager". The built-in emergency list no longer stops anything. closed: no tool runs. open: nothing is stopped. An unknown value is read as closed.
FCD_MODE_AT_PROTECT monitor or block: the shield's mode when the agent was protected. Used only until the live mode has been read once, so a monitor-mode agent that starts while the API is down still never blocks. Set it to avoid the one-time mode lookup on the first call (the cloud Protect and the init wizard already set it); without it the first call waits for that lookup (at most 1.5 s), so an Enforce shield's first call is checked. A check waits at most 1.5 s before the fail mode answers (timeout=); If FullCourtDefense hangs or cannot be reached, at most one call waits up to 1.5 s; after that, calls answer instantly (from the fail mode) until we're back. A hang or a network error opens the circuit breaker at once; while it is open one background probe runs every 10 s (calls never wait on it) and a successful probe restores live checks. HTTP answers (4xx / 5xx with a body) are handled as before.
FCD_SDK_SPOOL / FCD_SDK_SPOOL_DIR Records a short-lived process could not send before it ended (the end of the process waits at most 1.5 s for them) are kept in a small file in the user cache directory (~/.cache/fullcourtdefense/sdk-spool, %LOCALAPPDATA%, ~/Library/Caches) and sent by the next start with the same shield. FCD_SDK_SPOOL=off disables it; FCD_SDK_SPOOL_DIR moves it.
FCD_AUTOGUARD adk: add the gate to every Google ADK Runner at start-up (below). Unset: the start-up hook does nothing.
FCD_APPROVAL_MODE ADK plugin: block (default) stops a call that needs a manager's approval; wait holds it until a person decides (FCD_APPROVAL_TIMEOUT_S, default 900).

A monitor-mode shield never blocks and never waits: the tool runs at once and the call is recorded in the background.

Use

from fullcourtdefense_agent import ToolGate, ToolCallBlocked

gate = ToolGate.from_env()

# A function, checked before every call (sync or async def):
safe_refund = gate.protect(issue_refund, operation="refund")

# Or one call:
gate.guard_tool_call("issue_refund", lambda: issue_refund("o_1", 120),
                     tool_args={"order": "o_1", "amount": 120}, operation="refund")

A blocked call raises ToolCallBlocked; the tool did not run.

A wrong key, a refused or unknown shield, a refused request (HTTP 401 / 403 / 404 / 400) is not an outage. The fail mode does not apply: the tool does not run, the check is not reported later as "ran without a check", and FullCourtDefenseApiError (a ToolCallBlocked with status and code: FCD_UNAUTHORIZED, FCD_FORBIDDEN, FCD_SHIELD_NOT_FOUND, FCD_BAD_REQUEST) says what to change. It is also logged once on the fullcourtdefense logger. Any other 4xx except 408 / 429 (a 413, for example) is answered the same way. Only a real outage (no answer, 5xx, 408, 429) is answered by the fail mode.

On a monitor shield (owner decision O4) a refused key or shield does not stop the tool: the mode the gate knows (a live answer earlier in the process, else FCD_MODE_AT_PROTECT) is monitor, so the tool runs and the call is still reported, and FullCourtDefense raises a "wrong or missing shield key" alert. With no known mode the shield is treated as block.

Arguments too deep or too large to check (nested deeper than 32 levels, more than 20,000 values) are never sent: in block mode the call is stopped (ToolCallBlocked, "too large to check safely"), in monitor mode it runs and the record says what was not sent. Never an outage.

OpenAI Agents SDK and LangChain

from fullcourtdefense_agent.frameworks import protect_openai_agents_tool, protect_langchain_base_tool

agent = Agent(name="support", tools=[protect_openai_agents_tool(gate, lookup_customer)])  # still a FunctionTool
safe_sql = protect_langchain_base_tool(gate, sql_query)                                   # still a BaseTool

The full SDK (pip install fullcourtdefense) does the same with fcd.protect(tool).

Google ADK

from google.adk.apps import App
from fullcourtdefense_agent.adk import FcdToolGuardPlugin

app = App(name="billing", root_agent=root_agent, plugins=[FcdToolGuardPlugin()])

A blocked call is answered to the model as {"blocked": true, "reason": "...", "by": "FullCourtDefense"} and the tool does not run. Gemini's built-in tools (Google Search, built-in code execution, Vertex AI Search grounding) run inside Google and are not checked.

Start-up hook (no code change)

The wheel installs fullcourtdefense_agent_autoguard.pth into site-packages. Python runs its one line at start-up. Without FCD_AUTOGUARD it only reads that variable and stops. With FCD_AUTOGUARD=adk it waits for google.adk.runners to load and adds the plugin to every Runner the process creates, after the agent's own plugins. Security scanners flag start-up lines in .pth files as a supply-chain technique; this one is disclosed here, pinned by exact version and inert unless the variable is set. If the plugin cannot be added (an unsupported ADK version), the hook logs an error on the fullcourtdefense logger and stderr; it never stops the agent from starting.

What leaves the agent

The tool name, the operation, the agent identity, the session id and the tool arguments (the policy input; the server minimises them before storing). Never the tool's result and never the text of an error the tool raised.

Metadata

Release files for fullcourtdefense-agent 0.2.2

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

Source distribution (sdist)

Source distribution for fullcourtdefense-agent 0.2.2
File Size Uploaded Provenance Actions
fullcourtdefense_agent-0.2.2.tar.gz 89.5 kB With provenance Details for fullcourtdefense_agent-0.2.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fullcourtdefense-agent 0.2.2
File Interpreter ABI Platform Actions
fullcourtdefense_agent-0.2.2-py3-none-any.whl Python 3 none any Details for fullcourtdefense_agent-0.2.2-py3-none-any.whl

Total release size: 147.6 kB

Release files / fullcourtdefense_agent-0.2.2.tar.gz

Download URL fullcourtdefense_agent-0.2.2.tar.gz
Size 89.5 kB
Tags Source
SHA-256 checksum
How to use checksums
cfd69ea304193feded4ce21c7f3658ab37e96e36d453a7e772071967a1931ef0
BLAKE2b-256 checksum
How to use checksums
d8983d2ca5ac0f169dc06172bd41ca90c01896f85456d74fdf07160c1e0b9ded
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 28, 2026.

Transparency log

Release files / fullcourtdefense_agent-0.2.2-py3-none-any.whl

Download URL fullcourtdefense_agent-0.2.2-py3-none-any.whl
Size 58.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ee452e396c5793a63cb2f803ecf46385973d667e95abb71de144b493431745da
BLAKE2b-256 checksum
How to use checksums
03fdd995270a4084aba100897faf038485f17a5d3c879daff404bda5ac06eec5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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 Oct 28, 2026.

Transparency log

Security for fullcourtdefense-agent 0.2.2

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for fullcourtdefense_agent-0.2.2.tar.gz

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 Oct 28, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.2 This release

2 release files Full provenance

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