arcjet/skills

integrate-arcjet-guard-claude-agent-sdk-py

Integrate Arcjet Guard into the Python Claude Agent SDK — wrap authored @tool with guardtool, and use guardhooks for UserPromptSubmit inbound plus PreToolUse on unwrapped built-ins / MCP.

Zobacz źródło

Jak używać

  1. Skopiuj polecenie instalacji i sprawdź źródła przed instalacją we własnym środowisku agenta.
  2. W oryginale sprawdź wymagane narzędzia, dane dostępowe i obsługiwane środowiska.
  3. Opisz dane wejściowe i oczekiwany rezultat; porównaj wynik ze źródłami.
npx skills add https://github.com/arcjet/skills --skill integrate-arcjet-guard-claude-agent-sdk-py

Przed rozpoczęciem

  • Instalacja nie obejmuje subskrypcji modelu, limitu API ani uprawnień do plików.
  • Gwiazdki i instalacje pokazują aktywność, a nie niezależny test jakości lub bezpieczeństwa.
Oryginalny dokument Skill

Treść z repozytorium z zachowaniem nagłówków, przykładów, kodu, tabel, linków i obrazów.

Integrate Arcjet Guard into the Python Claude Agent SDK

arcjet.guard.claude_agent_sdk wraps the agent's existing Arcjet client. It never talks to the Arcjet API itself. Shared Guard fundamentals (client, rules, labels, decisions, capture, registration) live in ../arcjet/references/guards_python.md. Load that reference for anything that is not Claude Agent SDK-specific.

