Loading...
Loading...
Integrate Arcjet Guard into Python Strands Agents — wrap authored @tool with guard_tool, and put guard_hooks on Agent(hooks=) for unwrapped / MCP tools via BeforeToolCallEvent.cancel_tool. Use when asked to add Arcjet to strands / strands-agents, rate limit those tools, screen inbound messages, or block prompt injection / PII. This is Python strands, not JS @strands-agents/sdk.
npx skill4agent add arcjet/skills integrate-arcjet-guard-strands-agents-pyarcjet.guard.strands_agentsstrandsstrands-agents>=1.11.0,<2@arcjet/guard/strands-agents/v1arcjet.guard.strands_agents@arcjet/guard/strands-agents/v1arcjet[strands-agents]strands-agents = ["strands-agents>=1.11.0,<2"]pyproject.tomlguard_toolguard_hooksstrands_agent_context@toolAgent(hooks=)add_hook@toolguard_toolguard_hooksBeforeToolCallEvent.cancel_toolTruestrstrands_agent_contextinvocation_statetrace_idexamples/fastapi-strands-agents-guarda6308061BeforeToolCallEvent.cancel_toolguard_hooksDENYon_guard_error="deny"ArcjetDenialResultTruecancel_tool"allow"DENYguard()has_failed_open()event.interrupt()BeforeToolCallEvent.interrupt()human_inputhumanInTheLoopMiddlewareinterrupt()needsApprovalinterrupt()Agent(...)__call__stream_asyncaj.guard(...)guard()ALLOWdecision.has_failed_open()guard_toolguard_hooksguard_toolguard_hooksTokenBucketinvocation_stateguard_hooks(...)on_guard_error="deny""allow"@arcjet/guard/strands-agents/v1arcjet.guard.strands_agentsguard()Agent(...)__call__stream_asyncevent.interrupt()cancel_toolTruestrcancel_tooltrace_idagent.idguard_hooksguard_toolguard()arcjetarcjeta630806169b92757192f3f5cce2e305827b26567pip install "arcjet[strands-agents] @ git+https://github.com/arcjet/arcjet-py.git@a630806169b92757192f3f5cce2e305827b26567"import os
from arcjet.guard import launch_arcjet
aj = launch_arcjet(key=os.environ["ARCJET_KEY"])guard_toolfrom strands import Agent, tool
from arcjet.guard import DetectPromptInjection, TokenBucket, launch_arcjet
from arcjet.guard.strands_agents import guard_hooks, guard_tool, strands_agent_context
aj = launch_arcjet(key=os.environ["ARCJET_KEY"])
lookup_limit = TokenBucket(
label="order.looked-up",
bucket="lookups",
refill_rate=10,
interval_seconds=60,
max_tokens=10,
)
mcp_limit = TokenBucket(
label="mcp.invoked",
bucket="mcp-access",
refill_rate=20,
interval_seconds=60,
max_tokens=20,
)
inbound = DetectPromptInjection()
user_id = authenticated_user_id
@tool # event.interrupt() is HITL — not this policy gate
def lookup_order(order_id: str) -> dict:
"""Look up an order by ID."""
return {"order_id": order_id, "status": "shipped"}
lookup_order = guard_tool(
guard=aj,
tool=lookup_order,
action="order.looked-up",
rules=[lookup_limit(key=user_id, requested=1)],
on_guard_error="deny",
)guard_hooksmcp_tools = [] # from an MCP client you did not wrap with guard_tool
agent = Agent(
tools=[lookup_order, *mcp_tools],
# The agent-wide gate for tools guard_tool did not wrap.
# Already-wrapped tools are skipped, so Guard is not called twice.
# BeforeToolCallEvent.cancel_tool is True or a str (JSON of the payload).
hooks=[
guard_hooks(
guard=aj,
action="mcp.invoked",
rules=[mcp_limit(key=user_id, requested=1)],
session_id=conversation_id,
on_guard_error="deny",
),
],
)invocation_state = {"sessionId": conversation_id}
derived = strands_agent_context(invocation_state)
decision = await aj.guard(
label="message.received",
rules=[inbound(user_text)],
correlation_id=derived.correlation_id,
)
if decision.conclusion == "DENY":
raise RuntimeError("message blocked")
if decision.has_failed_open():
raise RuntimeError("inbound guard unavailable")
agent(user_text, invocation_state=invocation_state)strands_agent_contextinvocation_statecorrelationIdsessionIdrequestIdtrace_idagent.idguard_hooks(...)python -m py_compileguard_toolguard_hookscancel_toolTrueevent.interrupt()trace_idagent.idARCJET_KEYexamples/fastapi-strands-agents-guard