{"slug": "an-action-firewall-for-mcp-oauth-says-who-not-whether-the-call-should-run", "title": "An action firewall for MCP – OAuth says who, not whether the call should run", "summary": "AgenticDome released agenticdome-python-sdk, an official Python SDK and middleware package that enforces deterministic security controls—prompt ingress, tool execution, agent-to-agent handoffs, and output egress—across fifteen AI runtimes including CrewAI, LangGraph, and Microsoft AutoGen. The SDK supports local simulation mode without an account or network connection, and integrates with MCP hosts via a dedicated Action Firewall guide. The package is available on PyPI and includes framework integration guides and a production playbook.", "body_md": "[Source and examples](https://github.com/agenticdome/agenticdome-python-sdk) · [Framework integration guides](https://github.com/agenticdome/agenticdome-python-sdk/tree/main/docs/frameworks) · [MCP integration guide](https://github.com/agenticdome/agenticdome-python-sdk/blob/main/docs/mcp-integration.md) · [Issue tracker](https://github.com/agenticdome/agenticdome-python-sdk/issues) · [Security policy](https://github.com/agenticdome/agenticdome-python-sdk/blob/main/SECURITY.md) · [PyPI package](https://pypi.org/project/agenticdome-python-sdk/)\n\nProduction-grade security guardrails, DLP, tool authorization, and cryptographically verified multi-agent delegation for Python autonomous AI runtimes.\n\n`agenticdome-python-sdk`\n\nis the official Python SDK and middleware package for [AgenticDome](https://agenticdome.io). It enforces deterministic security controls at every boundary your agents cross — prompt ingress, tool execution, agent-to-agent handoffs, and output egress — using the tenant's assigned AgenticDome runtime sidecar.\n\n**One security pattern, fifteen runtimes:** CrewAI · PydanticAI · LangGraph/LangChain · Microsoft Agent Framework · Microsoft AutoGen · Microsoft AI Foundry · OpenAI Agents SDK · Claude Agent SDK · Hugging Face smolagents · Agno · Google ADK · LlamaIndex · AWS Bedrock · MCP hosts/gateways · custom Python.\n\n```\npip install agenticdome-python-sdk\nexport AGENTICDOME_MODE=local_sim\n```\n\nNo account, API key, tenant, network connection, or third-party framework package is required.\n\n```\nagenticdome-demo --framework langgraph --scenario both\n```\n\nLook for `ALLOWED — TOOL WOULD EXECUTE`\n\nfollowed by `BLOCKED — TOOL WOULD NOT EXECUTE`\n\n.\n\nOffline demonstration—not runtime evidence.This command evaluates two fixed onboarding scenarios with a deterministic, bundled public baseline. It does not contact AgenticDome, load tenant policy, execute either tool, or instantiate a LangGraph graph.`--framework langgraph`\n\nlabels the example payload and points you to the matching integration; it is not a LangGraph integration test.\n\n```\nagenticdome-demo --list-frameworks\nagenticdome-demo --framework all --scenario both\n```\n\nYou can also open the [framework example gallery](/agenticdome/agenticdome-python-sdk/blob/main/examples/README.md) and run the individual example matching your stack. Before editing production code, choose the dedicated [framework integration guide](/agenticdome/agenticdome-python-sdk/blob/main/docs/frameworks/README.md), then use the [production integration playbook](/agenticdome/agenticdome-python-sdk/blob/main/examples/PRODUCTION_INTEGRATION.md) for the cross-framework attachment and proof checklist. MCP host and gateway developers can follow the dedicated [MCP Action Firewall guide](https://github.com/agenticdome/agenticdome-python-sdk/blob/main/docs/mcp-integration.md).\n\nWhen you are ready to test real tenant policy, remove `AGENTICDOME_MODE=local_sim`\n\n, obtain the assigned runtime sidecar URL, Runtime/SDK API key and tenant ID from AgenticDome, and configure:\n\n```\nunset AGENTICDOME_MODE\nexport AGENTICDOME_API_BASE=\"https://your-assigned-sidecar.example\"\nexport AGENTICDOME_API_KEY=\"your_runtime_sdk_key\"\nexport AGENTICDOME_TENANT_ID=\"your_tenant_id\"\nagenticdome-demo --framework langgraph --scenario both --live\n```\n\nWith `--live`\n\n, the same fixed scenarios are sent to the actual assigned\nAgenticDome runtime sidecar, so their verdicts come from the customer's tenant\npolicy and engine. The live demo still does not instantiate LangGraph or prove\nthat an application has attached the framework adapter at every execution\nboundary. Use the framework guide to attach the adapter, test the real\napplication path, and use AgenticDome Runtime Assurance for production\nevidence.\n\n```\n# The 30-second version: block a prompt-injected refund before it executes.\nimport agenticdome_sdk.crewai   # registers global CrewAI security hooks\n\ncrew = Crew(agents=[manager, specialist], tasks=[task])\nresult = crew.kickoff()          # hostile prompts, unsafe tools, and rogue\n                                 # delegations are now BLOCKED before execution\n```\n\n[Five-minute developer trial](#five-minute-developer-trial)[Why AgenticDome](#why-agenticdome)[How It Works](#how-it-works)[Quickstart](#quickstart)[Installation](#installation)[Configuration](#configuration)[Choosing Your Integration Point](#choosing-your-integration-point)—[dedicated framework guides](/agenticdome/agenticdome-python-sdk/blob/main/docs/frameworks/README.md)[Framework Integrations](#framework-integrations)[Core SDK Client (Custom Runtimes)](#core-sdk-client-custom-runtimes)[Production Deployment](#production-deployment)[Source Installation and Verification](#source-installation-and-verification)[Licensing](#licensing)\n\nAn agent can be hijacked while holding valid tokens, approved tools, and fully authorized paths. Legacy security sees a compliant request; the business sees a breach. AgenticDome adds an intent-aware enforcement layer at the exact boundaries where agents act:\n\nGive every agent and workload a distinct principal and keep your identity provider authoritative for authentication, scopes, conditional access, credential lifecycle, and downstream entitlements. Then pass the identity the application has actually authenticated into AgenticDome alongside the agent, purpose, tool, final arguments, session, and delegation context. A valid token proves access authority; it does not prove that a prompt-influenced action is appropriate. AgenticDome complements IAM by evaluating that action at the application-controlled execution boundary. It does not issue enterprise identities or make a stolen credential safe.\n\nFor the architectural rationale, see\n[An agent principal proves who—not why](https://www.agenticdome.io/research/agent-principal-identity).\n\n| Control | What it stops | Where it runs |\n|---|---|---|\nPrompt ingress guardrails |\nPrompt injection, jailbreaks, system-prompt extraction, instruction override, policy bypass | Before agent/LLM execution |\nTool & skill authorization |\nUnauthorized or out-of-policy tool calls, evaluated on agent identity, tool name, arguments, session, source metadata, and delegation chain | Before tool execution |\nCryptographic delegation handoffs |\nConfused-deputy attacks, lateral privilege escalation, stolen-token replay between agents | At every manager→specialist handoff |\nInline output DLP |\nLeakage of PII, emails, phone numbers, API keys, access tokens, cloud credentials, corporate secrets, compliance-sensitive records | Before output is persisted, displayed, or re-enters the agent loop |\nFail-safe runtime behavior |\nSilent security bypass when the assigned runtime is unavailable | Configurable fail-closed (production) / fail-open (dev) |\n\n**Delegation tokens are the differentiator.** Every authorized handoff issues a decision token that binds the originating human subject (when present), the ordered and nested agent actor chain, source manager and target specialist, exact tool name and arguments, session ID, trace and lineage root/parent IDs, intent digest, policy binding, authorized scopes, trust epochs, tenant context, and the policy decision itself. Execution verification consumes the server-side call budget and checks revocation state — a token authorizes one exact action, once.\n\nOptional proof-of-possession hardens this further: a DPoP-style signed JWT is tied to the decision-token hash, so sending a public-key thumbprint alone is never treated as proof.\n\n```\npip install \"agenticdome-python-sdk[pop]\"\npython\nfrom agenticdome_sdk import create_dpop_proof, generate_rsa_proof_key\n\nproof_key = generate_rsa_proof_key()\n# Pass proof_key[\"thumbprint\"] as proof_thumbprint when authorizing.\nproof = create_dpop_proof(\n    proof_key[\"private_key_pem\"],\n    access_token=decision_token,\n    method=\"POST\",\n    uri=\"/a2a\",\n)\nclient.a2a_verify_decision_token_rpc(\n    decision_token,\n    tool_name=tool_name,\n    tool_args=tool_args,\n    agent_id=worker_id,\n    source_agent_id=manager_id,\n    proof_token=proof,\n)\n```\n\nAgenticDome uses a **hybrid split-plane architecture**. Your application executes agents, tools and workflows. The SDK protects the application-controlled boundaries and sends live policy checks to the tenant's assigned runtime sidecar. The management console distributes configuration to that runtime out of band; it is not the per-action SDK endpoint.\n\n```\nManagement console / control plane\n        |\n        | policy and tenant configuration (out of band)\n        v\nTenant-assigned runtime sidecar <------ live policy checks ------+\n        |                                                        |\n        +---------------- verdict / authorization -------------->|\n                                                                 |\nCustomer application                                             |\n  user input -> agent -> tool / MCP -> agent -> output            |\n                 ^         ^                    ^                  |\n                 +---------+--------------------+------------------+\n                     AgenticDome SDK protection boundaries\n```\n\nThe SDK must be attached in application code; setting environment variables alone does not intercept a framework. The assigned sidecar authenticates the tenant and evaluates live requests. Tools still execute in the customer's environment or selected provider unless a separate execution service has been deliberately configured.\n\n**Managed service:** AgenticDome assigns the tenant a managed sidecar in the selected supported geographic region, subject to availability and the customer's plan or contract.**Sovereign deployment:** the runtime is deployed within the contracted customer-controlled boundary, such as a dedicated VPC, customer cloud, or on-premises environment.\n\nThe SDK does not choose or change runtime placement; it connects to the\ntenant-specific API base supplied during onboarding. See\n[Runtime location and Redis responsibilities](https://github.com/agenticdome/agenticdome-python-sdk/blob/main/docs/runtime-deployment.md) for\nthe deployment boundary and customer responsibilities.\n\n| Persona / component | Responsibilities | Commercial model |\n|---|---|---|\nEnterprise / organization |\nHosts the local agent runtime. Uses the AgenticDome console to create policies, obtain a Tenant ID, generate API keys, and monitor security events. | Paid subscriber (SaaS license or API volume) |\nAgent / tool developer |\nBuilds tools, skills, agents, and workflow components. Uses the SDK to support secure tool calls, delegation metadata, and DLP-aware outputs. | Free ecosystem partner — no subscription required |\nThis Python SDK |\nRuns inside the application process. Protects supported framework boundaries, calls the assigned runtime sidecar, and enforces returned policy results. | Runtime security utility |\nAssigned runtime sidecar |\nAuthenticates the tenant and evaluates live guardrail, tool, delegation and output requests using distributed policy. | Runtime enforcement service |\nManagement console / control plane |\nManages tenant configuration, policy distribution, governance workflows and evidence. It is not the SDK's per-action API URL. | Management plane |\n\nStart with a network-free simulation, then connect the same SDK to your assigned runtime sidecar for real tenant enforcement.\n\n**1. Try the installed local simulation.** No account, API key, tenant, network call, telemetry, or framework package is required:\n\n```\npip install agenticdome-python-sdk\n# Label the fixed demonstration as LangGraph and show one ALLOWED and one BLOCKED path.\nagenticdome-demo --framework langgraph --scenario both\n\n# Or repeat the fixed demonstration under every supported framework label.\nagenticdome-demo --framework all --scenario both\nagenticdome-demo --list-frameworks\n```\n\nThis is visibly labelled **LOCAL SIMULATION — NOT CLOUD ENFORCEMENT**. It\nevaluates two fixed inputs with a small deterministic bundled baseline through\nthe public core-client response shape. The `--framework`\n\noption changes the\npayload label and integration guidance; it does not import, instantiate, or run\nthe selected framework. The simulation does not load tenant policy, issue\nsigned decision tokens or execution receipts, write cloud evidence, or provide\nruntime assurance. To exercise a real adapter inside your own process without\ncredentials, set `AGENTICDOME_MODE=local_sim`\n\nin that application or framework\nexample. The SDK refuses that mode when `AGENTICDOME_PRODUCTION_MODE=true`\n\n.\n\nBrowse the public [ examples/](/agenticdome/agenticdome-python-sdk/blob/main/examples/README.md) gallery for a runnable allowed/blocked example for CrewAI, PydanticAI, LangGraph, Microsoft Agent Framework, AutoGen, AI Foundry, OpenAI Agents, Claude, smolagents, Agno, Google ADK, LlamaIndex, Bedrock, MCP, and custom Python. Local blocked/redacted results emit safe terminal logs containing verdict metadata only—not raw prompts, arguments, keys, or secrets.\n\n**2. Onboard for real enforcement.** Create an account in the AgenticDome Management Console, obtain your tenant identifier and Runtime / SDK API key, and identify the tenant's assigned runtime sidecar.\n\n**3. Install** the SDK with the extra matching your framework:\n\n```\npip install \"agenticdome-python-sdk[crewai]\"     # or pydanticai, langgraph, ...\n```\n\n**4. Configure** the three required production environment variables (live clients fail with a configuration error rather than silently running unprotected):\n\n```\n# Tenant runtime sidecar URL. Do not use the control-plane website URL here.\nexport AGENTICDOME_API_BASE=\"https://demo-sidecar.agenticdome.io\"\nexport AGENTICDOME_API_KEY=\"your_api_key_abc123...\"\nexport AGENTICDOME_TENANT_ID=\"your_tenant_id_xyz789...\"\n```\n\n**5. Attach** AgenticDome at your framework's boundary — environment variables alone never intercept execution; every framework needs its one-time code attachment (see [Choosing Your Integration Point](#choosing-your-integration-point)):\n\n``` python\n# CrewAI example: one import in your bootstrap, before crews are built.\nimport agenticdome_sdk.crewai\n\nfrom crewai import Crew\ncrew = Crew(agents=[manager, specialist], tasks=[task])\nresult = crew.kickoff()\n```\n\n**6. Verify** locally or against the real assigned sidecar:\n\n```\n# Local, deterministic and network-free.\nagenticdome-demo --framework crewai --scenario both\nagenticdome-demo --framework langgraph --scenario both\nagenticdome-demo --framework claude --scenario both\nagenticdome-demo --framework smolagents --scenario both\nagenticdome-demo --framework all --scenario both\n\n# Additional blocked-only examples for focused attack demonstrations.\nagenticdome-demo --framework claude --scenario metadata_exfil\nagenticdome-demo --framework smolagents --scenario metadata_exfil\n\n# Live: uses AGENTICDOME_API_BASE/API_KEY/TENANT_ID and the assigned sidecar.\nagenticdome-demo --framework langgraph --scenario safe_lookup --live\n```\n\nEvery Python integration listed below is selectable through `agenticdome-demo --framework ...`\n\n; the demo prints the correct package extra and integration import for that framework. Local simulation proves SDK compatibility and control flow only. The live command proves the configured tenant-sidecar path.\n\nInstall the core SDK alone for custom runtimes, or add the extra for your framework:\n\n```\npip install agenticdome-python-sdk\n```\n\n| Target runtime | Command |\n|---|---|\n| CrewAI | `pip install \"agenticdome-python-sdk[crewai]\"` |\n| PydanticAI | `pip install \"agenticdome-python-sdk[pydanticai]\"` |\n| LangGraph / LangChain | `pip install \"agenticdome-python-sdk[langgraph]\"` |\n| Microsoft Agent Framework | `pip install \"agenticdome-python-sdk[microsoft]\"` |\n| Microsoft AutoGen AgentChat / Core (Python 3.10+) | `pip install \"agenticdome-python-sdk[autogen]\"` |\n| Microsoft AI Foundry | `pip install \"agenticdome-python-sdk[foundry]\"` |\n| OpenAI Agents SDK | `pip install \"agenticdome-python-sdk[openai-agents]\"` |\n| Anthropic Claude Agent SDK | `pip install \"agenticdome-python-sdk[claude]\"` |\n| Hugging Face smolagents | `pip install \"agenticdome-python-sdk[smolagents]\"` |\n| Agno | `pip install \"agenticdome-python-sdk[agno]\"` |\n| Google ADK | `pip install \"agenticdome-python-sdk[google-adk]\"` |\n| LlamaIndex | `pip install \"agenticdome-python-sdk[llamaindex]\"` |\n| AWS Bedrock / Bedrock Agents | `pip install \"agenticdome-python-sdk[bedrock]\"` |\n| MCP host / gateway | `pip install \"agenticdome-python-sdk[mcp]\"` |\n| Optional cross-process delegation store | `pip install \"agenticdome-python-sdk[redis]\"` |\n| Proof-of-possession helpers | `pip install \"agenticdome-python-sdk[pop]\"` |\n| All optional integrations | `pip install \"agenticdome-python-sdk[all]\"` |\n\nSome adapters are dependency-light at import time: Google ADK, LlamaIndex, Bedrock, MCP, and Microsoft helpers can wrap local boundaries without forcing one exact runtime stack. Install the framework packages your application actually uses.\n\nAgenticDome supports framework versions inside the dependency ranges declared by the published package. Check the package metadata and compatibility table before upgrading an integration, especially for multi-package stacks such as LangGraph/LangChain.\n\nThis means upgrading support for the latest framework does not silently drop customers on the previously certified version. Versions below the displayed certified floor are not claimed as supported until they are tested. Customers managing framework dependencies themselves may install the core SDK without an extra, but their framework version must still be inside the certified range for a production support claim.\n\nAgenticDome has two setup layers, and both are required:\n\n**Configuration layer**— environment variables that every framework reads at runtime. Global to the process, container, worker, or serverless function.**Code integration layer**— the framework boundary where AgenticDome is attached, imported, or wrapped (next section).\n\nEnvironment variables do **not** intercept framework execution by themselves.\n\nEvery supported Python integration inherits the same credential-free, network-free simulator:\n\n```\nexport AGENTICDOME_MODE=\"local_sim\"\n```\n\nNo API base, API key, tenant ID, or third-party framework package is needed to run the public demonstration. `ALLOWED`\n\ndecisions are logged at `INFO`\n\n; `BLOCKED`\n\nand `REDACTED`\n\ndecisions are logged at `WARNING`\n\nso they are visible in a normal terminal. Logs contain decision metadata only and exclude raw prompt text and tool arguments.\n\nLocal simulation is deliberately limited: it uses the bundled public baseline, does not execute tools, does not use tenant policy, topology, signed provenance, runtime telemetry, decision tokens, or execution receipts, and cannot satisfy an enforced execution broker. It is refused whenever `AGENTICDOME_PRODUCTION_MODE=true`\n\n.\n\n```\nexport AGENTICDOME_API_BASE=\"https://demo-sidecar.agenticdome.io\"   # tenant runtime sidecar, not the console URL\nexport AGENTICDOME_API_KEY=\"your_api_key_abc123...\"\nexport AGENTICDOME_TENANT_ID=\"your_tenant_id_xyz789...\"\nexport AGENTICDOME_FAIL_CLOSED=\"true\"\nexport AGENTICDOME_REDACT_PII=\"true\"\nexport AGENTICDOME_REDACT_SECRETS=\"true\"\nexport AGENTICDOME_BLOCK_ON_SENSITIVE_OUTPUT=\"false\"\nexport AGENTICDOME_REQUIRE_TOKEN=\"true\"\nexport AGENTICDOME_REPORT_INCIDENTS=\"true\"\n```\n\nRedis is **not required for normal SDK policy calls** or when using an assigned\nmanaged sidecar. Add customer-managed Redis only when manager-to-specialist\ndelegation is authorised in one application process, worker, or pod and its\none-time handoff state must be consumed in another:\n\n```\nexport AGENTICDOME_REDIS_URL=\"redis://redis.internal:6379/0\"\nexport AGENTICDOME_REDIS_KEY_PREFIX=\"AgenticDome:production:handoff\"\n```\n\n| Runtime style | Put AgenticDome config here |\n|---|---|\n| Local development | Shell exports, `.env` , direnv, or your IDE run configuration |\n| Docker / Compose | `environment:` entries, `env_file:` , or secret-mounted environment variables |\n| Kubernetes | `Secret` / `ConfigMap` values injected into the deployment or job |\n| CI/CD workers | Pipeline secret variables |\n| Celery / RQ / background workers | Worker process environment — not only the web process |\n| Serverless | Function environment variables or secret manager bindings |\n\n```\nexport AGENTICDOME_PLATFORM=\"crewai\"                  # runtime platform label used in policy context\nexport AGENTICDOME_REQUIRE_SESSION_ID=\"false\"         # require explicit session IDs (vs fallback local IDs)\nexport AGENTICDOME_DEFAULT_TOOL_PLATFORM=\"unknown\"    # fallback platform for tools\nexport AGENTICDOME_HANDOFF_TOKEN_TTL_S=\"900\"          # delegation token lifetime in seconds\nexport AGENTICDOME_BLOCKED_INCIDENT_SEVERITY=\"medium\" # default severity for incident reports\nexport AGENTICDOME_PRODUCTION_MODE=\"false\"            # production hardening (stable session ID enforcement)\n```\n\n**Full configuration reference — core variables**\n\n| Environment Variable | Type | Default | Description |\n|---|---|---|---|\n`AGENTICDOME_API_BASE` |\nstring | required | Tenant runtime sidecar origin, e.g. `https://demo-sidecar.agenticdome.io` . Separate from the control-plane console URL. |\n`AGENTICDOME_API_KEY` |\nstring | required | API key generated in the AgenticDome console. |\n`AGENTICDOME_TENANT_ID` |\nstring | required | Tenant or organization isolation namespace. |\n`AGENTICDOME_MODE` |\n`live` / `local_sim` |\n`live` |\nSelects real sidecar enforcement or the credential-free, network-free demonstration evaluator. `local_sim` is refused in production mode. |\n`AGENTICDOME_PLATFORM` |\nstring | framework-specific | Runtime platform label included in policy context. |\n`AGENTICDOME_TIMEOUT_S` |\ninteger | `20` |\nHTTP timeout in seconds for SDK calls. |\n`AGENTICDOME_FAIL_CLOSED` |\nboolean | `true` |\nBlocks execution if security checks fail. |\n`AGENTICDOME_REDACT_PII` |\nboolean | `true` |\nEnables PII redaction for output review. |\n`AGENTICDOME_REDACT_SECRETS` |\nboolean | `true` |\nEnables secret and credential redaction. |\n`AGENTICDOME_BLOCK_ON_SENSITIVE_OUTPUT` |\nboolean | `false` |\nBlocks entire output when sensitive content is detected. |\n`AGENTICDOME_REQUIRE_TOKEN` |\nboolean | `true` |\nRequires delegated specialist executions to include a token. |\n`AGENTICDOME_REQUIRE_SESSION_ID` |\nboolean | framework-specific | Requires explicit session ID for strict audit mapping. |\n`AGENTICDOME_DEFAULT_TOOL_PLATFORM` |\nstring | `unknown` / `python` |\nFallback platform for tools. |\n`AGENTICDOME_HANDOFF_TOKEN_TTL_S` |\ninteger | `900` |\nDelegation token TTL in seconds. |\n`AGENTICDOME_REDIS_URL` |\nstring | empty | Optional customer-application Redis URL, needed only when one-time delegation state must cross processes, workers, or pods. It is unrelated to the managed sidecar's internal backing services. |\n`AGENTICDOME_REDIS_KEY_PREFIX` |\nstring | framework-specific | Optional key prefix for the customer application's cross-process delegation store. |\n`AGENTICDOME_TOKEN_HMAC_SECRET` |\nstring | empty | Optional secret-manager value used by the SDK to protect shared delegation state. Applications should not inspect or construct that state. |\n`AGENTICDOME_PRODUCTION_MODE` |\nboolean | `false` |\nEnables production hardening such as stable session ID enforcement. |\n`AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD` |\nboolean | `true` |\nRequires a stable session/run/trace ID when production mode is enabled. |\n`AGENTICDOME_CLOUD_PROVIDER` |\nstring | empty | Optional cloud/provider label added to policy context. |\n`AGENTICDOME_CLOUD_PROJECT_ID` |\nstring | empty | Optional project/account label added to policy context. |\n`AGENTICDOME_IDENTITY_PROVIDER` |\nstring | empty | Optional identity-provider label added to policy context. |\n`AGENTICDOME_ENABLE_COPILOT_THREAT_API` |\nboolean | `false` |\nEnables optional Microsoft Copilot / AI Foundry threat helper calls where available. |\n`AGENTICDOME_ENFORCE_COPILOT_THREAT_API` |\nboolean | `false` |\nMakes optional Copilot / AI Foundry threat helper failures or blocks enforce locally. |\n`AGENTICDOME_COPILOT_API_VERSION` |\nstring | `2025-09-01` |\nAPI version used by optional Copilot / AI Foundry threat helper calls. |\n`AGENTICDOME_BEARER_TOKEN` |\nstring | optional | Bearer token used by Microsoft AI Foundry threat-contract endpoints. |\n`AGENTICDOME_REPORT_INCIDENTS` |\nboolean | `true` |\nReports blocked actions and middleware failures. |\n`AGENTICDOME_BLOCKED_INCIDENT_SEVERITY` |\nstring | `medium` |\nDefault severity for incident reports. |\n\n**Full configuration reference — per-adapter variables**\n\nEvery framework adapter exposes the same family of local hardening controls, prefixed per adapter: `AGENTICDOME_CREWAI_*`\n\n, `AGENTICDOME_PYDANTICAI_*`\n\n, `AGENTICDOME_LANGGRAPH_*`\n\n, `AGENTICDOME_MSAF_*`\n\n(Microsoft Agent Framework), `AGENTICDOME_AUTOGEN_*`\n\n, `AGENTICDOME_FOUNDRY_*`\n\n, `AGENTICDOME_OPENAI_AGENTS_*`\n\n, `AGENTICDOME_CLAUDE_*`\n\n, `AGENTICDOME_SMOLAGENTS_*`\n\n, `AGENTICDOME_AGNO_*`\n\n, `AGENTICDOME_BEDROCK_*`\n\n, `AGENTICDOME_GOOGLE_ADK_*`\n\n, and `AGENTICDOME_MCP_*`\n\n.\n\n**Common per-adapter pattern** (substitute the prefix for your adapter):\n\n| Variable suffix | Type | Default | Description |\n|---|---|---|---|\n`_MAX_INPUT_CHARS` |\ninteger | `50000` |\nMaximum prompt/input text reviewed before local truncation or blocking. |\n`_MAX_OUTPUT_CHARS` |\ninteger | `100000` |\nMaximum output text reviewed before local truncation or blocking. |\n`_MAX_TOOL_ARG_CHARS` |\ninteger | `20000` |\nMaximum serialized tool arguments before blocking. |\n`_STREAMING_BUFFER_CHARS` |\ninteger | `4000` |\nSliding buffer used by streaming sanitization helpers (CrewAI, Agno, OpenAI Agents, Bedrock, Google ADK, LangGraph). |\n`_RATE_LIMIT_PER_MINUTE` |\ninteger | `0` |\nPer-agent/session/purpose local rate limit; `0` disables it. |\n`_RETRY_ATTEMPTS` |\ninteger | `2` |\nRetry attempts for policy client calls. |\n`_RETRY_BACKOFF_S` |\nfloat | `0.25` |\nInitial exponential backoff delay for policy client retries. |\n`_CIRCUIT_BREAKER_FAILURES` |\ninteger | `5` |\nConsecutive policy call failures before opening the local circuit breaker. |\n`_CIRCUIT_BREAKER_RESET_S` |\ninteger | `60` |\nSeconds before retrying after the circuit breaker opens. |\n`_AUDIT_LOGGING` |\nboolean | `true` |\nEmits structured audit logs from the adapter. |\n`_OTEL_ENABLED` |\nboolean | `true` |\nEmits OpenTelemetry span events when OpenTelemetry is installed and a span is active. |\n`_EMERGENCY_BLOCK_TOOLS` |\nCSV string | empty | Local emergency deny list for tool names. |\n`_EMERGENCY_BLOCK_AGENTS` |\nCSV string | empty | Local emergency deny list for agent IDs. |\n\n**Adapter-specific additions:**\n\n| Environment Variable | Type | Default | Description |\n|---|---|---|---|\n`AGENTICDOME_LANGGRAPH_AGENT_ID` |\nstring | `langgraph_orchestrator` |\nDefault LangGraph orchestrator node identity. |\n`AGENTICDOME_LANGGRAPH_FINAL_ID` |\nstring | `langgraph_final_node` |\nDefault LangGraph final-output node identity. |\n`AGENTICDOME_LANGGRAPH_REQUIRE_SERVER_TOKENS` |\nboolean | `false` |\nRequires handoff authorization responses to include server-issued decision tokens. |\n`AGENTICDOME_CLAUDE_AGENT_ID` |\nstring | `claude_agent` |\nDefault Claude Agent SDK identity. |\n`AGENTICDOME_CLAUDE_STRICT_DELEGATED_EXECUTION` |\nboolean | `true` |\nRequires server-issued decision tokens for Claude multi-agent handoffs. |\n`AGENTICDOME_SMOLAGENTS_AGENT_ID` |\nstring | `smolagent` |\nDefault smolagents identity. |\n`AGENTICDOME_SMOLAGENTS_SCAN_CODE_EXPRESSIONS` |\nboolean | `true` |\nReviews CodeAgent-generated Python immediately before executor invocation. |\n`AGENTICDOME_SMOLAGENTS_STRICT_DELEGATED_EXECUTION` |\nboolean | `true` |\nAuthorizes and verifies managed-agent handoffs using bound decision tokens. |\n`AGENTICDOME_LANGGRAPH_STRICT_DELEGATED_EXECUTION` |\nboolean | `true` |\nBlocks delegated executions that carry delegation metadata without a valid token. |\n`AGENTICDOME_FOUNDRY_REQUIRE_OUTPUT_SANITIZATION_IN_PROD` |\nboolean | `true` |\nRequires API-key-backed Mesh output sanitization when Foundry production mode is enabled. |\n`AGENTICDOME_BEDROCK_AGENT_ID` |\nstring | `aws_bedrock_agent` |\nDefault agent identity for Bedrock runtime calls and local action handlers. |\n`AGENTICDOME_BEDROCK_MODEL_ID` |\nstring | empty | Optional default Bedrock model ID for policy context. |\n`AGENTICDOME_AWS_ACCOUNT_ID` |\nstring | empty | AWS account ID added to Bedrock policy context when available. |\n`AGENTICDOME_AWS_REGION` |\nstring | `AWS_REGION` / `AWS_DEFAULT_REGION` |\nAWS region added to Bedrock policy context when available. |\n`AGENTICDOME_AWS_ROLE_ARN` |\nstring | empty | AWS role ARN added to Bedrock policy context. |\n`AGENTICDOME_AWS_PRINCIPAL_ARN` |\nstring | empty | AWS principal/caller ARN added to Bedrock policy context. |\n`AGENTICDOME_SANITIZE_MODEL_OUTPUT` |\nboolean | `true` |\nEnables Mesh output review for model responses before returning to the application. |\n`AGENTICDOME_GOOGLE_ADK_AGENT_ID` |\nstring | `google_adk_agent` |\nDefault agent identity for Google ADK callback enforcement. |\n`AGENTICDOME_LLAMAINDEX_AGENT_ID` |\nstring | `llamaindex_agent` |\nDefault agent identity for LlamaIndex tools, query engines, and retrievers. |\n`AGENTICDOME_SANITIZE_QUERY_OUTPUT` |\nboolean | `true` |\nEnables Mesh output review for LlamaIndex query responses. |\n`AGENTICDOME_MCP_HOST_ID` |\nstring | `MCP_Enterprise_Host` |\nDefault agent identity for the MCP host or gateway process. |\n`AGENTICDOME_MCP_TOOL_PLATFORM` |\nstring | `mcp_third_party_server` |\nDefault downstream MCP server platform label for policy and billing context. |\n`AGENTICDOME_SANITIZE_TOOL_OUTPUT` |\nboolean | `true` |\nEnables Mesh output review for tool results before returning to the client. |\n`AGENTICDOME_SANITIZE_RESOURCE_OUTPUT` |\nboolean | `true` |\nEnables Mesh output review for MCP resource read results. |\n`AGENTICDOME_SANITIZE_PROMPT_OUTPUT` |\nboolean | `true` |\nEnables Mesh output review for MCP prompt results. |\n`AGENTICDOME_SANITIZE_STREAMING_OUTPUT` |\nboolean | `true` |\nEnables chunk-level sanitization helpers for streaming MCP responses. |\n`AGENTICDOME_VERIFY_DECISION_TOKENS` |\nboolean | `true` |\nVerifies delegated decision tokens when MCP tool calls carry handoff metadata. |\n`AGENTICDOME_SCREEN_UPSTREAM_PROMPT` |\nboolean | `true` |\nScreens upstream user prompt text in MCP host context before tool forwarding. |\n`AGENTICDOME_MCP_PROTECT_TOOLS_LIST` |\nboolean | `true` |\nAuthorizes and filters MCP `tools/list` discovery responses. |\n`AGENTICDOME_MCP_PROTECT_RESOURCES_LIST` |\nboolean | `true` |\nAuthorizes MCP `resources/list` discovery requests. |\n`AGENTICDOME_MCP_PROTECT_RESOURCES_READ` |\nboolean | `true` |\nAuthorizes MCP `resources/read` requests before forwarding. |\n`AGENTICDOME_MCP_PROTECT_PROMPTS_LIST` |\nboolean | `true` |\nAuthorizes MCP `prompts/list` discovery requests. |\n`AGENTICDOME_MCP_PROTECT_PROMPTS_GET` |\nboolean | `true` |\nAuthorizes MCP `prompts/get` requests before forwarding. |\n`AGENTICDOME_MCP_PROTECT_SAMPLING_CREATE_MESSAGE` |\nboolean | `true` |\nAuthorizes MCP `sampling/createMessage` requests. |\n`AGENTICDOME_MCP_SERVER_ID` |\nstring | empty | Default MCP server identity included in policy context. |\n`AGENTICDOME_MCP_SERVER_URL` |\nstring | empty | Default MCP server URL included in policy context. |\n`AGENTICDOME_MCP_SERVER_TRUST_LEVEL` |\nstring | empty | Default MCP server trust label included in policy context. |\n`AGENTICDOME_MCP_SERVER_VENDOR` |\nstring | empty | Default MCP server vendor included in policy context. |\n`AGENTICDOME_MCP_MAX_REQUEST_TEXT_CHARS` |\ninteger | `20000` |\nMaximum upstream request text sent for prompt/method authorization before local truncation. |\n\nOne table, one decision. Find your runtime, apply the required code action, and jump to its guide. In every case, environment config alone is **not** enough — hooks activate only after the code attachment shown here.\n\nFor a shorter operator/developer handoff, use the [production integration playbook](/agenticdome/agenticdome-python-sdk/blob/main/examples/PRODUCTION_INTEGRATION.md). It includes the attachment boundary, bypass warning and production proof checklist for every supported framework without exposing private policy or detection internals.\n\n| Runtime | SDK module | Global code location | Required code action | Tool-level enforcement |\n|---|---|---|---|---|\n|\n\n`agenticdome_sdk.crewai`\n\n`import agenticdome_sdk.crewai`\n\nonce for global hooks, or `AgenticDomeCrewAIFirewall().attach(...)`\n\nfor scoped hooks`secure_tool(...)`\n\nfor explicit local wrapper enforcement, schema validation, sanitized-argument execution[PydanticAI](#pydanticai)`agenticdome_sdk.pydantic`\n\n`Agent(...)`\n\nand tool is constructed`CyberSecFirewall(...)`\n\n+ `create_hooks()`\n\n, `install_native_hooks(agent)`\n\n, or `attach_to_agent(agent)`\n\n`@firewall.secure_tool(...)`\n\nwith optional `tool_schema`\n\nvalidation[LangGraph](#langgraph)`agenticdome_sdk.langgraph`\n\n`StateGraph`\n\nor LangChain `create_agent()`\n\nis assembled`input_node()`\n\n/ `transition_node()`\n\n/ `graph_transition_node()`\n\n/ `output_node()`\n\n, or `as_langchain_middleware()`\n\n`wrap_agent_node()`\n\n, `wrap_tool_node()`\n\n, `security_route()`\n\nfor blocked edges[Microsoft Agent Framework](#microsoft-agent-framework)`agenticdome_sdk.microsoft_agent_framework`\n\n`create_middleware()`\n\n, `install_on_agent()`\n\n, or `run_agent_securely()`\n\n`@firewall.secure_tool`\n\n, `wrap_tool_handler`\n\n, `secure_delegated_tool`\n\n, `wrap_delegated_tool_handler`\n\n[Microsoft AutoGen](#microsoft-autogen)`agenticdome_sdk.autogen`\n\n`wrap_team()`\n\n, `create_intervention_handler()`\n\n, `create_termination_condition()`\n\n, `attach_agentchat_agent()`\n\n, or `attach_conversable_agent()`\n\n`FunctionCall`\n\nauthorization plus inherited `wrap_tool_handler()`\n\n/ `secure_tool()`\n\n[Microsoft AI Foundry](#microsoft-ai-foundry)`agenticdome_sdk.microsoft_ai_foundry`\n\n`create_middleware()`\n\n, `install_on_client()`\n\n, or `run_secure()`\n\n`wrap_tool_executor()`\n\n, `@firewall.secure_tool(...)`\n\n, `before_tool_call()`\n\n, delegation verifiers[OpenAI Agents SDK](#openai-agents-sdk)`agenticdome_sdk.openai_agents`\n\n`Agent`\n\n, `Runner.run(...)`\n\n, `@function_tool`\n\n, guardrails, or handoffs are declared`run_agent_securely()`\n\n, `run_agent_stream_securely()`\n\n, `create_input_guardrail()`\n\n, `create_output_guardrail()`\n\n`wrap_tool_handler()`\n\n, `wrap_delegated_tool_handler()`\n\n, `@firewall.secure_tool(...)`\n\n, handoff verifiers[Claude Agent SDK](#claude-agent-sdk)`agenticdome_sdk.claude`\n\n`ClaudeAgentOptions`\n\n, `ClaudeSDKClient`\n\n, SDK MCP tools, or `query()`\n\n`install_on_options()`\n\nplus `run_client_securely()`\n\n, or `secure_query()`\n\n`PreToolUse`\n\n/`PostToolUse`\n\nhooks, `wrap_tool_handler()`\n\n, and `secure_sdk_tool()`\n\n[smolagents](#hugging-face-smolagents)`agenticdome_sdk.smolagents`\n\n`CodeAgent`\n\n, `ToolCallingAgent`\n\n, tools, or managed agents`run_agent_securely()`\n\nor `attach_firewall()`\n\n`Tool`\n\nwrappers, CodeAgent executor proxy, observation callback, and managed-agent token verification[Agno](#agno)`agenticdome_sdk.agno`\n\n`Agent`\n\n, Team, Workflow, or AgentOS components are declared`attach_firewall(agent_or_team)`\n\n, `create_hook_bundle()`\n\n, `create_middleware()`\n\n, or `create_plugin()`\n\n`@firewall.secure_tool(...)`\n\nfor high-risk local tools[Google ADK](#google-adk)`agenticdome_sdk.google_adk`\n\n`LlmAgent(...)`\n\nor ADK plugins are declared`build_callback_kwargs()`\n\n, `create_plugin()`\n\n, or `install_on_agent(...)`\n\n`wrap_tool_handler()`\n\nor `@firewall.secure_tool(...)`\n\n[LlamaIndex](#llamaindex)`agenticdome_sdk.llamaindex`\n\n`run_query_securely()`\n\n, `wrap_query_engine()`\n\n, `wrap_retriever()`\n\n, `create_node_postprocessor()`\n\n, `create_callback_handler()`\n\n`wrap_tool_function()`\n\n, `to_function_tool()`\n\n, `@firewall.secure_tool(...)`\n\n, handoff verifiers[AWS Bedrock](#aws-bedrock)`agenticdome_sdk.aws_bedrock`\n\n`converse(...)`\n\n, `invoke_model(...)`\n\n, `invoke_agent(...)`\n\n, action-group Lambdas, or retrieval`converse_securely()`\n\n, `converse_stream_securely()`\n\n, `invoke_model_securely()`\n\n, `invoke_model_with_response_stream_securely()`\n\n, `invoke_agent_securely()`\n\n`wrap_tool_handler()`\n\n, `@firewall.secure_tool(...)`\n\n, `wrap_action_group_lambda()`\n\n, delegation verifiers[MCP host / gateway](#mcp-host--gateway)`agenticdome_sdk.mcp_host`\n\n`preflight_request()`\n\nor `forward_with_firewall()`\n\naround the forwarder`authorize_manager_handoff()`\n\nand `verify_decision_token_if_present()`\n\n; SDK-managed security metadata never reaches the upstream server[Custom Python](#core-sdk-client-custom-runtimes)`agenticdome_sdk.client`\n\n`guardrail_validate()`\n\nbefore prompts/tools; `mesh_validate()`\n\nbefore returning output`a2a_authorize_tool()`\n\nand `a2a_verify_decision_token_rpc()`\n\nfor delegationIn production, wire AgenticDome at **every** local boundary your process controls: prompt ingress, tool execution, delegation handoff, specialist execution, and output egress.\n\nEvery integration follows the same template: **install → attach → secure tools → delegate safely → notes**. Capability details and configuration snippets are collapsible so you can scan the happy path first.\n\nOne import in your bootstrap registers global hooks for `before_llm_call`\n\n, `before_tool_call`\n\n, and `after_tool_call`\n\n— prompt screening, tool authorization, and output DLP across every crew in the process.\n\n```\npip install \"agenticdome-python-sdk[crewai]\"\npython\nfrom crewai import Agent, Crew, Task\n\n# Importing this module registers AgenticDome global before/after hooks.\nimport agenticdome_sdk.crewai  # noqa: F401\n\nmanager = Agent(\n    role=\"Operations Manager\",\n    goal=\"Coordinate cross-functional tasks and delegate to specialist units\",\n    backstory=\"Corporate coordinator responsible for resource routing.\",\n    allow_delegation=True,\n)\n\nresearcher = Agent(\n    role=\"Research Specialist\",\n    goal=\"Extract analytical records from approved secure repositories\",\n    backstory=\"Analytical expert executing restricted tasks under policy control.\",\n)\n\ntask = Task(\n    description=\"Analyze database outputs and pass a summary report to the operations manager.\",\n    expected_output=\"A structured analytical report.\",\n    agent=manager,\n)\n\ncrew = Crew(agents=[manager, researcher], tasks=[task])\nresult = crew.kickoff()\n```\n\n**Scoped attachment** — use the class facade for explicit hook functions, scoped attach/unregister, or a test-local client and token store (additive to the global import):\n\n``` python\nfrom agenticdome_sdk.crewai import AgenticDomeCrewAIFirewall\n\nfirewall = AgenticDomeCrewAIFirewall()\nfirewall.attach(crew)\n# ... run a scoped test or runtime ...\nfirewall.unregister(crew)\n```\n\n**Secure high-risk tools explicitly** — if AgenticDome returns sanitized arguments, the wrapper executes the tool with those sanitized values:\n\n```\n@firewall.secure_tool(\n    tool_name=\"crm.customer.read\",\n    tool_platform=\"crm\",\n    tool_schema={\"required\": [\"customer_id\"], \"properties\": {\"customer_id\": {\"type\": \"string\"}}},\n)\ndef lookup_customer(agent, customer_id: str):\n    return crm.get_customer(customer_id)\n```\n\n**Security flow:** (1) prompts are screened before the LLM is called; (2) tool name, clean arguments, session context, agent identity, and policy metadata are validated before execution; (3) manager→specialist delegation is authorized and can return a decision token; (4) the specialist's token is verified through the assigned runtime and consumed once using the configured SDK state store; (5) output is reviewed and can be redacted, blocked, or preserved as structured output before leaving the runtime.\n\n## CrewAI capabilities, configuration, and imports\n\nSupports: prompt screening before LLM calls · direct tool authorization · manager-to-specialist handoff authorization with explicit target metadata · specialist-side delegated execution verification using SDK-managed, one-time shared state · sanitized tool arguments and optional schema validation · output DLP with structured-output preservation and sanitized JSON parsing · streaming sanitization via `sanitize_streaming_response()`\n\n· production mode with stable session ID requirements · local size limits, rate limits, retries/backoff, circuit breaker, audit logs, OpenTelemetry events, and emergency deny lists.\n\n```\nexport AGENTICDOME_PLATFORM=\"crewai\"\nexport AGENTICDOME_PRODUCTION_MODE=\"true\"\nexport AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=\"true\"\nexport AGENTICDOME_CREWAI_MAX_INPUT_CHARS=\"50000\"\nexport AGENTICDOME_CREWAI_MAX_OUTPUT_CHARS=\"100000\"\nexport AGENTICDOME_CREWAI_MAX_TOOL_ARG_CHARS=\"20000\"\nexport AGENTICDOME_CREWAI_RATE_LIMIT_PER_MINUTE=\"120\"\nexport AGENTICDOME_CREWAI_RETRY_ATTEMPTS=\"2\"\nexport AGENTICDOME_CREWAI_CIRCUIT_BREAKER_FAILURES=\"5\"\nexport AGENTICDOME_CREWAI_AUDIT_LOGGING=\"true\"\nexport AGENTICDOME_CREWAI_OTEL_ENABLED=\"true\"\n# Optional for distributed multi-worker delegation:\n# export AGENTICDOME_REDIS_URL=\"redis://localhost:6379/0\"\n# export AGENTICDOME_REDIS_KEY_PREFIX=\"AgenticDome:crewai:handoff\"\n# export AGENTICDOME_TOKEN_HMAC_SECRET=\"replace-with-secret-from-your-secret-manager\"\npython\nimport agenticdome_sdk.crewai\n\nfrom agenticdome_sdk.crewai import (\n    CONFIG,\n    CLIENT,\n    AgenticDomeCrewAIFirewall,\n    DecisionTokenRecord,\n    DecisionTokenStore,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n    AgenticDome_before_tool_call,\n    AgenticDome_after_tool_call,\n    AgenticDome_before_llm_call,\n    sanitize_streaming_response,\n    attach_firewall,\n    unregister_firewall,\n)\n```\n\nAttach lifecycle hooks where each `Agent(...)`\n\nis constructed, and always decorate tools that access data, systems, or external actions.\n\n```\npip install \"agenticdome-python-sdk[pydanticai]\"\npython\nimport os\nfrom typing import Any\n\nfrom pydantic_ai import Agent, RunContext\nfrom agenticdome_sdk.pydantic import CyberSecFirewall, FirewallConfig\n\n# 1. Instantiate the enterprise firewall capability.\nfirewall = CyberSecFirewall(\n    config=FirewallConfig(\n        api_base=os.environ[\"AGENTICDOME_API_BASE\"],\n        api_key=os.environ[\"AGENTICDOME_API_KEY\"],\n        tenant_id=os.environ[\"AGENTICDOME_TENANT_ID\"],\n        fail_closed=True,\n        block_on_sensitive_output=True,\n    )\n)\n\n# 2. Define your PydanticAI Agent.\ncustomer_support_agent = Agent(\n    \"gemini-2.5-flash\",\n    name=\"customer_support_agent\",\n    result_type=str,\n    system_prompt=\"You are a helpful customer platform support assistant.\",\n)\n\n# 3. Prefer native PydanticAI Hooks where your version supports capabilities.\n#    You can also pass firewall.create_hooks() at Agent construction via capabilities=[...].\nfirewall.install_native_hooks(customer_support_agent)\n\n# Legacy PydanticAI versions can still use compatibility lifecycle hooks.\nfirewall.attach_to_agent(customer_support_agent)\n\n# 4. Protect capability tools using the perimeter decorator.\n@customer_support_agent.tool\n@firewall.secure_tool(\n    tool_name=\"customer.profile.read\",\n    tool_platform=\"crm\",\n    tool_schema={\n        \"required\": [\"user_id\"],\n        \"properties\": {\"user_id\": {\"type\": \"string\"}},\n    },\n)\nasync def fetch_user_profile(ctx: RunContext[Any], user_id: str) -> dict:\n    \"\"\"Retrieves account management metadata profiles for a corporate ID.\"\"\"\n    return {\n        \"user_id\": user_id,\n        \"status\": \"active\",\n        \"passport_number\": \"A-1234567\",\n    }\n```\n\n**Manual firewall usage** — in custom routers, test harnesses, or execution gateways:\n\n```\nasync for safe_chunk in firewall.sanitize_streaming_response(\n    chunks=agent_stream,\n    agent_id=\"customer_support_agent\",\n    session_id=\"sess_prod_01J4X\",\n):\n    yield safe_chunk\n```\n\n## PydanticAI capabilities, version notes, and imports\n\nSupports: prompt ingress checks via legacy lifecycle hooks where available · native `Hooks`\n\ncapability creation through `create_hooks()`\n\nfor current PydanticAI versions · tool perimeter authorization via `@firewall.secure_tool(...)`\n\n· Pydantic/JSON-schema argument validation and sanitized-argument execution · manager/specialist delegation with SDK-managed, integrity-protected shared state · egress output DLP with correct `block_on_sensitive_output`\n\nsemantics · structured-output preservation (sanitized JSON parsed back to dicts/lists) · stable session ID enforcement in production mode · local rate limits, size limits, retries, circuit breaker, audit logging, OpenTelemetry span events · streaming sanitization · identity-rich policy context from `ctx`\n\n, `deps`\n\n, or nested identity/principal objects · emergency deny lists.\n\nVersion notes: PydanticAI lifecycle hook APIs have evolved. Current PydanticAI documents `pydantic_ai.capabilities.Hooks`\n\nfor lifecycle interception across runs, model requests, tool validation/execution, output processing, and event streams. Prefer `create_hooks()`\n\n/ `install_native_hooks()`\n\non current runtimes; keep `@firewall.secure_tool(...)`\n\non sensitive tools as a hard enforcement boundary. If legacy lifecycle decorators are available, `attach_to_agent()`\n\nattaches prompt ingress and egress DLP hooks; if not, `@firewall.secure_tool(...)`\n\nstill protects tool execution. `AGENTICDOME_BLOCK_ON_SENSITIVE_OUTPUT=true`\n\nmeans AgenticDome may ask Mesh to block sensitive output; the SDK only blocks when the policy response verdict is `BLOCKED`\n\n.\n\n```\nfrom agenticdome_sdk.pydantic import (\n    CyberSecFirewall,\n    FirewallConfig,\n    PydanticAIFirewallError,\n    PydanticAIFirewallDenied,\n    PydanticAIFirewallConfigurationError,\n    DecisionTokenRecord,\n    DecisionTokenStore,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n)\n```\n\nThree production patterns: **explicit firewall nodes** when you own the graph topology, **wrappers** when you already have nodes, and **LangChain middleware** when you use `create_agent(..., middleware=[...])`\n\n.\n\n```\npip install \"agenticdome-python-sdk[langgraph]\"\n```\n\n**Pattern 1 — explicit firewall nodes** (clear security boundaries before input, before tools, before final output):\n\n``` python\nimport os\nfrom langgraph.graph import END, START, StateGraph\nfrom agenticdome_sdk.langgraph import AgentState, AgenticDomeLangGraphFirewall, FirewallConfig\n\nfirewall = AgenticDomeLangGraphFirewall(\n    config=FirewallConfig(\n        api_base=os.environ[\"AGENTICDOME_API_BASE\"],\n        api_key=os.environ[\"AGENTICDOME_API_KEY\"],\n        tenant_id=os.environ[\"AGENTICDOME_TENANT_ID\"],\n        fail_closed=True,\n        production_mode=True,\n        require_explicit_session_id=True,\n        rate_limit_per_minute=120,\n        max_tool_arg_chars=20_000,\n    )\n)\n\nasync def agent_node(state: AgentState) -> AgentState:\n    # Your normal LangGraph agent/model node. It may append AIMessage objects\n    # with tool_calls; transition_node() authorizes those calls before a tool node.\n    return state\n\nasync def tool_node(state: AgentState) -> AgentState:\n    # Your normal LangGraph tool execution node.\n    return state\n\ngraph = StateGraph(AgentState)\ngraph.add_node(\"input_firewall\", firewall.input_node(agent_id=\"support_orchestrator\"))\ngraph.add_node(\"agent\", agent_node)\ngraph.add_node(\"transition_firewall\", firewall.transition_node(agent_id=\"support_orchestrator\"))\ngraph.add_node(\"tools\", tool_node)\ngraph.add_node(\"output_firewall\", firewall.output_node(agent_id=\"support_orchestrator\"))\n\ngraph.add_edge(START, \"input_firewall\")\ngraph.add_edge(\"input_firewall\", \"agent\")\ngraph.add_edge(\"agent\", \"transition_firewall\")\ngraph.add_edge(\"transition_firewall\", \"tools\")\ngraph.add_edge(\"tools\", \"output_firewall\")\ngraph.add_edge(\"output_firewall\", END)\n\ncompiled = graph.compile()\n\nresult = await compiled.ainvoke({\n    \"session_id\": \"sess_prod_01J4X\",\n    \"messages\": [{\"role\": \"user\", \"content\": \"Check the customer refund status.\"}],\n    \"agent_id\": \"support_orchestrator\",\n})\n```\n\n**Pattern 2 — wrap existing nodes** without changing their internals:\n\n```\nsecure_agent_node = firewall.wrap_agent_node(\n    existing_agent_node,\n    agent_id=\"claims_agent\",\n    screen_input=True,\n    sanitize_output=True,\n)\n\nsecure_tool_node = firewall.wrap_tool_node(\n    existing_tool_node,\n    agent_id=\"claims_agent\",\n    sanitize_tool_output=True,\n)\n```\n\n**Pattern 3 — LangChain agent middleware:**\n\n``` python\nfrom langchain.agents import create_agent\n\nagent = create_agent(\n    model=\"openai:gpt-4.1-mini\",\n    tools=[lookup_customer, create_refund],\n    middleware=[firewall.as_langchain_middleware(agent_id=\"support_agent\")],\n)\n```\n\n**Delegation** — a manager node requests a specialist handoff by adding an `AgenticDome.handoff`\n\n(or top-level `handoff`\n\n) payload to graph state; the firewall authorizes it and stores the decision token in state plus the token store:\n\n```\nstate[\"AgenticDome\"] = {\n    \"handoff\": {\n        \"target_agent_id\": \"refund_specialist\",\n        \"delegated_tool_name\": \"payments.refund.create\",\n        \"delegated_tool_args\": {\n            \"customer_id\": \"cust_123\",\n            \"amount\": 250,\n            \"currency\": \"AUD\",\n        },\n        \"tool_platform\": \"payments\",\n        \"text\": \"Manager delegates refund execution to a specialist agent.\",\n    }\n}\n```\n\nSpecialist execution is verified when the delegated tool call reaches `authorize_transition()`\n\nor a wrapped tool node. The SDK carries or recovers integrity-protected delegation state and consumes it once. Applications should use the public handoff/wrapper APIs rather than create, inspect or forward the SDK's internal metadata.\n\n**Hardening helpers** — policy-control sensitive graph edges; blocked states set `AgenticDome.route`\n\nand `next_agent_id`\n\nto `security_block`\n\n:\n\n```\ngraph.add_node(\n    \"authorize_escalation\",\n    firewall.graph_transition_node(\n        from_node=\"triage\",\n        to_node=\"refund_specialist\",\n        agent_id=\"support_orchestrator\",\n    ),\n)\n\ngraph.add_conditional_edges(\n    \"authorize_escalation\",\n    firewall.security_route,\n    {\"continue\": \"refund_specialist\", \"security_block\": \"security_block\"},\n)\n```\n\nUse `sanitize_retrieval_documents()`\n\nbefore adding retrieved chunks to model context, and `sanitize_streaming_events()`\n\nfor async event streams — both use the same Mesh output policy path as final-output sanitization.\n\n## LangGraph capabilities, interception notes, and imports\n\nSupports: prompt ingress via `screen_input()`\n\n/ `input_node()`\n\n· tool-call authorization via `authorize_transition()`\n\n/ `transition_node()`\n\n· delegation authorization from documented handoff fields · specialist-side verification using SDK-managed one-time state · sanitized tool-argument mutation before local execution · final message and tool-output DLP via `sanitize_output()`\n\n/ `output_node()`\n\n· retrieval and streaming sanitization · graph transition authorization · `security_block`\n\nrouting · wrappers for existing agent and tool nodes.\n\nInterception notes: LangGraph is graph-native — reliable interception means inserting security nodes or wrapping nodes/tool nodes. LangChain's modern `create_agent()`\n\nsupports a `middleware`\n\nparameter documented as the way to intercept model, tool, and agent-loop behavior; `as_langchain_middleware()`\n\ntargets that style, and the adapter mutates sanitized tool arguments back into the tool request for local execution. For custom `StateGraph`\n\nworkflows, implement middleware as graph nodes or wrappers at the boundaries you must enforce: before model input, before tool execution, before handoff execution, and before final output. Remote or provider-hosted tools that execute outside your Python process can only be guarded at the local request/response boundary.\n\nOfficial references: [LangChain middleware overview](https://docs.langchain.com/oss/python/langchain/middleware/overview) · [LangChain create_agent reference](https://reference.langchain.com/python/langchain/agents/#langchain.agents.create_agent)\n\n```\nfrom agenticdome_sdk.langgraph import (\n    AgentState,\n    AgenticDomeLangGraphFirewall,\n    AgenticDomeLangChainMiddleware,\n    FirewallConfig,\n    AgenticDomeDenied,\n    AgenticDomeConfigurationError,\n    DecisionTokenRecord,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n)\n```\n\nBoundary-oriented async firewall: protect the run boundary, local function-tool handlers, delegated specialist tools, and final output. It does not monkey-patch every Microsoft provider or hosted tool surface.\n\n```\npip install \"agenticdome-python-sdk[microsoft]\"\n```\n\nInstall the Microsoft Agent Framework packages used by your application separately — the AgenticDome helper is dependency-light because deployments vary across local function tools, hosted tools, Foundry agents, Copilot Studio, A2A agents, workflow executors, and custom clients.\n\n**Secure a local function tool** — wrap the callable that actually executes, so arguments are authorized before execution and results sanitized after. If AgenticDome returns `sanitized_tool_args`\n\n, the wrapped handler receives those safe arguments instead of the model-provided originals:\n\n``` python\nimport os\nfrom typing import Annotated\n\nfrom pydantic import Field\nfrom agent_framework import tool\nfrom agenticdome_sdk.microsoft_agent_framework import AgenticDomeMicrosoftAgentFirewall\n\nfirewall = AgenticDomeMicrosoftAgentFirewall()\n\nasync def raw_get_customer_profile(ctx, args):\n    customer_id = args[\"customer_id\"]\n    return {\n        \"customer_id\": customer_id,\n        \"email\": \"alice@example.com\",\n        \"risk\": \"medium\",\n    }\n\nsecure_get_customer_profile = firewall.wrap_tool_handler(\n    tool_name=\"crm.customer_profile.read\",\n    handler=raw_get_customer_profile,\n    tool_platform=\"crm\",\n)\n\n@tool(approval_mode=\"never_require\")\nasync def get_customer_profile(\n    customer_id: Annotated[str, Field(description=\"Customer identifier\")],\n) -> str:\n    # Adapt this context object to your runtime. It should expose session_id/run_id\n    # and agent identity if available.\n    ctx = {\n        \"session_id\": \"sess_prod_01J4X\",\n        \"agent_name\": \"customer_support_agent\",\n    }\n    return await secure_get_customer_profile(ctx, {\"customer_id\": customer_id})\n```\n\n**Native-style middleware hooks** — a harder-to-bypass assembly-level integration where the runtime exposes middleware or callbacks; `before_tool_call()`\n\nreturns the sanitized tool arguments to forward to the local executor:\n\n```\nfirewall = AgenticDomeMicrosoftAgentFirewall()\n\nagent = firewall.install_on_agent(agent)\nmiddleware = firewall.create_middleware()\n\n# The returned middleware exposes async hook methods:\n# before_agent_run(ctx, input_text)\n# after_agent_run(ctx, output)\n# before_tool_call(ctx, tool_name, tool_args)\n# after_tool_call(ctx, tool_name, result)\n```\n\n**Secure the whole agent run boundary** — prompt ingress plus final-output DLP:\n\n```\nresult = await firewall.run_agent_securely(\n    run_callable=agent.run,\n    input_text=\"Find the customer's refund status.\",\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"refund_agent\",\n    policy_context={\"request_purpose\": \"customer_support\"},\n    output_extractor=lambda value: getattr(value, \"text\", str(value)),\n)\n```\n\n**Delegated specialist pattern** — authorize at the manager, verify at the specialist:\n\n```\nauthorization = await firewall.authorize_manager_handoff(\n    text=\"Manager delegates refund execution to a payment specialist.\",\n    manager_agent_id=\"support_manager\",\n    specialist_agent_id=\"payments_specialist\",\n    tool_name=\"payments.refund.create\",\n    tool_args={\"customer_id\": \"cust_123\", \"amount\": 250, \"currency\": \"AUD\"},\n    session_id=\"sess_prod_01J4X\",\n    tool_platform=\"payments\",\n)\n\nsecure_refund_handler = firewall.wrap_delegated_tool_handler(\n    tool_name=\"payments.refund.create\",\n    handler=raw_refund_handler,\n)\n```\n\n## Microsoft Agent Framework capabilities, configuration, notes, and imports\n\nSupports: prompt ingress via `screen_input()`\n\n, middleware hooks, or `run_agent_securely()`\n\n· function-tool authorization with sanitized-argument enforcement · manager-to-specialist delegation and specialist verification through public wrapper APIs · stable session ID enforcement for production · Entra/principal identity context propagation · output DLP with structured JSON preservation and optional response-object mutation · streaming sanitization · OpenTelemetry events and structured audit logging · local rate limits, size limits, retries, circuit breaker · optional Copilot / AI Foundry threat helper enforcement · shared multi-worker delegation state · emergency deny lists.\n\n```\nexport AGENTICDOME_PLATFORM=\"microsoft_agent_framework_v1\"\nexport AGENTICDOME_PRODUCTION_MODE=\"true\"\nexport AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=\"true\"\nexport AGENTICDOME_MSAF_MAX_INPUT_CHARS=\"50000\"\nexport AGENTICDOME_MSAF_MAX_OUTPUT_CHARS=\"100000\"\nexport AGENTICDOME_MSAF_MAX_TOOL_ARG_CHARS=\"20000\"\nexport AGENTICDOME_MSAF_RATE_LIMIT_PER_MINUTE=\"0\"\nexport AGENTICDOME_MSAF_RETRY_ATTEMPTS=\"2\"\nexport AGENTICDOME_MSAF_CIRCUIT_BREAKER_FAILURES=\"5\"\nexport AGENTICDOME_MSAF_CIRCUIT_BREAKER_RESET_S=\"60\"\nexport AGENTICDOME_MSAF_AUDIT_LOGGING=\"true\"\nexport AGENTICDOME_MSAF_OTEL_ENABLED=\"true\"\n# Optional local emergency controls:\n# export AGENTICDOME_MSAF_EMERGENCY_BLOCK_TOOLS=\"payments.refund.create\"\n# export AGENTICDOME_MSAF_EMERGENCY_BLOCK_AGENTS=\"legacy_agent\"\n# Optional integrity secret for SDK-managed shared delegation state:\n# export AGENTICDOME_TOKEN_HMAC_SECRET=\"change-me\"\n# Optional Copilot / AI Foundry helper enforcement:\n# export AGENTICDOME_ENABLE_COPILOT_THREAT_API=\"true\"\n# export AGENTICDOME_ENFORCE_COPILOT_THREAT_API=\"true\"\n```\n\nNotes: the framework's tool-approval feature is human-in-the-loop gating, not policy enforcement, DLP, or tenant-aware A2A token verification — AgenticDome should sit at the local tool handler or workflow executor boundary for deterministic enforcement. Wrap the executor/run boundary or the tool/executor functions that process sensitive actions. Tools that execute remotely (hosted providers, Foundry agents, Copilot Studio, hosted MCP servers, remote A2A agents) can only be protected at the local request/response boundary. In production, pass stable `session_id`\n\n/`run_id`\n\n/`trace_id`\n\nvalues and Entra/principal identity fields in context.\n\nOfficial references: [Agent Framework docs](https://learn.microsoft.com/en-us/agent-framework/) · [Tools overview](https://learn.microsoft.com/en-us/agent-framework/agents/tools/) · [Workflow execution](https://learn.microsoft.com/en-us/agent-framework/workflows/workflows)\n\n```\nfrom agenticdome_sdk.microsoft_agent_framework import (\n    AgenticDomeMicrosoftAgentFirewall,\n    FirewallConfig,\n    load_config,\n    MicrosoftAgentFirewallDenied,\n    MicrosoftAgentFirewallError,\n    DecisionTokenRecord,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n)\n```\n\nAutoGen is Microsoft's open-source conversational multi-agent framework and is now community-managed in maintenance mode; Microsoft Agent Framework is the recommended successor for new systems. AgenticDome supports current AutoGen AgentChat/Core applications and existing legacy `ConversableAgent`\n\ndeployments so teams can migrate without losing runtime enforcement.\n\n```\n# AutoGen AgentChat requires Python 3.10+.\npip install \"agenticdome-python-sdk[autogen]\"\n```\n\n**Protect a current AgentChat team** — the wrapper screens the initial task, every streamed team event, and final messages while retaining the underlying Team API:\n\n``` python\nfrom autogen_agentchat.teams import RoundRobinGroupChat\nfrom agenticdome_sdk.autogen import AgenticDomeAutoGenFirewall\n\nfirewall = AgenticDomeAutoGenFirewall()\nteam = RoundRobinGroupChat([planner, researcher, payments_specialist], max_turns=12)\nsecure_team = firewall.wrap_team(\n    team,\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"customer_operations_team\",\n    policy_context={\"request_purpose\": \"customer_support\"},\n)\n\nresult = await secure_team.run(task=user_prompt)\n```\n\n**Authorize AutoGen Core tool traffic at the runtime boundary** — current AutoGen sends `FunctionCall`\n\nmessages to tool agents, so the intervention handler is a stronger boundary than patching an individual assistant:\n\n``` python\nfrom autogen_core import SingleThreadedAgentRuntime\n\nhandler = firewall.create_intervention_handler(\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"autogen_planner\",\n)\nruntime = SingleThreadedAgentRuntime(intervention_handlers=[handler])\n```\n\n**Freeze a group chat on behavioral drift** — compose the AgenticDome condition with AutoGen's normal termination conditions:\n\n```\nagenticdome_stop = firewall.create_termination_condition(\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"customer_operations_team\",\n)\nteam = RoundRobinGroupChat(\n    [planner, researcher, payments_specialist],\n    termination_condition=agenticdome_stop | normal_stop,\n    max_turns=12,\n)\n```\n\nFamily 2 policy receives a bounded rolling conversation window digest, participant lineage, semantic-deviation evaluation request, and tool-call frequency. A blocked cross-agent message or excessive tool rate freezes the local session, reports a trust incident, and advances revocation state for the emitting agent before an external action can run.\n\n**Existing AutoGen 0.2 deployments** — attach to the legacy `ConversableAgent.send()`\n\n, `receive()`\n\n, `a_send()`\n\n, and `a_receive()`\n\nlifecycles:\n\nKeep the customer's already-certified legacy AutoGen dependency in place and install the dependency-light base SDK (do not use the `[autogen]`\n\nextra, because that extra deliberately installs the current AgentChat release):\n\n```\npip install agenticdome-python-sdk\nassistant = firewall.attach_conversable_agent(\n    assistant,\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"legacy_autogen_assistant\",\n)\nuser_proxy = firewall.attach_conversable_agent(\n    user_proxy,\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"legacy_autogen_user_proxy\",\n)\n```\n\nWrap side-effecting local tools as well; conversation screening does not replace authorization at the execution boundary:\n\n```\nsecure_refund = firewall.wrap_tool_handler(\n    tool_name=\"payments.refund.create\",\n    tool_platform=\"payments\",\n    handler=raw_refund,\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"payments_specialist\",\n)\nexport AGENTICDOME_PLATFORM=\"autogen\"\nexport AGENTICDOME_PRODUCTION_MODE=\"true\"\nexport AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=\"true\"\nexport AGENTICDOME_AUTOGEN_CONVERSATION_WINDOW=\"12\"\nexport AGENTICDOME_AUTOGEN_MAX_TOOL_CALLS_PER_WINDOW=\"8\"\nexport AGENTICDOME_AUTOGEN_FREEZE_ON_BLOCK=\"true\"\nexport AGENTICDOME_AUTOGEN_REVOKE_ON_FREEZE=\"true\"\n```\n\nOfficial references: [AutoGen project status and migration guidance](https://github.com/microsoft/autogen) · [AgentChat teams](https://microsoft.github.io/autogen/stable/reference/python/autogen_agentchat.teams.html) · [Core intervention handlers](https://microsoft.github.io/autogen/dev/user-guide/core-user-guide/cookbook/tool-use-with-intervention.html) · [Legacy 0.2 conversational agents](https://microsoft.github.io/autogen/0.2/docs/Use-Cases/agent_chat/)\n\nFor services that call Foundry agents, handle function-call requests from Foundry, execute local function tools, or use `FoundryChatClient`\n\nwith local tools.\n\n```\npip install \"agenticdome-python-sdk[foundry]\"\n```\n\nThe adapter itself is dependency-light; the `[foundry]`\n\nextra installs common Azure SDK packages (`azure-ai-projects`\n\n, `azure-identity`\n\n) for applications using Foundry directly.\n\n**Authentication model** — Foundry threat-contract calls use bearer auth; Mesh output DLP and incident reporting use API-key auth. In production mode, output sanitization is required by default:\n\n```\nexport AGENTICDOME_API_BASE=\"https://demo-sidecar.agenticdome.io\"\nexport AGENTICDOME_BEARER_TOKEN=\"your_foundry_threat_contract_bearer_token\"\n\nexport AGENTICDOME_API_KEY=\"your_api_key\"\nexport AGENTICDOME_TENANT_ID=\"your_tenant_id\"\nexport AGENTICDOME_PRODUCTION_MODE=\"true\"\nexport AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=\"true\"\nexport AGENTICDOME_FOUNDRY_REQUIRE_OUTPUT_SANITIZATION_IN_PROD=\"true\"\n\n# Optional only when delegated execution crosses processes/workers/pods:\n# export AGENTICDOME_REDIS_URL=\"redis://redis.internal:6379/0\"\n# export AGENTICDOME_REDIS_KEY_PREFIX=\"AgenticDome:foundry:handoff\"\n# export AGENTICDOME_TOKEN_HMAC_SECRET=\"replace-with-secret-from-kms\"\n```\n\n**Attach middleware, then secure the run boundary:**\n\n``` python\nfrom agenticdome_sdk.microsoft_ai_foundry import AgenticDomeMicrosoftAIFoundryFirewall\n\nfirewall = AgenticDomeMicrosoftAIFoundryFirewall()\nfoundry_client = firewall.install_on_client(foundry_client)\n\n# For custom runtimes, register the middleware object explicitly.\nmiddleware = firewall.create_middleware()\nawait middleware.before_run(ctx, input_text)\nresult = await foundry_agent.run(input_text)\nresult = await middleware.after_run(ctx, result)\nresult = await firewall.run_secure(\n    run_callable=foundry_agent.run,\n    input_text=\"Find the customer's refund status.\",\n    ctx={\n        \"agent_id\": \"foundry_refund_agent\",\n        \"session_id\": \"sess_prod_01J4X\",\n        \"user_id\": \"user_123\",\n    },\n    output_extractor=lambda value: getattr(value, \"text\", str(value)),\n)\n```\n\n**Secure local function-tool execution** — at the exact boundary before your app submits function output back to Foundry:\n\n``` python\nasync def raw_lookup_customer(ctx, args):\n    return {\"customer_id\": args[\"customer_id\"], \"email\": \"alice@example.com\"}\n\nsecure_lookup_customer = firewall.wrap_tool_executor(\n    tool_name=\"crm.customer.read\",\n    tool_platform=\"crm\",\n    handler=raw_lookup_customer,\n    tool_schema={\n        \"required\": [\"customer_id\"],\n        \"properties\": {\"customer_id\": {\"type\": \"string\"}},\n    },\n)\n\nresult = await secure_lookup_customer(\n    {\"agent_id\": \"foundry_support_agent\", \"session_id\": \"sess_prod_01J4X\"},\n    {\"customer_id\": \"cust_123\"},\n)\n```\n\nDecorator form:\n\n```\n@firewall.secure_tool(\n    tool_name=\"payments.refund.create\",\n    tool_platform=\"payments\",\n    tool_schema={\n        \"required\": [\"customer_id\", \"amount_cents\"],\n        \"properties\": {\n            \"customer_id\": {\"type\": \"string\"},\n            \"amount_cents\": {\"type\": \"integer\"},\n        },\n    },\n)\ndef create_refund(ctx, args):\n    return {\"refund_id\": \"rfnd_123\", \"status\": \"created\"}\n```\n\n**Delegated Foundry tool execution** — authorization stores decision state in\nmemory by default. Configure the optional Redis store only when the specialist\nexecutes in another process, worker, or pod; the specialist consumes and\nverifies the decision before executing:\n\n```\nawait firewall.authorize_manager_handoff(\n    text=\"Ask the billing specialist to create a refund.\",\n    manager_agent_id=\"foundry_manager\",\n    specialist_agent_id=\"billing_specialist\",\n    tool_name=\"payments.refund.create\",\n    tool_args={\"customer_id\": \"cust_123\", \"amount_cents\": 2500},\n    session_id=\"sess_prod_01J4X\",\n    tool_platform=\"payments\",\n)\n\nawait firewall.verify_delegated_execution(\n    specialist_agent_id=\"billing_specialist\",\n    tool_name=\"payments.refund.create\",\n    tool_args={\"customer_id\": \"cust_123\", \"amount_cents\": 2500},\n    session_id=\"sess_prod_01J4X\",\n)\n```\n\n**Streaming output sanitization:**\n\n```\nasync for safe_chunk in firewall.sanitize_streaming_response(\n    chunks=foundry_stream,\n    agent_id=\"foundry_support_agent\",\n    session_id=\"sess_prod_01J4X\",\n):\n    yield safe_chunk\n```\n\n## Microsoft AI Foundry capabilities, notes, and imports\n\nSupports: prompt/run validation via `validate_prompt_contract()`\n\n, `before_run()`\n\n, or `run_secure()`\n\n· middleware hooks via `create_middleware()`\n\n/ `install_on_client()`\n\n· local function-tool analysis via `analyze_tool_execution()`\n\n, `before_tool_call()`\n\n, or `wrap_tool_executor()`\n\n· `@firewall.secure_tool(...)`\n\nfor high-risk callables · lightweight JSON-schema validation and sanitized-argument execution · enterprise identity context propagation for Entra IDs, roles/scopes, Foundry project IDs, and Purview/sensitivity labels · production-mode stable session ID and output-sanitization requirements · output DLP through Mesh · structured-output preservation · local rate limits, size limits, retries, circuit breaker, audit logging, OpenTelemetry span events · streaming sanitization · optional handoff authorization and SDK-managed multi-worker verification · emergency deny lists.\n\nNotes: Foundry function calling asks your application to execute local functions and return tool output — wrap that local execution before output is submitted back to Foundry. Production deployments should pass a stable `session_id`\n\n, `run_id`\n\n, `trace_id`\n\n, `conversation_id`\n\n, or `thread_id`\n\n; generated fallback IDs are for local development only. Pass Entra identity, roles/scopes, Foundry project IDs, and Purview/sensitivity labels on `ctx`\n\nor `policy_context`\n\nfor identity-aware server-side policy. Hosted tools executing entirely inside a remote provider runtime can only be protected at the local request/response boundary. Threat-contract prompt and tool analysis additionally require `AGENTICDOME_BEARER_TOKEN`\n\n.\n\nOfficial references: [Foundry function calling](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/tools/function-calling) · [Foundry agents quickstart](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart)\n\n```\nfrom agenticdome_sdk.microsoft_ai_foundry import (\n    AgenticDomeMicrosoftAIFoundryFirewall,\n    FirewallConfig,\n    MicrosoftAIFoundryDenied,\n    MicrosoftAIFoundryFirewallError,\n    MicrosoftAIFoundryConfigurationError,\n    DecisionTokenRecord,\n    DecisionTokenStore,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n)\n```\n\nThe OpenAI Agents SDK ships agents, function tools, guardrails, handoffs, sessions, streaming, and tracing; AgenticDome complements those primitives by enforcing tenant policy before local tool execution, validating delegated specialist execution, and sanitizing outputs before they leave the runtime.\n\n```\npip install \"agenticdome-python-sdk[openai-agents]\"   # installs the openai-agents package\n```\n\n**Secure a runner boundary:**\n\n``` python\nfrom agents import Agent, Runner\nfrom agenticdome_sdk.openai_agents import AgenticDomeOpenAIAgentsFirewall\n\nfirewall = AgenticDomeOpenAIAgentsFirewall()\nagent = Agent(name=\"support_agent\", instructions=\"Help support users safely.\")\n\nresult = await firewall.run_agent_securely(\n    runner=Runner,\n    agent=agent,\n    input_text=\"Check customer refund status.\",\n    session_id=\"sess_prod_01J4X\",\n)\n```\n\nFor streamed runs, use `run_agent_stream_securely()`\n\nor pass the stream through `sanitize_streaming_response()`\n\nbefore returning chunks.\n\n**Register guardrail helpers** where your wiring supports input/output guardrail slots — but keep tool authorization at function-tool boundaries, because tool execution can happen multiple times inside one run:\n\n```\ninput_guardrail = firewall.create_input_guardrail()\noutput_guardrail = firewall.create_output_guardrail()\n```\n\n**Secure a function tool** — wrap the local implementation before exposing it with `@function_tool`\n\n; sanitized arguments replace originals and SDK-managed security metadata is never passed to the business handler:\n\n``` python\nfrom agents import function_tool\n\nasync def raw_lookup_customer(ctx, args):\n    return {\"customer_id\": args[\"customer_id\"], \"email\": \"alice@example.com\"}\n\nsecure_lookup_customer = firewall.wrap_tool_handler(\n    tool_name=\"crm.customer.read\",\n    tool_platform=\"crm\",\n    tool_schema={\"required\": [\"customer_id\"], \"properties\": {\"customer_id\": {\"type\": \"string\"}}},\n    handler=raw_lookup_customer,\n)\n\n@function_tool\nasync def lookup_customer(customer_id: str) -> str:\n    return await secure_lookup_customer(\n        {\"agent_id\": \"support_agent\", \"session_id\": \"sess_prod_01J4X\"},\n        {\"customer_id\": customer_id},\n    )\n```\n\n**Delegated specialist tool pattern:**\n\n```\nawait firewall.authorize_manager_handoff(\n    session_id=\"sess_prod_01J4X\",\n    manager_agent_id=\"triage_agent\",\n    specialist_agent_id=\"refund_agent\",\n    tool_name=\"payments.refund.create\",\n    tool_args={\"customer_id\": \"cust_123\", \"amount\": 250},\n    text=\"Triage agent delegates refund creation to refund specialist.\",\n    tool_platform=\"payments\",\n)\n\nsecure_refund_tool = firewall.wrap_delegated_tool_handler(\n    tool_name=\"payments.refund.create\",\n    handler=raw_refund_handler,\n)\n```\n\n## OpenAI Agents SDK capabilities, configuration, notes, and imports\n\nSupports: prompt ingress via `screen_input()`\n\n, `run_agent_securely()`\n\n, `run_agent_stream_securely()`\n\n, or `create_input_guardrail()`\n\n· function-tool authorization via `wrap_tool_handler()`\n\n/ `@firewall.secure_tool(...)`\n\n· sanitized arguments and optional schema validation · handoff authorization via `authorize_manager_handoff()`\n\n· specialist-side verification via `verify_specialist_execution()`\n\nand `wrap_delegated_tool_handler()`\n\n· SDK-managed one-time multi-worker delegation state · output DLP via `sanitize_output()`\n\nand `create_output_guardrail()`\n\n· streaming sanitization · structured-output preservation and sanitized JSON parsing · production mode with stable session IDs · size limits, rate limits, retries/backoff, circuit breaker, audit logs, OpenTelemetry events, identity-rich policy context, emergency deny lists.\n\n```\nexport AGENTICDOME_PLATFORM=\"openai_agents_sdk\"\nexport AGENTICDOME_PRODUCTION_MODE=\"true\"\nexport AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=\"true\"\nexport AGENTICDOME_OPENAI_AGENTS_MAX_INPUT_CHARS=\"50000\"\nexport AGENTICDOME_OPENAI_AGENTS_MAX_OUTPUT_CHARS=\"100000\"\nexport AGENTICDOME_OPENAI_AGENTS_MAX_TOOL_ARG_CHARS=\"20000\"\nexport AGENTICDOME_OPENAI_AGENTS_RATE_LIMIT_PER_MINUTE=\"120\"\nexport AGENTICDOME_OPENAI_AGENTS_RETRY_ATTEMPTS=\"2\"\nexport AGENTICDOME_OPENAI_AGENTS_CIRCUIT_BREAKER_FAILURES=\"5\"\nexport AGENTICDOME_OPENAI_AGENTS_AUDIT_LOGGING=\"true\"\nexport AGENTICDOME_OPENAI_AGENTS_OTEL_ENABLED=\"true\"\n# Optional for distributed multi-worker delegation:\n# export AGENTICDOME_REDIS_URL=\"redis://localhost:6379/0\"\n# export AGENTICDOME_REDIS_KEY_PREFIX=\"AgenticDome:openai_agents:handoff\"\n# export AGENTICDOME_TOKEN_HMAC_SECRET=\"replace-with-secret-from-your-secret-manager\"\n```\n\nNotes: guardrails are useful at run boundaries, but side-effecting local tools still need function-tool wrappers. Handoffs are represented as tools to the model, so manager-to-specialist policy should be enforced where handoff/tool execution is invoked. Hosted tools, MCP tools, and remote runtimes can only be protected at the local request/response boundary. Use stable `session_id`\n\n/`run_id`\n\n/`trace_id`\n\n/`conversation_id`\n\n/`thread_id`\n\nvalues and Redis-backed token storage when authorization and execution can happen in different workers.\n\nOfficial references: [Overview](https://openai.github.io/openai-agents-python/) · [Tools](https://openai.github.io/openai-agents-python/tools/) · [Guardrails](https://openai.github.io/openai-agents-python/guardrails/) · [Handoffs](https://openai.github.io/openai-agents-python/handoffs/)\n\n```\nfrom agenticdome_sdk.openai_agents import (\n    AgenticDomeOpenAIAgentsFirewall,\n    FirewallConfig,\n    OpenAIAgentsFirewallDenied,\n    OpenAIAgentsFirewallError,\n    DecisionTokenRecord,\n    DecisionTokenStore,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n)\n```\n\nThe adapter uses Claude Agent SDK's native hook contract for prompt submission, pre-tool permission decisions, and post-tool output replacement. It also wraps the asynchronous `query()`\n\nand `ClaudeSDKClient.receive_response()`\n\npipelines so final assistant text is reviewed before your application returns it.\n\n```\npip install \"agenticdome-python-sdk[claude]\"\n```\n\n**Secure a ClaudeSDKClient and its built-in/MCP tools:**\n\n``` python\nfrom claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient\nfrom agenticdome_sdk.claude import AgenticDomeClaudeFirewall\n\nfirewall = AgenticDomeClaudeFirewall()\noptions = ClaudeAgentOptions(allowed_tools=[\"Read\", \"mcp__crm__lookup\"])\nfirewall.install_on_options(\n    options,\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"claude_support_agent\",\n)\n\nasync with ClaudeSDKClient(options=options) as client:\n    async for message in firewall.run_client_securely(\n        client,\n        \"Look up the customer's active support case.\",\n        session_id=\"sess_prod_01J4X\",\n        agent_id=\"claude_support_agent\",\n    ):\n        consume(message)\n```\n\nFor the one-shot API, iterate `firewall.secure_query(prompt, session_id=..., options=...)`\n\n. If the run may execute built-in tools, install the returned hook matchers on its options as well; `secure_query()`\n\nitself covers ingress and returned messages.\n\n**Compose with Claude's native SDK MCP @tool:**\n\n```\n@firewall.secure_sdk_tool(\n    \"lookup_customer\",\n    \"Look up a customer support profile\",\n    {\"customer_id\": str},\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"claude_support_agent\",\n    tool_platform=\"crm\",\n)\nasync def lookup_customer(args):\n    return {\"content\": [{\"type\": \"text\", \"text\": crm_lookup(args[\"customer_id\"])}]}\n```\n\nThe `PreToolUse`\n\nhook returns Claude's native `permissionDecision: deny`\n\nresponse before local side effects. If policy supplies sanitized arguments, it returns `updatedInput`\n\n. The `PostToolUse`\n\nhook uses `updatedToolOutput`\n\nso DLP-reviewed tool data is what the model sees.\n\n```\nexport AGENTICDOME_PLATFORM=\"claude_agent_sdk\"\nexport AGENTICDOME_CLAUDE_AGENT_ID=\"claude_support_agent\"\nexport AGENTICDOME_CLAUDE_MAX_INPUT_CHARS=\"50000\"\nexport AGENTICDOME_CLAUDE_MAX_OUTPUT_CHARS=\"100000\"\nexport AGENTICDOME_CLAUDE_MAX_TOOL_ARG_CHARS=\"20000\"\nexport AGENTICDOME_CLAUDE_STREAMING_BUFFER_CHARS=\"4000\"\nexport AGENTICDOME_CLAUDE_RATE_LIMIT_PER_MINUTE=\"60\"\nexport AGENTICDOME_CLAUDE_RETRY_ATTEMPTS=\"2\"\nexport AGENTICDOME_CLAUDE_RETRY_BACKOFF_S=\"0.25\"\nexport AGENTICDOME_CLAUDE_CIRCUIT_BREAKER_FAILURES=\"5\"\nexport AGENTICDOME_CLAUDE_CIRCUIT_BREAKER_RESET_S=\"60\"\nexport AGENTICDOME_CLAUDE_AUDIT_LOGGING=\"true\"\nexport AGENTICDOME_CLAUDE_OTEL_ENABLED=\"true\"\nexport AGENTICDOME_CLAUDE_STRICT_DELEGATED_EXECUTION=\"true\"\nexport AGENTICDOME_CLAUDE_EMERGENCY_BLOCK_TOOLS=\"\"\nexport AGENTICDOME_CLAUDE_EMERGENCY_BLOCK_AGENTS=\"\"\n```\n\nUse `authorize_manager_handoff()`\n\nand `verify_specialist_execution()`\n\nwhen a manager delegates sensitive work. Configure the documented shared store and integrity secret when authorization and specialist execution can land on different workers. Claude hooks protect operations visible to the local SDK process; externally hosted services still require enforcement at their local gateway or MCP host.\n\nOfficial references: [Claude Agent SDK Python](https://github.com/anthropics/claude-agent-sdk-python) · [Claude Agent SDK overview](https://platform.claude.com/docs/en/agent-sdk/overview)\n\nsmolagents `CodeAgent`\n\ngenerates Python and invokes `python_executor(code)`\n\nbefore step callbacks run. The adapter therefore wraps the executor itself, wraps every native `Tool`\n\n, sanitizes step observations before the next model turn, and enforces managed-agent handoffs with bound decision tokens.\n\n```\npip install \"agenticdome-python-sdk[smolagents]\"\npython\nfrom smolagents import CodeAgent, InferenceClientModel, tool\nfrom agenticdome_sdk.smolagents import AgenticDomeSmolagentsFirewall\n\n@tool\ndef lookup_customer(customer_id: str) -> str:\n    \"\"\"Look up a customer by ID.\"\"\"\n    return crm_lookup(customer_id)\n\nagent = CodeAgent(tools=[lookup_customer], model=InferenceClientModel())\nfirewall = AgenticDomeSmolagentsFirewall()\n\nresult = firewall.run_agent_securely(\n    agent,\n    \"Look up customer cust_123 for their active support case.\",\n    session_id=\"sess_prod_01J4X\",\n    agent_id=\"smol_support_agent\",\n)\n```\n\n`attach_firewall(agent, session_id=...)`\n\nis idempotent and can be used when another component owns `agent.run()`\n\n. For streaming, use `run_agent_stream_securely()`\n\nso event output is reviewed before it is yielded. Direct `agent.run()`\n\nafter attachment still gets tool, code, managed-agent, and step-observation enforcement, but the application should use the secure run wrapper for final-output DLP.\n\n```\nexport AGENTICDOME_PLATFORM=\"smolagents\"\nexport AGENTICDOME_SMOLAGENTS_AGENT_ID=\"smol_support_agent\"\nexport AGENTICDOME_SMOLAGENTS_MAX_INPUT_CHARS=\"50000\"\nexport AGENTICDOME_SMOLAGENTS_MAX_OUTPUT_CHARS=\"100000\"\nexport AGENTICDOME_SMOLAGENTS_MAX_TOOL_ARG_CHARS=\"20000\"\nexport AGENTICDOME_SMOLAGENTS_STREAMING_BUFFER_CHARS=\"4000\"\nexport AGENTICDOME_SMOLAGENTS_RATE_LIMIT_PER_MINUTE=\"60\"\nexport AGENTICDOME_SMOLAGENTS_RETRY_ATTEMPTS=\"2\"\nexport AGENTICDOME_SMOLAGENTS_RETRY_BACKOFF_S=\"0.25\"\nexport AGENTICDOME_SMOLAGENTS_CIRCUIT_BREAKER_FAILURES=\"5\"\nexport AGENTICDOME_SMOLAGENTS_CIRCUIT_BREAKER_RESET_S=\"60\"\nexport AGENTICDOME_SMOLAGENTS_AUDIT_LOGGING=\"true\"\nexport AGENTICDOME_SMOLAGENTS_OTEL_ENABLED=\"true\"\nexport AGENTICDOME_SMOLAGENTS_EMERGENCY_BLOCK_TOOLS=\"\"\nexport AGENTICDOME_SMOLAGENTS_EMERGENCY_BLOCK_AGENTS=\"\"\nexport AGENTICDOME_SMOLAGENTS_STRICT_DELEGATED_EXECUTION=\"true\"\nexport AGENTICDOME_SMOLAGENTS_SCAN_CODE_EXPRESSIONS=\"true\"\n```\n\nKeep code-expression scanning enabled in production. It adds business-intent policy before smolagents' local or remote executor; it does not replace the executor's OS/container/WASM sandbox. The adapter intentionally sends generated code and serialized tool arguments to the configured AgenticDome sidecar, so place that sidecar within the approved trust boundary and apply normal data-residency controls.\n\nOfficial references: [smolagents agents](https://huggingface.co/docs/smolagents/main/reference/agents) · [smolagents tools](https://huggingface.co/docs/smolagents/main/reference/tools)\n\nAgno's Agent reference documents `pre_hooks`\n\n, `post_hooks`\n\n, and `tool_hooks`\n\n; the adapter attaches to those boundaries so policy is enforced before prompts/tools run and before output returns. Middleware/plugin-shaped helpers are available for applications that centralize hook registration.\n\n```\npip install \"agenticdome-python-sdk[agno]\"     # install agno separately as needed\n```\n\n**Attach firewall hooks** in the module where you create the Agno `Agent`\n\n, Team, Workflow, or AgentOS component (`attach_firewall()`\n\nis idempotent):\n\n``` python\nfrom agno.agent import Agent\nfrom agenticdome_sdk.agno import AgenticDomeAgnoFirewall\n\nfirewall = AgenticDomeAgnoFirewall()\n\nsupport_agent = Agent(\n    name=\"support_agent\",\n    model=\"openai:gpt-5.5\",\n    tools=[lookup_customer, create_refund],\n)\n\nfirewall.attach_firewall(support_agent)\nphp\npre_hooks   -> prompt input, tool authorization, delegation authorization, token verification\npost_hooks  -> final output DLP and redaction/blocking\ntool_hooks  -> additional local tool boundary enforcement where Agno invokes tool hooks\n```\n\nFor centralized registration layers:\n\n```\nhook_bundle = firewall.create_hook_bundle()\nmiddleware = firewall.create_middleware()\nplugin = firewall.create_plugin()\n```\n\n**Decorate high-risk tools** — anything that reads sensitive data, mutates state, sends messages, writes files, calls payment systems, or triggers external APIs:\n\n```\n@firewall.secure_tool(\n    tool_name=\"crm.customer.read\",\n    tool_platform=\"crm\",\n    tool_schema={\"required\": [\"customer_id\"], \"properties\": {\"customer_id\": {\"type\": \"string\"}}},\n)\ndef lookup_customer(agent, customer_id: str) -> dict:\n    return {\"customer_id\": customer_id, \"email\": \"alice@example.com\"}\n```\n\n**Delegation** — pass target metadata in hook kwargs or tool args; AgenticDome authorizes the handoff and stores the decision token for specialist verification:\n\n```\nfirewall.pre_hook(\n    manager_agent,\n    session_id=\"sess_prod_01J4X\",\n    input=\"Delegate refund execution to payment specialist.\",\n    tool_name=\"delegate_refund\",\n    tool_platform=\"payments\",\n    tool_args={\n        \"target_agent_id\": \"payments_specialist\",\n        \"target_tool_name\": \"payments.refund.create\",\n        \"target_tool_args\": {\"customer_id\": \"cust_123\", \"amount\": 250},\n    },\n)\n```\n\nThe specialist side verifies a token passed in args or recovers it from the configured in-process or optional Redis store; stored tokens are consumed once:\n\n```\nfirewall.pre_hook(\n    payments_specialist,\n    session_id=\"sess_prod_01J4X\",\n    tool_name=\"payments.refund.create\",\n    tool_args={\"customer_id\": \"cust_123\", \"amount\": 250},\n)\n```\n\n**Retrieved context and streaming sanitization** — before retrieved or streamed content is shown to a user or re-enters an agent loop:\n\n```\nsafe_context = firewall.sanitize_retrieved_text(\n    text=retrieved_context,\n    agent_id=\"support_agent\",\n    session_id=\"sess_prod_01J4X\",\n    policy_context={\"source\": \"agno_knowledge\"},\n)\n\nasync for safe_chunk in firewall.sanitize_streaming_response(\n    chunks,\n    agent_id=\"support_agent\",\n    session_id=\"sess_prod_01J4X\",\n):\n    yield safe_chunk\n```\n\n## Agno capabilities, configuration, notes, and imports\n\nSupports: prompt ingress via `pre_hook`\n\n/ `cybersec_pre_hook`\n\n· tool-call authorization via `pre_hook`\n\n, `tool_hook`\n\n, and `@firewall.secure_tool`\n\n· sanitized arguments and optional schema validation · delegation authorization and specialist-side one-time verification through SDK-managed state · output DLP via `post_hook`\n\n/ `cybersec_post_hook`\n\nwith structured-output preservation · retrieved-context sanitization for Agno knowledge/RAG pipelines · streaming sanitization · production mode with stable session IDs · size limits, rate limits, retries/backoff, circuit breaker, audit logs, OpenTelemetry events, identity-rich policy context, emergency deny lists.\n\n```\nexport AGENTICDOME_PLATFORM=\"agno\"\nexport AGENTICDOME_PRODUCTION_MODE=\"true\"\nexport AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=\"true\"\nexport AGENTICDOME_AGNO_MAX_INPUT_CHARS=\"50000\"\nexport AGENTICDOME_AGNO_MAX_OUTPUT_CHARS=\"100000\"\nexport AGENTICDOME_AGNO_MAX_TOOL_ARG_CHARS=\"20000\"\nexport AGENTICDOME_AGNO_RATE_LIMIT_PER_MINUTE=\"120\"\nexport AGENTICDOME_AGNO_RETRY_ATTEMPTS=\"2\"\nexport AGENTICDOME_AGNO_CIRCUIT_BREAKER_FAILURES=\"5\"\nexport AGENTICDOME_AGNO_AUDIT_LOGGING=\"true\"\nexport AGENTICDOME_AGNO_OTEL_ENABLED=\"true\"\n# Optional for distributed multi-worker delegation:\n# export AGENTICDOME_REDIS_URL=\"redis://localhost:6379/0\"\n# export AGENTICDOME_REDIS_KEY_PREFIX=\"AgenticDome:agno:handoff\"\n# export AGENTICDOME_TOKEN_HMAC_SECRET=\"replace-with-secret-from-your-secret-manager\"\n```\n\nNotes: environment configuration alone does not attach AgenticDome — call `attach_firewall(agent_or_team)`\n\n, register `create_hook_bundle()`\n\n, use the middleware/plugin helper, or assign `cybersec_pre_hook`\n\n, `cybersec_post_hook`\n\n, and `cybersec_tool_hook`\n\ndirectly. Hosted/remote tools can only be protected at the local request/response boundary. Use stable `session_id`\n\n/`run_id`\n\n/`trace_id`\n\nvalues. Configure the optional Redis store only when delegation authorization and execution cross workers or pods.\n\nOfficial references: [Agno SDK overview](https://docs.agno.com/features/sdk) · [Agent reference](https://docs.agno.com/reference/agents/agent)\n\n```\nfrom agenticdome_sdk.agno import (\n    AgenticDomeAgnoFirewall,\n    FirewallConfig,\n    AgenticDomeAgnoDenied,\n    DecisionTokenRecord,\n    DecisionTokenStore,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n    attach_firewall,\n    cybersec_pre_hook,\n    cybersec_post_hook,\n    cybersec_tool_hook,\n    sanitize_retrieved_text,\n)\n```\n\nRegister at agent construction with ADK callback keyword arguments, attach to an existing agent, or expose as a plugin-style object for ADK plugin registration.\n\n```\npip install \"agenticdome-python-sdk[google-adk]\"\n```\n\n**Register callbacks** — `build_callback_kwargs()`\n\nreturns the official callback keyword names used by `LlmAgent(...)`\n\n:\n\n``` python\nfrom google.adk.agents import LlmAgent\nfrom agenticdome_sdk.google_adk import AgenticDomeGoogleADKFirewall\n\nfirewall = AgenticDomeGoogleADKFirewall()\n\nagent = LlmAgent(\n    name=\"support_adk_agent\",\n    model=\"gemini-2.5-flash\",\n    instruction=\"Help support analysts safely.\",\n    **firewall.build_callback_kwargs(),\n)\nfirewall.install_on_agent(agent)      # attach to an existing agent\nplugin = firewall.create_plugin()     # plugin-style registration\n```\n\n**Tool protection** — sanitized arguments replace originals and SDK-managed security metadata is never passed to the business handler; pass a Pydantic model, Pydantic v1 model, or JSON-schema-like dict to validate arguments:\n\n```\n@firewall.secure_tool(tool_name=\"crm.customer.read\", tool_platform=\"crm\")\ndef lookup_customer(tool_context, args):\n    return crm.get_customer(args[\"customer_id\"])\n\nsecured_lookup = firewall.wrap_tool_handler(\n    tool_name=\"crm.customer.read\",\n    tool_platform=\"crm\",\n    tool_schema={\"required\": [\"customer_id\"], \"properties\": {\"customer_id\": {\"type\": \"string\"}}},\n    handler=lookup_customer,\n)\n```\n\n**Multi-agent delegation** — use the public handoff methods so the adapter manages authorization state and verifies delegated execution before the specialist runs the tool:\n\n```\nrecord = await firewall.authorize_manager_handoff(\n    source_agent_id=\"manager\",\n    target_agent_id=\"filesystem_specialist\",\n    target_tool_name=\"filesystem.read\",\n    target_tool_args={\"path\": \"/reports/q4.txt\"},\n    tool_context=tool_context,\n)\n\nawait firewall.verify_delegated_execution(\n    target_agent_id=\"filesystem_specialist\",\n    tool_name=\"filesystem.read\",\n    tool_args={\"path\": \"/reports/q4.txt\"},\n    tool_context=tool_context,\n    decision_token=record.decision_token,\n)\n```\n\n## Google ADK capabilities, configuration, notes, and imports\n\nSupports: prompt screening via `before_model`\n\n· model output sanitization via `after_model`\n\n· tool argument authorization, schema validation, and sanitized-argument enforcement via `before_tool`\n\n· tool result sanitization with structured JSON preservation via `after_tool`\n\n· lifecycle audit visibility via `before_agent`\n\n/ `after_agent`\n\n· explicit wrappers via `wrap_tool_handler()`\n\n/ `@firewall.secure_tool(...)`\n\n· manager/specialist handoff authorization with SDK-managed one-time state · streaming sanitization with a sliding review buffer · rate limits, size limits, retries/backoff, circuit breaker, structured audit logs, OpenTelemetry span events, identity-rich policy context, emergency deny lists.\n\n```\nexport AGENTICDOME_PLATFORM=\"google_adk\"\nexport AGENTICDOME_GOOGLE_ADK_AGENT_ID=\"support_adk_agent\"\nexport AGENTICDOME_SANITIZE_MODEL_OUTPUT=\"true\"\nexport AGENTICDOME_SANITIZE_TOOL_OUTPUT=\"true\"\nexport AGENTICDOME_PRODUCTION_MODE=\"true\"\nexport AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=\"true\"\nexport AGENTICDOME_HANDOFF_TOKEN_TTL_S=\"900\"\nexport AGENTICDOME_GOOGLE_ADK_MAX_INPUT_CHARS=\"50000\"\nexport AGENTICDOME_GOOGLE_ADK_MAX_OUTPUT_CHARS=\"100000\"\nexport AGENTICDOME_GOOGLE_ADK_MAX_TOOL_ARG_CHARS=\"20000\"\nexport AGENTICDOME_GOOGLE_ADK_RATE_LIMIT_PER_MINUTE=\"120\"\nexport AGENTICDOME_GOOGLE_ADK_RETRY_ATTEMPTS=\"2\"\nexport AGENTICDOME_GOOGLE_ADK_CIRCUIT_BREAKER_FAILURES=\"5\"\nexport AGENTICDOME_GOOGLE_ADK_AUDIT_LOGGING=\"true\"\nexport AGENTICDOME_GOOGLE_ADK_OTEL_ENABLED=\"true\"\n# Optional for distributed multi-worker handoff verification:\n# export AGENTICDOME_REDIS_URL=\"redis://localhost:6379/0\"\n# export AGENTICDOME_REDIS_KEY_PREFIX=\"AgenticDome:google_adk:handoff\"\n# export AGENTICDOME_TOKEN_HMAC_SECRET=\"replace-with-secret-from-your-secret-manager\"\n```\n\nNotes: register callbacks, the plugin object, or tool wrappers — env config alone does not intercept ADK execution. Use async callback methods (`before_model`\n\n, `after_model`\n\n, `before_tool`\n\n, `after_tool`\n\n) when your ADK runner supports them; the `*_callback`\n\nsync methods are for synchronous configurations only. In production, provide stable ADK context values (`session_id`\n\n, `run_id`\n\n, `trace_id`\n\n, `conversation_id`\n\n, `request_id`\n\n) — otherwise the adapter fails closed when `AGENTICDOME_REQUIRE_STABLE_SESSION_ID_IN_PROD=true`\n\n. The SDK protects the local ADK callback boundary and returned content, not execution inside remote tools/services. Include Google Cloud identity and project context when available. Use the documented shared store and integrity secret for multi-worker or Kubernetes deployments.\n\n```\nfrom agenticdome_sdk.google_adk import (\n    AgenticDomeGoogleADKFirewall,\n    DecisionTokenRecord,\n    DecisionTokenStore,\n    FirewallConfig,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n)\n```\n\nProtects the local boundaries your application controls: FunctionTool functions, query calls, query-engine tools, retrieved context, and final synthesized output.\n\n```\npip install \"agenticdome-python-sdk[llamaindex]\"\n```\n\n**Secure a FunctionTool** before giving it to a LlamaIndex agent:\n\n``` python\nfrom agenticdome_sdk.llamaindex import AgenticDomeLlamaIndexFirewall\n\nfirewall = AgenticDomeLlamaIndexFirewall()\n\ndef lookup_customer(customer_id: str) -> dict:\n    return crm.get_customer(customer_id)\n\nsecure_lookup = firewall.to_function_tool(\n    lookup_customer,\n    tool_name=\"crm.customer.read\",\n    tool_platform=\"crm\",\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n\n# Or wrap explicitly without constructing a FunctionTool:\nsecure_lookup_fn = firewall.wrap_tool_function(\n    lookup_customer,\n    tool_name=\"crm.customer.read\",\n    tool_platform=\"crm\",\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n```\n\n**Query and retrieval protection** — around query engines you invoke directly, and at central assembly points:\n\n```\nanswer = await firewall.run_query_securely(\n    query_callable=query_engine.query,\n    query_text=\"Find customer renewal risk.\",\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n\nsecure_query_engine = firewall.wrap_query_engine(\n    query_engine,\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n\nsecure_retriever = firewall.wrap_retriever(\n    retriever,\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n\n# After retrievers return nodes, before retrieved text is inserted into a prompt:\nsafe_nodes = await firewall.sanitize_retrieval_result(\n    retrieval_result=nodes,\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n\n# For RAG pipelines that accept node postprocessors:\nnode_postprocessor = firewall.create_node_postprocessor(\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n```\n\n**Callback visibility** — global audit visibility, incident telemetry, optional extra blocking (keep hard enforcement in the wrappers; set `enforce_input=True`\n\nonly for an additional synchronous input check on callback query/prompt events):\n\n``` python\nfrom llama_index.core import Settings\nfrom llama_index.core.callbacks import CallbackManager\n\nhandler = firewall.create_callback_handler(\n    agent_id=\"support_llamaindex_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n\nSettings.callback_manager = CallbackManager([handler])\n```\n\n**Multi-agent handoffs** — only when your application delegates from managers to specialists that can execute sensitive tools:\n\n```\nawait firewall.authorize_manager_handoff(\n    manager_agent_id=\"triage_manager\",\n    specialist_agent_id=\"billing_specialist\",\n    tool_name=\"billing.refund.create\",\n    tool_args={\"invoice_id\": \"inv_123\", \"amount\": 2500},\n    tool_platform=\"billing\",\n    session_id=\"sess_prod_01J4X\",\n)\n\nawait firewall.verify_delegated_execution(\n    specialist_agent_id=\"billing_specialist\",\n    tool_name=\"billing.refund.create\",\n    tool_args={\"invoice_id\": \"inv_123\", \"amount\": 2500},\n    session_id=\"sess_prod_01J4X\",\n)\n```\n\n## LlamaIndex capabilities, configuration, notes, and imports\n\nSupports: prompt/query screening before query execution · FunctionTool and local tool authorization · tool output review before results return to the agent · query output DLP and redaction · retrieval-result sanitization before retrieved context enters a prompt or reaches a user · query-engine and retriever wrappers for central assembly points · node postprocessor creation for RAG context sanitization · callback handler creation for global audit visibility and optional extra input blocking · optional handoff authorization and token verification · optional Redis-backed token storage for multi-worker deployments · optional creation of LlamaIndex `FunctionTool`\n\nobjects when LlamaIndex is installed.\n\n```\nexport AGENTICDOME_PLATFORM=\"llamaindex\"\nexport AGENTICDOME_LLAMAINDEX_AGENT_ID=\"support_llamaindex_agent\"\nexport AGENTICDOME_SANITIZE_QUERY_OUTPUT=\"true\"\nexport AGENTICDOME_SANITIZE_TOOL_OUTPUT=\"true\"\nexport AGENTICDOME_HANDOFF_TOKEN_TTL_S=\"900\"\n# Optional for distributed multi-worker handoff verification:\n# export AGENTICDOME_REDIS_URL=\"redis://localhost:6379/0\"\n# export AGENTICDOME_REDIS_KEY_PREFIX=\"AgenticDome:llamaindex:handoff\"\n```\n\nNotes: wrap tools, query calls, query engines, retrievers, node postprocessors, callbacks, or output boundaries — env config alone does not intercept. LlamaIndex has many integrations and provider-native tool specs; remote services outside your process must be protected at their request/response boundary. Place wrappers in the module where components are assembled, not only inside request handlers. Use stable `session_id`\n\nvalues, and set `AGENTICDOME_REQUIRE_SESSION_ID=true`\n\nwhen every query/tool call must be traceable.\n\n```\nfrom agenticdome_sdk.llamaindex import (\n    AgenticDomeLlamaIndexFirewall,\n    DecisionTokenRecord,\n    DecisionTokenStore,\n    FirewallConfig,\n    InMemoryDecisionTokenStore,\n    RedisDecisionTokenStore,\n)\n```\n\nFor services that call Bedrock Runtime directly, stream model responses, invoke Bedrock Agents, implement action-group Lambda handlers, execute local tool-use results, or process knowledge-base retrieval. The adapter accepts any boto3-compatible client and does not import boto3 at module import time, so tests and custom clients use the same wrapper.\n\n```\npip install \"agenticdome-python-sdk[bedrock]\"\n```\n\n**Secure Converse** — the flow is `messages/system → prompt screen → Bedrock Converse → output review → sanitized response`\n\n:\n\n``` python\nimport boto3\nfrom agenticdome_sdk.aws_bedrock import AgenticDomeAWSBedrockFirewall\n\nbedrock = boto3.client(\"bedrock-runtime\", region_name=\"us-east-1\")\nfirewall = AgenticDomeAWSBedrockFirewall()\n\nresponse = await firewall.converse_securely(\n    bedrock_runtime_client=bedrock,\n    model_id=\"anthropic.claude-3-5-sonnet-20241022-v2:0\",\n    messages=[\n        {\n            \"role\": \"user\",\n            \"content\": [{\"text\": \"Summarize this customer case.\"}],\n        }\n    ],\n    agent_id=\"support_bedrock_agent\",\n    session_id=\"sess_prod_01J4X\",\n)\n# Streaming Converse:\nasync for event in firewall.converse_stream_securely(\n    bedrock_runtime_client=bedrock,\n    model_id=\"anthropic.claude-3-5-sonnet-20241022-v2:0\",\n    messages=messages,\n    agent_id=\"support_bedrock_agent\",\n    session_id=\"sess_prod_01J4X\",\n):\n    yield event\n```\n\n**Secure InvokeModel** — for provider-specific payloads (Titan, Claude, Llama, Mistral, or other model-native bodies):\n\n``` python\nimport json\n\nresponse = await firewall.invoke_model_securely(\n    bedrock_runtime_client=bedrock,\n    model_id=\"amazon.titan-text-express-v1\",\n    body=json.dumps({\"inputText\": \"Draft a customer email.\"}),\n    agent_id=\"support_bedrock_agent\",\n    session_id=\"sess_prod_01J4X\",\n    contentType=\"application/json\",\n    accept=\"application/json\",\n)\n# Streamed provider-native responses:\nasync for event in firewall.invoke_model_with_response_stream_securely(\n    bedrock_runtime_client=bedrock,\n    model_id=\"amazon.titan-text-express-v1\",\n    body=json.dumps({\"inputText\": \"Draft a customer email.\"}),\n    agent_id=\"support_bedrock_agent\",\n    session_id=\"sess_prod_01J4X\",\n):\n    yield event\n```\n\nThe adapter extracts prompt text from common payload shapes (`inputText`\n\n, `prompt`\n\n, `messages`\n\n, `contents`\n\n, `system`\n\n, Claude `anthropic_version`\n\nmessages, Llama/Mistral prompts, provider-native JSON bodies) and writes sanitized text back into common response fields (`outputText`\n\n, `completion`\n\n, `generation`\n\n, `answer`\n\n, `text`\n\n, `generated_text`\n\n, Converse `output.message.content[].text`\n\n).\n\n**Bedrock Agents and action groups:**", "url": "https://wpnews.pro/news/an-action-firewall-for-mcp-oauth-says-who-not-whether-the-call-should-run", "canonical_source": "https://github.com/agenticdome/agenticdome-python-sdk", "published_at": "2026-08-13 11:15:58+00:00", "updated_at": "2026-08-13 11:42:13.481768+00:00", "lang": "en", "topics": ["ai-safety", "ai-tools", "developer-tools", "ai-agents"], "entities": ["AgenticDome", "agenticdome-python-sdk", "CrewAI", "LangGraph", "Microsoft AutoGen", "PyPI", "MCP"], "alternates": {"html": "https://wpnews.pro/news/an-action-firewall-for-mcp-oauth-says-who-not-whether-the-call-should-run", "markdown": "https://wpnews.pro/news/an-action-firewall-for-mcp-oauth-says-who-not-whether-the-call-should-run.md", "text": "https://wpnews.pro/news/an-action-firewall-for-mcp-oauth-says-who-not-whether-the-call-should-run.txt", "jsonld": "https://wpnews.pro/news/an-action-firewall-for-mcp-oauth-says-who-not-whether-the-call-should-run.jsonld"}}