Skip to content

CrewAI

agent_airlock.integrations.crewai is the canonical adapter for CrewAI. It is the v0.7.2 promotion of the previously example-only CrewAI integration to adapter-shipped and closes issue #5 (opened 2026-03-14).

Why an adapter (not just @Airlock())

CrewAI v1.14.4 (2026-04-30) added native MCP server support and a litellm SSTI bump — that's the floor where the tool-call surface this adapter binds to became stable. With one wrap_crew(crew, policy=...) call, every Agent's tool registry and every task-level Task(tools=[...]) override is walked and Airlock-decorated. Users no longer have to remember the @framework_decorator over @Airlock() rule themselves.

Install

pip install "agent-airlock[crewai]"

The extra pins crewai>=1.14.4,<2.0. v1.14.4 is the floor because that's the release that introduced native MCP server support — older versions wire MCP through a different surface and would silently mis-wire. The v1.14.5a1 / a2 alpha cycle is supported but not the floor; operators on the alpha track should pin manually.

Quickstart

from agent_airlock.integrations.crewai import CrewAIAdapter
from agent_airlock.policy import STRICT_POLICY
from crewai import Agent, Crew, Task
from crewai.tools import tool

@tool("Search Tool")
def search(query: str) -> str:
    return f"Results for: {query}"

researcher = Agent(role="Researcher", goal="Investigate", tools=[search])
crew = Crew(agents=[researcher], tasks=[Task(description="...", agent=researcher)])

# wrap_crew walks every Agent's tools (and any Task(tools=[...]) overrides).
adapter = CrewAIAdapter()
adapter.wrap_crew(crew, policy=STRICT_POLICY)

# Now every tool call routes through the configured policy.
result = crew.kickoff()

What the adapter does

  1. Walks crew.agents and for each Agent, walks agent.tools (CrewAI's BaseTool registry). For an @tool-built tool the user function (func) is replaced with the Airlock(policy=...)-wrapped version, which covers run, arun and the structured-tool path an agent uses; for a BaseTool subclass it is _run. The wrapper carries the tool's name, so SecurityPolicy.allowed_tools / denied_tools lists target the tool rather than its method, and the tool's signature, so strict validation and ghost-argument stripping have parameters to check.

  2. Walks crew.tasks for task-level Task(tools=[...]) overrides — these win over Agent.tools at runtime, so they need wrapping too.

  3. Guards the tools CrewAI adds at run time: delegation tools (allow_delegation), MCP tools (agent.mcps), platform tools (agent.apps), code-execution, multimodal, memory and input-file tools, and the skill loader. None exists when the crew is walked. On the crew and agents it is given (only them), the adapter wraps the methods that hand CrewAI the final tool list and walks what they return: Crew._prepare_tools (every crew task, the hierarchical manager's delegation tools included), Crew.copy (the copies kickoff_for_each and train run) and Agent._add_skill_loader_tool (the last step before a task or Agent.kickoff parses its tools). Where those methods are missing, an agent configured to receive such tools gets a UserWarning naming them. Until 0.10.28 these tools ran outside Airlock.

  4. Pins SUPPORTED_CREWAI_VERSIONS (listed in the module) — running a version outside this set emits a UserWarning (no hard fail). Update the tuple once a new release has been smoke-tested against the adapter.

  5. wrap_agent(agent, policy=...) is also exposed as a single-agent shortcut for the standalone-researcher pattern where you don't have a Crew yet.

Honest scope

  • The adapter does not import crewai at module load — passing a stub crew (any object with an agents or tools attribute) works without the extra installed. This is what the test suite uses.
  • Real CrewAI objects (whose __module__ starts with crewai.*) do trigger the import check. If the extra is missing, the adapter raises CrewAIMissingError with a clear install hint.
  • crewai is heavy (pulls litellm, chromadb, embedchain); kept strictly opt-in via the [crewai] extra.
  • CrewAI validates arguments against the tool's args_schema before the call, in lax mode, so a coercible value such as "5" for an int arrives converted (verified on 1.15.22). Airlock's strict check applies to what CrewAI hands the tool.
  • The example-only path (examples/crewai_integration.py) remains documented and still works — @Airlock() over a raw @tool-decorated callable is supported.

Closes

Primary sources