Official claude-agent-sdk>=0.2.127,<1 only — not the JS @anthropic-ai/claude-agent-sdk adapter (@arcjet/guard/claude-agent-sdk/v0, docs https://docs.arcjet.com/guards/claude-agent-sdk/), not Vercel AI, not community forks, and not Claude Managed Agents hosted client.beta.sessions (arcjet.guard.claude_managed_agents). Importing arcjet.guard.claude_agent_sdk does not load LangChain, CrewAI, or the JS adapter. The extra is safe (no chromadb).

Exports: guard_tool, guard_hooks, claude_agent_context. Authored @tool + create_sdk_mcp_server for tools you own; guard_hooks for inbound UserPromptSubmit and unwrapped built-ins / MCP PreToolUse. There is no inbound helper and no guard_can_use_tool.

Three surfaces, one decision rule:

  • An authored `@tool` → guard_tool. Denial is JSON-in-content +

is_error: True. Python does not forward structuredContent (that is the JS adapter). Do not raise.

  • Inbound text → guard_hooks UserPromptSubmit (`decision:

"block"`). That is the only place a turn can be declined before the model reads the prompt.

  • Built-ins / unwrapped MCP → guard_hooks PreToolUse

(permissionDecision: "deny"). PostToolUse is capture only.

  • Correlation → claude_agent_context reads a caller-owned UUID

session_id. It never mints.

Docs: https://docs.arcjet.com/guards/claude-agent-sdk/.

Unwrapped tools deny on PreToolUse via guard_hooks

Built-ins (Bash, Write, …) and MCP tools you did not pass through guard_tool have no authored handler. PreToolUse is the only deny for those. PostToolUse cannot undo a tool that already ran. List every guard_tool wrapper in exclude or each authored tool is guarded twice (two round trips, two quota units). Entries match the reported name: pass {"server": "support", "name": "lookup_order"} for an authored MCP tool (it resolves to mcp__support__lookup_order) and a bare string for a built-in such as "Bash". A bare authored name deliberately does not match every server's tool of that name.

Authored @tool denial is JSON-in-content + is_error: True

guard_tool wraps the @tool definition so the handler never runs on DENY (or unevaluated Guard under the default on_guard_error="deny"). The model receives the ArcjetDenialResult as JSON text on content with is_error: True. Do not set structuredContent (JS only). Do not raise: a throw is a raw exception; omitting is_error looks like success. Same fail-closed default as #196: only "allow" fails open; a DENY always blocks. Core guard() still fails open.

can_use_tool is not a policy gate

can_use_tool is human-in-the-loop. allowed_tools, allow rules, and bypassPermissions / acceptEdits skip it. Same trap as CrewAI human_input, JS canUseTool, LangGraph interrupt(), and OpenAI Agents needs_approval. There is no inbound helper — screen prompt injection on guard_hooks UserPromptSubmit.

Questions to ask the human first

Ask only what you cannot infer from the code; suggest defaults.

  1. Which tools are risky (external side effects, irreversible, spends

money, sends messages)? Those get guard_tool. Built-ins and unwrapped MCP get guard_hooks PreToolUse.

  1. What limits? (e.g. "10 lookups/min per user" → TokenBucket.)
  2. Who is the user for metadata — an opaque user/tenant ID (never PII)?

Default: none. Session id is the correlation id, not the user. The Claude SDK requires session_id to be a UUID and allows a given id to be created only once — later turns use resume.

  1. Is an Arcjet outage unacceptable? Every helper defaults to

on_guard_error="deny". Ask explicitly about inbound UserPromptSubmit: failing closed there means the agent stops answering, so "allow" is a routine and legitimate choice at that one call site.

The things readers get wrong

  1. This is not the JS adapter and not Claude Managed Agents. Do not

import @arcjet/guard/claude-agent-sdk/v0 or arcjet.guard.claude_managed_agents.

  1. There is no inbound helper. Screen on UserPromptSubmit via

guard_hooks.

  1. `can_use_tool` is HITL, not policy.
  2. Python does not forward `structuredContent`. Authored deny is

JSON-in-content + is_error: True.

  1. `session_id` is a UUID, and only once. Later turns: resume.

claude_agent_context never mints.

  1. List every `guard_tool` wrapper in `exclude`. Missing exclude

double-calls Guard.

  1. Key rate limits on the authenticated caller, not a model-supplied

order id. Hand query / create_sdk_mcp_server the copy guard_tool returns.

  1. Do not hand-wrap every tool with raw `guard()`.
  2. **Isolation needs both setting_sources=[] and

strict_mcp_config=True.** One without the other still exposes an unguarded path.

  1. Two distinct ids. ClaudeAgentOptions.session_id is a unique

UUID per run (resume later). The Guard session_id is a long-lived actor id. A missing decision is not a denial.

Step 1: Install and find the guard client

Requires PyPI arcjet 1.1.0 (the arcjet[claude-agent-sdk] extra is not in 1.0.0):

bash
pip install "arcjet[claude-agent-sdk]"

If the agent has no guard client yet, launch one once at module scope:

python
import os
from arcjet.guard import launch_arcjet

aj = launch_arcjet(key=os.environ["ARCJET_KEY"])

Step 2: Gate authored tools — guard_tool

python
from claude_agent_sdk import ClaudeAgentOptions, create_sdk_mcp_server, query, tool
from arcjet.guard import DetectPromptInjection, TokenBucket, launch_arcjet
from arcjet.guard.claude_agent_sdk import guard_hooks, guard_tool

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
# Caller-owned UUID for this conversation. Later turns: resume=session_id.
session_id = conversation_id

@tool("lookup_order", "Look up an order by number", {"order_id": str})
async def lookup_order(args: dict) -> dict:
    return {
        "content": [{"type": "text", "text": f"{args['order_id']}: 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",
)
# can_use_tool=... is HITL — not this policy gate

Step 3: Screen inbound and gate unwrapped tools — guard_hooks

python
async for message in query(
    prompt=user_text,
    options=ClaudeAgentOptions(
        session_id=session_id,  # later turns: resume=session_id instead
        mcp_servers={
            "support": create_sdk_mcp_server(name="support", tools=[lookup_order]),
        },
        hooks=guard_hooks(
            guard=aj,
            session_id=session_id,
            exclude=[{"server": "support", "name": "lookup_order"}],
            inbound={
                "action": "message.received",
                "rules": lambda ctx: [inbound(ctx["prompt"])],
            },
            action="mcp.invoked",
            rules=lambda ctx: [mcp_limit(key=user_id, requested=1)],
            on_guard_error="deny",
        ),
    ),
):
    pass

Step 4: Correlation

claude_agent_context(session_id=session_id) reads that same caller-owned UUID (hook session_id first, then options.session_id). It never mints. Pass the id you already have into guard_hooks and ClaudeAgentOptions — do not derive a new one per turn. The Claude SDK also requires session_id to be a UUID and allows a given id to be created only once — later turns use resume.

Verify the integration

  1. python -m py_compile (or the project's type-check) passes.
  2. Exercise inbound PI (UserPromptSubmit block), an authored-tool deny

(JSON-in-content + is_error: True, no structuredContent), a built-in / unwrapped MCP deny (PreToolUse), a rate limit, and fail-closed (an unreachable guard). Confirm a wrapped tool produces one guard decision per invocation — a second decision under the PreToolUse action means a missing exclude entry.

  1. Confirm in the Arcjet Console / CLI that decisions share the

caller-owned UUID session_id.

  1. Manual E2E with a real ARCJET_KEY is still-to-verify until you run it.

The example fastapi-claude-agent-sdk-guard stays with Runtime — do not invent a new example name. Do not add an example in this skills repo.

z tego samego repozytorium

Więcej Skills

Wszystkie Skills
arcjet
Społeczność

integrate-arcjet-guard-crewai

Integrate Arcjet Guard into official CrewAI — register process-wide PRETOOLCALL with registerarcjethooks, or wrap a standalone BaseTool you call yourself with guardtool. Use when asked to add Arcjet to CrewAI, LiteAgent, crew-injected tools, rate limit those tools, screen inbound messages before kickoff, or block prompt injection / PII. Official crewai only; not an npm CrewAI port and not LangChain Crew wrappers.

instalacje
1
GitHub Stars
4
Aktualizacja
7 paź
arcjet
Społeczność

integrate-arcjet-guard-google-adk-py

Integrate Arcjet Guard into Python Google ADK — assign guardtool to LlmAgent(beforetoolcallback=), put guardplugin first on Runner(plugins=) so a deny dict with arcjetDenied skips the tool, and read a caller-owned id via googleadkcontext. Use when asked to add Arcjet to google-adk, Google ADK Python, rate limit those tools, screen inbound messages, or block prompt injection / PII. This is Python google-adk 2.x, not JS @google/adk and not @google/genai.

instalacje
1
GitHub Stars
4
Aktualizacja
7 paź
arcjet
Społeczność

integrate-arcjet-guard-openai-agents-py

Integrate Arcjet Guard into Python OpenAI Agents — wrap FunctionTool / functiontool with guardtool and read a caller-owned session or conversation id via openaiagentscontext. Use when asked to add Arcjet to openai-agents, Runner.run, rate limit those tools, screen inbound messages, or block prompt injection / PII. This is Python openai-agents, not the JS @openai/agents adapter.

instalacje
1
GitHub Stars
4
Aktualizacja
7 paź
arcjet
Społeczność

integrate-arcjet-guard-strands-agents-py

Integrate Arcjet Guard into Python Strands Agents — wrap authored @tool with guardtool, and put guardhooks on Agent(hooks=) for unwrapped / MCP tools via BeforeToolCallEvent.canceltool. 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.

instalacje
1
GitHub Stars
4
Aktualizacja
7 paź