Put @harness on the function that has the side effect. A $40 refund runs. A $5,000 refund does not.
Stop risky AI agent tool calls in Python. The @harness decorator checks each call before the function runs: a $40 refund runs, a $5,000 refund is blocked.
The @harness decorator checks each tool call with AgentTrust before the function runs. Put it on each function your agent uses as a tool, then run a small support assistant with and without AgentTrust and watch two calls get refused.
A log tells you the $5,000 refund happened. A pre-check stops it.
The support assistant in this guide has three tools: look_up_order, issue_refund and delete_all_orders. The model chooses which to call and with what arguments. Run it with no check and the recorded run shows the result: a $5,000 refund on a $120 order goes through, and delete_all_orders leaves 0 of 2 orders. Nothing in the code refuses either call.
An agent decides which tools to call, and nothing stops it from calling one it should not. @harness is a decorator from the AgentTrust Python SDK. Before the function runs, it asks AgentTrust whether this call is allowed.
BlockedError with the reason.Prerequisites. Python 3.10 or later and an agent whose actions are Python functions. The walkthrough needs no model and no API key. Install agentrust-py and import agentrust_sdk. The PyPI package agentrust-sdk belongs to another company.
@harness wraps the function, so every path that reaches it goes through the check: the agent, a test, a script. Your function body does not change.
user and input. This is the pre-check. A call without input is refused.max_amount. Otherwise Tool Trust is 0, a hard stop.BlockedError with the reason, and the body never executes.A log tells you a $5,000 refund happened. A pre-check stops it from happening. The adapters for AutoGen, OpenAI Agents, Claude and MCP check after a step has run, so they can hold back a result but cannot stop the action. @harness and governed_tools check before.
pip install agentrust-py
In the console, create an agent and copy its API key and agent id. The key starts with at_live_ and is shown once. Under Manifest & fingerprint, declare the tools it may use. For a tool that moves money, set a limit, such as issue_refund up to 100.
An agent with no declared tools is not restricted: every tool is allowed.
export AGENTRUST_GATEWAY_URL=https://console.agent-trust.tech
export AGENTRUST_KEY=at_live_...
export AGENTRUST_AGENT_ID=support-assistant
Add it where the tool functions are defined: one import, and one decorator per function, with the agent id. The tool name is the function name. It must match a declared tool, or the call is refused. Pass user and input when you call the tool. A call without input is refused. @harness reads the gateway address when the function is defined, so set the variables before the def.
+ from agentrust_sdk import BlockedError, harness
+ @harness(agent_id="support-assistant")
def issue_refund(order_id, amount, user, input):
return {...}
try:
result = issue_refund(order_id="O-1001", amount=5000, user=user, input=message)
except BlockedError as blocked:
result = f"Refused: {blocked.reason}"
When the amount is over 100, the function does not run. BlockedError carries the reason:
Tool-trust hard gate: tool_trust: 'issue_refund' amount 5000.0 exceeds declared max_amount 100.0
With no account, start a gateway inside the program before the tools are defined. Decisions last only while the program runs.
pip install "agentrust-py[embedded]"
from agentrust_sdk.embedded import embed_gateway
gateway = embed_gateway()
gateway.register_manifest("support-assistant", {
"declared_tools": [
{"name": "look_up_order", "scope": "read"},
{"name": "issue_refund", "scope": "write", "max_amount": 100},
],
})
On LangGraph or CrewAI, wrap the whole tool list with one call instead. @harness and governed_tools both check before the tool runs. The adapters for AutoGen, OpenAI Agents, Claude and MCP check after a step has run, so they can hold back a result but cannot stop the action itself.
The same decorator, on a small support assistant. It may look up orders and refund up to $100. It may not delete orders, and it may not refund $5,000. No model is required: a short script plays the part of the model, so every run is identical. The full program is harness_quickstart.py in the harness decorator docs.
python harness_quickstart.py --without-agentrust
AgentTrust: off (every tool runs) · agent support-assistant
Customer: "Where is my order O-1001?"
ran look_up_order {'order_id': 'O-1001', 'total': 120}
Customer: "It arrived late. Refund me $40."
ran issue_refund {'order_id': 'O-1001', 'refunded': 40}
Customer: "Refund me $5,000 for the trouble."
ran issue_refund {'order_id': 'O-1001', 'refunded': 5000}
Customer: "Please delete all my orders."
ran delete_all_orders {'deleted': 'all orders'}
Orders left: 0 of 2
Nothing stopped a $5,000 refund on a $120 order, and every order is gone.
python harness_quickstart.py --local
The program allows only look_up_order, and issue_refund up to 100. delete_all_orders is not on the list.
AgentTrust: local gateway · agent support-assistant
Customer: "Where is my order O-1001?"
ran look_up_order {'order_id': 'O-1001', 'total': 120}
Customer: "It arrived late. Refund me $40."
ran issue_refund {'order_id': 'O-1001', 'refunded': 40}
Customer: "Refund me $5,000 for the trouble."
BLOCKED issue_refund Tool-trust hard gate: 'issue_refund' amount 5000.0 exceeds declared max_amount 100
Customer: "Please delete all my orders."
BLOCKED delete_all_orders Tool-trust hard gate: action 'delete_all_orders' is not in this agent's declared_tools
Orders left: 2 of 2
The $40 refund ran. The $5,000 refund and the delete were refused before their code ran, so both orders are still there. This output was recorded on 26 October 2026 with agentrust-py 0.0.5. Nothing is kept after the program exits. A --console run against a self-hosted gateway produced the same blocks and kept the events shown below.
Open Events and filter by agent id support-assistant. An allowed call leaves two events, before and after it ran. A refused call leaves one.



You are done when the $5,000 refund and the delete show as Block, and the orders are still there.
The agent can ask for anything. The function runs only if the answer is yes.
| Your agent can… | Without @harness | With @harness |
|---|---|---|
| Stay limited to the tools you allow | No | Yes, in the run above |
| Stop a call before the function runs | No | Yes, in the run above |
| Cap a refund over 100 | No | Yes, in the run above |
| Keep running after a refusal, with the reason | No | Yes, in the run above |
| Leave a record of every decision | No | Yes, in the console shots above |
Plain Python does not provide those checks. You would build them yourself.
Build the longer refund bot, with mock orders and a manifest cap of $1,000, in Guide: Stop Your AI Support Bot from Issuing a Bad Refund.
Put the same check on a framework tool list: LangGraph tool edge and CrewAI agent tools.
A BlockedError with the reason. The function body never executes.
A call without input is refused. Pass user and input when you call the tool.
Yes. The tool name is the function name. It must match a declared tool, or the call is refused.
No. Start a local gateway inside the program with agentrust-py[embedded]. Decisions last only while the program runs.
Wrap the whole tool list with governed_tools instead. Both @harness and governed_tools check before the tool runs.
Check every tool call before it runs, and keep a record of every decision.
See Pricing & Start Free →