Compare commits

..

2 Commits

Author SHA1 Message Date
iris-clawd
75ef1ecf91 test: add test script for LinearTool 2026-04-20 15:08:13 -03:00
iris-clawd
7961678879 feat: add LinearTool for Linear project management API 2026-04-20 15:08:04 -03:00
18 changed files with 198 additions and 1840 deletions

View File

@@ -83,7 +83,6 @@ intelligent automations.
## Table of contents
- [Build with AI](#build-with-ai)
- [Why CrewAI?](#why-crewai)
- [Getting Started](#getting-started)
- [Key Features](#key-features)
@@ -102,22 +101,6 @@ intelligent automations.
- [Telemetry](#telemetry)
- [License](#license)
## Build with AI
Using an AI coding agent? Teach it CrewAI best practices in one command:
**Claude Code:**
```shell
/plugin marketplace add crewAIInc/skills
```
**Cursor, Codex, Windsurf, and others ([skills.sh](https://skills.sh/crewaiinc/skills)):**
```shell
npx skills add crewaiinc/skills
```
This installs the official [CrewAI Skills](https://github.com/crewAIInc/skills) — structured instructions that teach coding agents how to scaffold Flows, configure Crews, design agents and tasks, and follow CrewAI patterns.
## Why CrewAI?
<div align="center" style="margin-bottom: 30px;">

124
crewai/tools/linear_tool.py Normal file
View File

@@ -0,0 +1,124 @@
import os
from enum import Enum
from typing import Any, Type
import httpx
from crewai.tools import BaseTool
from pydantic import BaseModel, Field
LINEAR_API_URL = "https://api.linear.app/graphql"
class LinearAction(str, Enum):
MY_ISSUES = "my_issues"
LIST_TEAMS = "list_teams"
LIST_PROJECTS = "list_projects"
class LinearToolInput(BaseModel):
action: LinearAction = Field(
description=(
"Action to perform: "
"'my_issues' — fetch issues assigned to the authenticated user; "
"'list_teams' — list all teams in the workspace; "
"'list_projects' — list all projects in the workspace."
)
)
first: int = Field(
default=25,
ge=1,
le=250,
description="Maximum number of records to return (1250).",
)
_QUERIES: dict[LinearAction, str] = {
LinearAction.MY_ISSUES: """
query MyIssues($first: Int!) {
viewer {
assignedIssues(first: $first, orderBy: updatedAt) {
nodes {
id
identifier
title
state { name }
priority
url
updatedAt
}
}
}
}
""",
LinearAction.LIST_TEAMS: """
query ListTeams($first: Int!) {
teams(first: $first) {
nodes {
id
name
key
description
}
}
}
""",
LinearAction.LIST_PROJECTS: """
query ListProjects($first: Int!) {
projects(first: $first, orderBy: updatedAt) {
nodes {
id
name
description
state
url
updatedAt
}
}
}
""",
}
def _extract(action: LinearAction, data: dict) -> list[dict]:
if action == LinearAction.MY_ISSUES:
return data["viewer"]["assignedIssues"]["nodes"]
if action == LinearAction.LIST_TEAMS:
return data["teams"]["nodes"]
if action == LinearAction.LIST_PROJECTS:
return data["projects"]["nodes"]
return []
class LinearTool(BaseTool):
name: str = "Linear API Tool"
description: str = (
"Interact with the Linear project management API. "
"Supports fetching your assigned issues, listing teams, and listing projects."
)
args_schema: Type[BaseModel] = LinearToolInput
def _run(self, action: LinearAction, first: int = 25) -> Any:
api_key = os.environ.get("LINEAR_API_KEY", "")
if not api_key:
raise EnvironmentError("LINEAR_API_KEY environment variable is not set.")
query = _QUERIES[action]
payload = {"query": query, "variables": {"first": first}}
headers = {
"Authorization": api_key,
"Content-Type": "application/json",
}
response = httpx.post(
LINEAR_API_URL,
json=payload,
headers=headers,
timeout=15,
)
response.raise_for_status()
body = response.json()
if "errors" in body:
raise RuntimeError(f"Linear API errors: {body['errors']}")
return _extract(action, body["data"])

View File

@@ -1,206 +0,0 @@
---
title: "Build with AI"
description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features."
icon: robot
mode: "wide"
---
# Build with AI
CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows.
### Supported Coding Agents
<CardGroup cols={5}>
<Card title="Claude Code" icon="message-bot" color="#D97706" />
<Card title="Cursor" icon="arrow-pointer" color="#3B82F6" />
<Card title="Codex" icon="terminal" color="#10B981" />
<Card title="Windsurf" icon="wind" color="#06B6D4" />
<Card title="Gemini CLI" icon="sparkles" color="#8B5CF6" />
</CardGroup>
<Note>
This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access.
</Note>
---
## 1. Skills — Teach Your Agent CrewAI
**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions.
<Tabs>
<Tab title="Claude Code (Plugin Marketplace)">
<img src="https://cdn.simpleicons.org/anthropic/D97706" alt="Anthropic" width="28" style={{display: "inline", verticalAlign: "middle", marginRight: "8px"}} />
CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies:
```
/plugin marketplace add crewAIInc/skills
```
</Tab>
<Tab title="npx (Any Agent)">
Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent:
```shell
npx skills add crewaiinc/skills
```
Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills).
</Tab>
</Tabs>
<Steps>
<Step title="Install the official skill pack">
Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack.
</Step>
<Step title="Your agent gets instant CrewAI expertise">
The skill pack teaches your agent:
- **Flows** — stateful apps, steps, and crew kickoffs
- **Crews & Agents** — YAML-first patterns, roles, tasks, delegation
- **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools
- **Project layout** — CLI scaffolds and repo conventions
- **Up-to-date patterns** — tracks current CrewAI docs and best practices
</Step>
<Step title="Start building">
Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session.
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Skills concept" icon="bolt" href="/en/concepts/skills">
How skills work in CrewAI agents — injection, activation, and patterns.
</Card>
<Card title="Skills landing page" icon="wand-magic-sparkles" href="/en/skills">
Overview of the crewAIInc/skills pack and what it includes.
</Card>
<Card title="AGENTS.md & coding tools" icon="terminal" href="/en/guides/coding-tools/agents-md">
Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI.
</Card>
<Card title="Skills registry (skills.sh)" icon="globe" href="https://skills.sh/crewaiinc/skills">
Official listing — skills, install stats, and audits.
</Card>
</CardGroup>
---
## 2. llms.txt — Machine-Readable Docs
CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format.
```
https://docs.crewai.com/llms.txt
```
<Tabs>
<Tab title="What is llms.txt?">
[`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs.
CrewAI's `llms.txt` is **already live** — your agent can use it right now.
</Tab>
<Tab title="How to use it">
Point your coding agent at the URL when it needs CrewAI reference docs:
```
Fetch https://docs.crewai.com/llms.txt for CrewAI documentation.
```
Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides.
</Tab>
<Tab title="Why it matters">
- **No scraping required** — clean, structured content in one request
- **Always up-to-date** — served directly from docs.crewai.com
- **Optimized for LLMs** — formatted for context windows, not browsers
- **Complements skills** — skills teach patterns, llms.txt provides reference
</Tab>
</Tabs>
---
## 3. Deploy to Enterprise
Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes.
<Steps>
<Step title="Build locally">
Scaffold and test your crew or flow:
```bash
crewai create crew my_crew
cd my_crew
crewai run
```
</Step>
<Step title="Prepare for deployment">
Ensure your project structure is ready:
```bash
crewai deploy --prepare
```
See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements.
</Step>
<Step title="Deploy to AMP">
Push to the CrewAI AMP platform:
```bash
crewai deploy
```
You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio).
</Step>
<Step title="Access via API">
Your deployed crew gets a REST API endpoint. Integrate it into any application:
```bash
curl -X POST https://app.crewai.com/api/v1/crews/<crew-id>/kickoff \
-H "Authorization: Bearer $CREWAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputs": {"topic": "AI agents"}}'
```
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Deploy to AMP" icon="rocket" href="/en/enterprise/guides/deploy-to-amp">
Full deployment guide — CLI, GitHub, and Crew Studio methods.
</Card>
<Card title="AMP introduction" icon="globe" href="/en/enterprise/introduction">
Platform overview — what AMP provides for production crews.
</Card>
</CardGroup>
---
## 4. Enterprise Features
CrewAI AMP is built for production teams. Here's what you get beyond deployment.
<CardGroup cols={2}>
<Card title="Observability" icon="chart-line">
Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time.
</Card>
<Card title="Crew Studio" icon="paintbrush">
No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly.
</Card>
<Card title="Webhook Streaming" icon="webhook">
Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer.
</Card>
<Card title="Team Management" icon="users">
SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team.
</Card>
<Card title="Tool Repository" icon="toolbox">
Publish and share custom tools across your organization. Install community tools from the registry.
</Card>
<Card title="Factory (Self-Hosted)" icon="server">
Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls.
</Card>
</CardGroup>
<AccordionGroup>
<Accordion title="Who is AMP for?">
AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents.
</Accordion>
<Accordion title="What deployment options are available?">
- **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production
- **Factory (self-hosted)** — run on your own infrastructure for full data control
- **Hybrid** — mix cloud and self-hosted based on sensitivity requirements
</Accordion>
<Accordion title="How does pricing work?">
Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request.
</Accordion>
</AccordionGroup>
<Card title="Explore CrewAI AMP →" icon="arrow-right" href="https://app.crewai.com">
Sign up and deploy your first crew to production.
</Card>

View File

@@ -79,7 +79,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -128,8 +127,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -555,7 +553,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -604,8 +601,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -1031,7 +1027,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -1080,8 +1075,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -1507,7 +1501,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -1556,8 +1549,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -1983,7 +1975,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -2032,8 +2023,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -2459,7 +2449,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -2508,8 +2497,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -2933,7 +2921,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -2982,8 +2969,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -3407,7 +3393,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -3456,8 +3441,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -3882,7 +3866,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -3931,8 +3914,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -4358,7 +4340,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -4407,8 +4388,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -4832,7 +4812,6 @@
"group": "Get Started",
"pages": [
"en/introduction",
"en/guides/coding-tools/build-with-ai",
"en/skills",
"en/installation",
"en/quickstart"
@@ -4881,8 +4860,7 @@
"group": "Coding Tools",
"icon": "terminal",
"pages": [
"en/guides/coding-tools/agents-md",
"en/guides/coding-tools/build-with-ai"
"en/guides/coding-tools/agents-md"
]
},
{
@@ -5339,7 +5317,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -5798,7 +5775,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -6257,7 +6233,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -6716,7 +6691,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -7175,7 +7149,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -7634,7 +7607,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -8092,7 +8064,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -8550,7 +8521,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -9008,7 +8978,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -9465,7 +9434,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -9922,7 +9890,6 @@
"group": "Começando",
"pages": [
"pt-BR/introduction",
"pt-BR/guides/coding-tools/build-with-ai",
"pt-BR/skills",
"pt-BR/installation",
"pt-BR/quickstart"
@@ -10410,7 +10377,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -10881,7 +10847,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -11352,7 +11317,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -11823,7 +11787,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -12294,7 +12257,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -12765,7 +12727,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -13235,7 +13196,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -13705,7 +13665,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -14175,7 +14134,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -14644,7 +14602,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -15113,7 +15070,6 @@
"group": "시작 안내",
"pages": [
"ko/introduction",
"ko/guides/coding-tools/build-with-ai",
"ko/skills",
"ko/installation",
"ko/quickstart"
@@ -15613,7 +15569,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -16084,7 +16039,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -16555,7 +16509,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -17026,7 +16979,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -17497,7 +17449,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -17968,7 +17919,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -18438,7 +18388,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -18908,7 +18857,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -19378,7 +19326,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -19847,7 +19794,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"
@@ -20316,7 +20262,6 @@
"group": "البدء",
"pages": [
"ar/introduction",
"ar/guides/coding-tools/build-with-ai",
"ar/skills",
"ar/installation",
"ar/quickstart"

View File

@@ -1,206 +0,0 @@
---
title: "Build with AI"
description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features."
icon: robot
mode: "wide"
---
# Build with AI
CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows.
### Supported Coding Agents
<CardGroup cols={5}>
<Card title="Claude Code" icon="message-bot" color="#D97706" />
<Card title="Cursor" icon="arrow-pointer" color="#3B82F6" />
<Card title="Codex" icon="terminal" color="#10B981" />
<Card title="Windsurf" icon="wind" color="#06B6D4" />
<Card title="Gemini CLI" icon="sparkles" color="#8B5CF6" />
</CardGroup>
<Note>
This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access.
</Note>
---
## 1. Skills — Teach Your Agent CrewAI
**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions.
<Tabs>
<Tab title="Claude Code (Plugin Marketplace)">
<img src="https://cdn.simpleicons.org/anthropic/D97706" alt="Anthropic" width="28" style={{display: "inline", verticalAlign: "middle", marginRight: "8px"}} />
CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies:
```
/plugin marketplace add crewAIInc/skills
```
</Tab>
<Tab title="npx (Any Agent)">
Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent:
```shell
npx skills add crewaiinc/skills
```
Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills).
</Tab>
</Tabs>
<Steps>
<Step title="Install the official skill pack">
Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack.
</Step>
<Step title="Your agent gets instant CrewAI expertise">
The skill pack teaches your agent:
- **Flows** — stateful apps, steps, and crew kickoffs
- **Crews & Agents** — YAML-first patterns, roles, tasks, delegation
- **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools
- **Project layout** — CLI scaffolds and repo conventions
- **Up-to-date patterns** — tracks current CrewAI docs and best practices
</Step>
<Step title="Start building">
Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session.
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Skills concept" icon="bolt" href="/en/concepts/skills">
How skills work in CrewAI agents — injection, activation, and patterns.
</Card>
<Card title="Skills landing page" icon="wand-magic-sparkles" href="/en/skills">
Overview of the crewAIInc/skills pack and what it includes.
</Card>
<Card title="AGENTS.md & coding tools" icon="terminal" href="/en/guides/coding-tools/agents-md">
Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI.
</Card>
<Card title="Skills registry (skills.sh)" icon="globe" href="https://skills.sh/crewaiinc/skills">
Official listing — skills, install stats, and audits.
</Card>
</CardGroup>
---
## 2. llms.txt — Machine-Readable Docs
CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format.
```
https://docs.crewai.com/llms.txt
```
<Tabs>
<Tab title="What is llms.txt?">
[`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs.
CrewAI's `llms.txt` is **already live** — your agent can use it right now.
</Tab>
<Tab title="How to use it">
Point your coding agent at the URL when it needs CrewAI reference docs:
```
Fetch https://docs.crewai.com/llms.txt for CrewAI documentation.
```
Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides.
</Tab>
<Tab title="Why it matters">
- **No scraping required** — clean, structured content in one request
- **Always up-to-date** — served directly from docs.crewai.com
- **Optimized for LLMs** — formatted for context windows, not browsers
- **Complements skills** — skills teach patterns, llms.txt provides reference
</Tab>
</Tabs>
---
## 3. Deploy to Enterprise
Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes.
<Steps>
<Step title="Build locally">
Scaffold and test your crew or flow:
```bash
crewai create crew my_crew
cd my_crew
crewai run
```
</Step>
<Step title="Prepare for deployment">
Ensure your project structure is ready:
```bash
crewai deploy --prepare
```
See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements.
</Step>
<Step title="Deploy to AMP">
Push to the CrewAI AMP platform:
```bash
crewai deploy
```
You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio).
</Step>
<Step title="Access via API">
Your deployed crew gets a REST API endpoint. Integrate it into any application:
```bash
curl -X POST https://app.crewai.com/api/v1/crews/<crew-id>/kickoff \
-H "Authorization: Bearer $CREWAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputs": {"topic": "AI agents"}}'
```
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Deploy to AMP" icon="rocket" href="/en/enterprise/guides/deploy-to-amp">
Full deployment guide — CLI, GitHub, and Crew Studio methods.
</Card>
<Card title="AMP introduction" icon="globe" href="/en/enterprise/introduction">
Platform overview — what AMP provides for production crews.
</Card>
</CardGroup>
---
## 4. Enterprise Features
CrewAI AMP is built for production teams. Here's what you get beyond deployment.
<CardGroup cols={2}>
<Card title="Observability" icon="chart-line">
Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time.
</Card>
<Card title="Crew Studio" icon="paintbrush">
No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly.
</Card>
<Card title="Webhook Streaming" icon="webhook">
Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer.
</Card>
<Card title="Team Management" icon="users">
SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team.
</Card>
<Card title="Tool Repository" icon="toolbox">
Publish and share custom tools across your organization. Install community tools from the registry.
</Card>
<Card title="Factory (Self-Hosted)" icon="server">
Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls.
</Card>
</CardGroup>
<AccordionGroup>
<Accordion title="Who is AMP for?">
AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents.
</Accordion>
<Accordion title="What deployment options are available?">
- **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production
- **Factory (self-hosted)** — run on your own infrastructure for full data control
- **Hybrid** — mix cloud and self-hosted based on sensitivity requirements
</Accordion>
<Accordion title="How does pricing work?">
Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request.
</Accordion>
</AccordionGroup>
<Card title="Explore CrewAI AMP →" icon="arrow-right" href="https://app.crewai.com">
Sign up and deploy your first crew to production.
</Card>

View File

@@ -1,206 +0,0 @@
---
title: "Build with AI"
description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features."
icon: robot
mode: "wide"
---
# Build with AI
CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows.
### Supported Coding Agents
<CardGroup cols={5}>
<Card title="Claude Code" icon="message-bot" color="#D97706" />
<Card title="Cursor" icon="arrow-pointer" color="#3B82F6" />
<Card title="Codex" icon="terminal" color="#10B981" />
<Card title="Windsurf" icon="wind" color="#06B6D4" />
<Card title="Gemini CLI" icon="sparkles" color="#8B5CF6" />
</CardGroup>
<Note>
This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access.
</Note>
---
## 1. Skills — Teach Your Agent CrewAI
**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions.
<Tabs>
<Tab title="Claude Code (Plugin Marketplace)">
<img src="https://cdn.simpleicons.org/anthropic/D97706" alt="Anthropic" width="28" style={{display: "inline", verticalAlign: "middle", marginRight: "8px"}} />
CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies:
```
/plugin marketplace add crewAIInc/skills
```
</Tab>
<Tab title="npx (Any Agent)">
Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent:
```shell
npx skills add crewaiinc/skills
```
Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills).
</Tab>
</Tabs>
<Steps>
<Step title="Install the official skill pack">
Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack.
</Step>
<Step title="Your agent gets instant CrewAI expertise">
The skill pack teaches your agent:
- **Flows** — stateful apps, steps, and crew kickoffs
- **Crews & Agents** — YAML-first patterns, roles, tasks, delegation
- **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools
- **Project layout** — CLI scaffolds and repo conventions
- **Up-to-date patterns** — tracks current CrewAI docs and best practices
</Step>
<Step title="Start building">
Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session.
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Skills concept" icon="bolt" href="/en/concepts/skills">
How skills work in CrewAI agents — injection, activation, and patterns.
</Card>
<Card title="Skills landing page" icon="wand-magic-sparkles" href="/en/skills">
Overview of the crewAIInc/skills pack and what it includes.
</Card>
<Card title="AGENTS.md & coding tools" icon="terminal" href="/en/guides/coding-tools/agents-md">
Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI.
</Card>
<Card title="Skills registry (skills.sh)" icon="globe" href="https://skills.sh/crewaiinc/skills">
Official listing — skills, install stats, and audits.
</Card>
</CardGroup>
---
## 2. llms.txt — Machine-Readable Docs
CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format.
```
https://docs.crewai.com/llms.txt
```
<Tabs>
<Tab title="What is llms.txt?">
[`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs.
CrewAI's `llms.txt` is **already live** — your agent can use it right now.
</Tab>
<Tab title="How to use it">
Point your coding agent at the URL when it needs CrewAI reference docs:
```
Fetch https://docs.crewai.com/llms.txt for CrewAI documentation.
```
Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides.
</Tab>
<Tab title="Why it matters">
- **No scraping required** — clean, structured content in one request
- **Always up-to-date** — served directly from docs.crewai.com
- **Optimized for LLMs** — formatted for context windows, not browsers
- **Complements skills** — skills teach patterns, llms.txt provides reference
</Tab>
</Tabs>
---
## 3. Deploy to Enterprise
Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes.
<Steps>
<Step title="Build locally">
Scaffold and test your crew or flow:
```bash
crewai create crew my_crew
cd my_crew
crewai run
```
</Step>
<Step title="Prepare for deployment">
Ensure your project structure is ready:
```bash
crewai deploy --prepare
```
See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements.
</Step>
<Step title="Deploy to AMP">
Push to the CrewAI AMP platform:
```bash
crewai deploy
```
You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio).
</Step>
<Step title="Access via API">
Your deployed crew gets a REST API endpoint. Integrate it into any application:
```bash
curl -X POST https://app.crewai.com/api/v1/crews/<crew-id>/kickoff \
-H "Authorization: Bearer $CREWAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputs": {"topic": "AI agents"}}'
```
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Deploy to AMP" icon="rocket" href="/en/enterprise/guides/deploy-to-amp">
Full deployment guide — CLI, GitHub, and Crew Studio methods.
</Card>
<Card title="AMP introduction" icon="globe" href="/en/enterprise/introduction">
Platform overview — what AMP provides for production crews.
</Card>
</CardGroup>
---
## 4. Enterprise Features
CrewAI AMP is built for production teams. Here's what you get beyond deployment.
<CardGroup cols={2}>
<Card title="Observability" icon="chart-line">
Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time.
</Card>
<Card title="Crew Studio" icon="paintbrush">
No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly.
</Card>
<Card title="Webhook Streaming" icon="webhook">
Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer.
</Card>
<Card title="Team Management" icon="users">
SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team.
</Card>
<Card title="Tool Repository" icon="toolbox">
Publish and share custom tools across your organization. Install community tools from the registry.
</Card>
<Card title="Factory (Self-Hosted)" icon="server">
Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls.
</Card>
</CardGroup>
<AccordionGroup>
<Accordion title="Who is AMP for?">
AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents.
</Accordion>
<Accordion title="What deployment options are available?">
- **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production
- **Factory (self-hosted)** — run on your own infrastructure for full data control
- **Hybrid** — mix cloud and self-hosted based on sensitivity requirements
</Accordion>
<Accordion title="How does pricing work?">
Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request.
</Accordion>
</AccordionGroup>
<Card title="Explore CrewAI AMP →" icon="arrow-right" href="https://app.crewai.com">
Sign up and deploy your first crew to production.
</Card>

View File

@@ -1,206 +0,0 @@
---
title: "Build with AI"
description: "Everything AI coding agents need to build, deploy, and scale with CrewAI — skills, machine-readable docs, deployment, and enterprise features."
icon: robot
mode: "wide"
---
# Build with AI
CrewAI is AI-native. This page brings together everything an AI coding agent needs to build with CrewAI — whether you're Claude Code, Codex, Cursor, Gemini CLI, or any other assistant helping a developer ship crews and flows.
### Supported Coding Agents
<CardGroup cols={5}>
<Card title="Claude Code" icon="message-bot" color="#D97706" />
<Card title="Cursor" icon="arrow-pointer" color="#3B82F6" />
<Card title="Codex" icon="terminal" color="#10B981" />
<Card title="Windsurf" icon="wind" color="#06B6D4" />
<Card title="Gemini CLI" icon="sparkles" color="#8B5CF6" />
</CardGroup>
<Note>
This page is designed to be consumed by both humans and AI assistants. If you're a coding agent, start with **Skills** to get CrewAI context, then use **llms.txt** for full docs access.
</Note>
---
## 1. Skills — Teach Your Agent CrewAI
**Skills** are instruction packs that give coding agents deep CrewAI knowledge — how to scaffold Flows, configure Crews, use tools, and follow framework conventions.
<Tabs>
<Tab title="Claude Code (Plugin Marketplace)">
<img src="https://cdn.simpleicons.org/anthropic/D97706" alt="Anthropic" width="28" style={{display: "inline", verticalAlign: "middle", marginRight: "8px"}} />
CrewAI skills are available in the **Claude Code plugin marketplace** — the same distribution channel used by top AI-native companies:
```
/plugin marketplace add crewAIInc/skills
```
</Tab>
<Tab title="npx (Any Agent)">
Works with Claude Code, Codex, Cursor, Gemini CLI, or any coding agent:
```shell
npx skills add crewaiinc/skills
```
Pulls from the [skills.sh registry](https://skills.sh/crewaiinc/skills).
</Tab>
</Tabs>
<Steps>
<Step title="Install the official skill pack">
Use either method above — the Claude Code plugin marketplace or `npx skills add`. Both install the official [crewAIInc/skills](https://github.com/crewAIInc/skills) pack.
</Step>
<Step title="Your agent gets instant CrewAI expertise">
The skill pack teaches your agent:
- **Flows** — stateful apps, steps, and crew kickoffs
- **Crews & Agents** — YAML-first patterns, roles, tasks, delegation
- **Tools & Integrations** — search, APIs, MCP servers, and common CrewAI tools
- **Project layout** — CLI scaffolds and repo conventions
- **Up-to-date patterns** — tracks current CrewAI docs and best practices
</Step>
<Step title="Start building">
Your agent can now scaffold and build CrewAI projects without you re-explaining the framework each session.
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Skills concept" icon="bolt" href="/en/concepts/skills">
How skills work in CrewAI agents — injection, activation, and patterns.
</Card>
<Card title="Skills landing page" icon="wand-magic-sparkles" href="/en/skills">
Overview of the crewAIInc/skills pack and what it includes.
</Card>
<Card title="AGENTS.md & coding tools" icon="terminal" href="/en/guides/coding-tools/agents-md">
Set up AGENTS.md for Claude Code, Codex, Cursor, and Gemini CLI.
</Card>
<Card title="Skills registry (skills.sh)" icon="globe" href="https://skills.sh/crewaiinc/skills">
Official listing — skills, install stats, and audits.
</Card>
</CardGroup>
---
## 2. llms.txt — Machine-Readable Docs
CrewAI publishes an `llms.txt` file that gives AI assistants direct access to the full documentation in a machine-readable format.
```
https://docs.crewai.com/llms.txt
```
<Tabs>
<Tab title="What is llms.txt?">
[`llms.txt`](https://llmstxt.org/) is an emerging standard for making documentation consumable by large language models. Instead of scraping HTML, your agent can fetch a single structured text file with all the content it needs.
CrewAI's `llms.txt` is **already live** — your agent can use it right now.
</Tab>
<Tab title="How to use it">
Point your coding agent at the URL when it needs CrewAI reference docs:
```
Fetch https://docs.crewai.com/llms.txt for CrewAI documentation.
```
Many coding agents (Claude Code, Cursor, etc.) can fetch URLs directly. The file contains structured documentation covering all CrewAI concepts, APIs, and guides.
</Tab>
<Tab title="Why it matters">
- **No scraping required** — clean, structured content in one request
- **Always up-to-date** — served directly from docs.crewai.com
- **Optimized for LLMs** — formatted for context windows, not browsers
- **Complements skills** — skills teach patterns, llms.txt provides reference
</Tab>
</Tabs>
---
## 3. Deploy to Enterprise
Go from a local crew to production on **CrewAI AMP** (Agent Management Platform) in minutes.
<Steps>
<Step title="Build locally">
Scaffold and test your crew or flow:
```bash
crewai create crew my_crew
cd my_crew
crewai run
```
</Step>
<Step title="Prepare for deployment">
Ensure your project structure is ready:
```bash
crewai deploy --prepare
```
See the [preparation guide](/en/enterprise/guides/prepare-for-deployment) for details on project structure and requirements.
</Step>
<Step title="Deploy to AMP">
Push to the CrewAI AMP platform:
```bash
crewai deploy
```
You can also deploy via [GitHub integration](/en/enterprise/guides/deploy-to-amp) or [Crew Studio](/en/enterprise/guides/enable-crew-studio).
</Step>
<Step title="Access via API">
Your deployed crew gets a REST API endpoint. Integrate it into any application:
```bash
curl -X POST https://app.crewai.com/api/v1/crews/<crew-id>/kickoff \
-H "Authorization: Bearer $CREWAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"inputs": {"topic": "AI agents"}}'
```
</Step>
</Steps>
<CardGroup cols={2}>
<Card title="Deploy to AMP" icon="rocket" href="/en/enterprise/guides/deploy-to-amp">
Full deployment guide — CLI, GitHub, and Crew Studio methods.
</Card>
<Card title="AMP introduction" icon="globe" href="/en/enterprise/introduction">
Platform overview — what AMP provides for production crews.
</Card>
</CardGroup>
---
## 4. Enterprise Features
CrewAI AMP is built for production teams. Here's what you get beyond deployment.
<CardGroup cols={2}>
<Card title="Observability" icon="chart-line">
Detailed execution traces, logs, and performance metrics for every crew run. Monitor agent decisions, tool calls, and task completion in real time.
</Card>
<Card title="Crew Studio" icon="paintbrush">
No-code/low-code interface to create, customize, and deploy crews visually — then export to code or deploy directly.
</Card>
<Card title="Webhook Streaming" icon="webhook">
Stream real-time events from crew executions to your systems. Integrate with Slack, Zapier, or any webhook consumer.
</Card>
<Card title="Team Management" icon="users">
SSO, RBAC, and organization-level controls. Manage who can create, deploy, and access crews across your team.
</Card>
<Card title="Tool Repository" icon="toolbox">
Publish and share custom tools across your organization. Install community tools from the registry.
</Card>
<Card title="Factory (Self-Hosted)" icon="server">
Run CrewAI AMP on your own infrastructure. Full platform capabilities with data residency and compliance controls.
</Card>
</CardGroup>
<AccordionGroup>
<Accordion title="Who is AMP for?">
AMP is for teams that need to move AI agent workflows from prototypes to production — with observability, access controls, and scalable infrastructure. Whether you're a startup or enterprise, AMP handles the operational complexity so you can focus on building agents.
</Accordion>
<Accordion title="What deployment options are available?">
- **Cloud (app.crewai.com)** — managed by CrewAI, fastest path to production
- **Factory (self-hosted)** — run on your own infrastructure for full data control
- **Hybrid** — mix cloud and self-hosted based on sensitivity requirements
</Accordion>
<Accordion title="How does pricing work?">
Sign up at [app.crewai.com](https://app.crewai.com) to see current plans. Enterprise and Factory pricing is available on request.
</Accordion>
</AccordionGroup>
<Card title="Explore CrewAI AMP →" icon="arrow-right" href="https://app.crewai.com">
Sign up and deploy your first crew to production.
</Card>

View File

@@ -107,9 +107,6 @@ a2a = [
file-processing = [
"crewai-files",
]
signet = [
"signet-auth>=0.5.0",
]
qdrant-edge = [
"qdrant-edge-py>=0.6.0",
]

View File

@@ -1,6 +0,0 @@
"""First-party integrations for CrewAI.
Each subpackage is opt-in and must lazily import any third-party dependencies
so that importing ``crewai.integrations`` has no runtime cost for users who
have not installed the corresponding extra.
"""

View File

@@ -1,83 +0,0 @@
"""Optional Signet integration for CrewAI.
`Signet <https://github.com/Prismer-AI/signet>`_ produces Ed25519-signed,
hash-chained receipts for AI agent tool calls. This integration registers a
:class:`BaseEventListener` that signs a receipt for every governed action
(structured tool, MCP tool execution, A2A delegation) using the paired
``Started``/``Completed`` events emitted by the CrewAI event bus.
The integration is installed as an optional extra::
pip install 'crewai[signet]'
Then enabled with a single call::
from crewai.integrations.signet import install
listener = install(key_name="my-crew-agent")
After installation, every tool call, MCP tool execution, and A2A delegation
produces a signed receipt stored on the returned listener and (optionally)
appended to a local hash-chained audit log by ``signet-auth``.
"""
from __future__ import annotations
from crewai.integrations.signet.config import SignetConfig
from crewai.integrations.signet.listener import Receipt, SignetEventListener
__all__ = ["Receipt", "SignetConfig", "SignetEventListener", "install"]
def install(
key_name: str,
*,
owner: str | None = None,
audit: bool = True,
policy_path: str | None = None,
create_if_missing: bool = True,
tool_events: bool = True,
mcp_events: bool = True,
a2a_events: bool = True,
signing_agent: object | None = None,
) -> SignetEventListener:
"""Install the Signet event listener on the CrewAI event bus.
Args:
key_name: Signet ``SigningAgent`` identity name. If the identity does
not exist yet and ``create_if_missing=True`` (the default), a new
Ed25519 keypair is created and stored under ``~/.signet/keys/``.
owner: Optional owner string used when creating a new identity.
audit: If ``True``, ``signet-auth`` appends every receipt to its local
hash-chained audit log at ``~/.signet/audit/``.
policy_path: Optional path to a Signet policy file that is co-signed
with every receipt.
create_if_missing: If ``True``, create the identity on first use when
no matching key is found. If ``False``, load the existing key only
and raise if it cannot be found.
tool_events: If ``True``, sign structured tool calls
(``tool_usage_started`` / ``tool_usage_finished``).
mcp_events: If ``True``, sign MCP tool executions
(``mcp_tool_execution_started`` / ``mcp_tool_execution_completed``).
a2a_events: If ``True``, sign A2A delegations
(``a2a_delegation_started`` / ``a2a_delegation_completed``).
signing_agent: Optional pre-built ``SigningAgent`` (or a test double
exposing a ``sign(action, params=...)`` method). When provided,
``signet-auth`` is not imported and the extra is not required.
Returns:
The registered :class:`SignetEventListener`. Receipts can be inspected
via ``listener.receipts``.
"""
config = SignetConfig(
key_name=key_name,
owner=owner,
audit=audit,
policy_path=policy_path,
create_if_missing=create_if_missing,
tool_events=tool_events,
mcp_events=mcp_events,
a2a_events=a2a_events,
)
return SignetEventListener(config=config, signing_agent=signing_agent)

View File

@@ -1,34 +0,0 @@
"""Configuration for the optional Signet integration."""
from __future__ import annotations
from pydantic import BaseModel, ConfigDict, Field
class SignetConfig(BaseModel):
"""User-facing configuration for the Signet listener.
Attributes:
key_name: Signet ``SigningAgent`` identity name.
owner: Optional owner string used when creating a new identity.
audit: Whether signet-auth should append receipts to its hash-chained
audit log.
policy_path: Optional path to a Signet policy file that is co-signed
with every receipt.
create_if_missing: Whether to create the identity on first use when no
matching key is found.
tool_events: Whether to sign structured tool calls.
mcp_events: Whether to sign MCP tool executions.
a2a_events: Whether to sign A2A delegations.
"""
model_config = ConfigDict(extra="forbid", frozen=True)
key_name: str = Field(..., min_length=1)
owner: str | None = None
audit: bool = True
policy_path: str | None = None
create_if_missing: bool = True
tool_events: bool = True
mcp_events: bool = True
a2a_events: bool = True

View File

@@ -1,326 +0,0 @@
"""Signet event listener that signs paired CrewAI action events.
The listener subscribes to the paired ``Started``/``Completed`` events emitted
for structured tool calls, MCP tool executions, and A2A delegations. When a
``Completed`` event fires, it correlates the two payloads via the CrewAI event
scope (``event.started_event_id``) and produces a single Ed25519-signed Signet
receipt covering the input and the output.
The ``signet_auth`` dependency is **lazy-imported**. If the ``crewai[signet]``
extra is not installed and no ``signing_agent`` is injected, a clear
:class:`ImportError` is raised the first time a matching event fires. Users
who do not opt in pay no import cost.
"""
from __future__ import annotations
from dataclasses import dataclass
import importlib
import threading
from typing import TYPE_CHECKING, Any, Protocol, TypeVar, runtime_checkable
from crewai.events.base_event_listener import BaseEventListener
from crewai.events.types.a2a_events import (
A2ADelegationCompletedEvent,
A2ADelegationStartedEvent,
)
from crewai.events.types.mcp_events import (
MCPToolExecutionCompletedEvent,
MCPToolExecutionFailedEvent,
MCPToolExecutionStartedEvent,
)
from crewai.events.types.tool_usage_events import (
ToolUsageErrorEvent,
ToolUsageFinishedEvent,
ToolUsageStartedEvent,
)
from crewai.integrations.signet.config import SignetConfig
if TYPE_CHECKING:
from crewai.events.base_events import BaseEvent
from crewai.events.event_bus import CrewAIEventsBus
_StartedEventT = TypeVar("_StartedEventT")
_SIGNET_INSTALL_HINT: str = (
"The Signet integration requires the `signet-auth` package. Install the "
"optional extra with `pip install 'crewai[signet]'` or inject a "
"`signing_agent` with a `.sign(action, params=...)` method."
)
@runtime_checkable
class SigningAgentProtocol(Protocol):
"""Minimal protocol satisfied by ``signet_auth.SigningAgent``.
Any object exposing a ``sign(action, params=...)`` method returning a
receipt (typically a mapping) is accepted. This keeps the listener
decoupled from ``signet-auth`` for testing and for alternative backends.
"""
def sign(
self, action: str, *, params: dict[str, Any]
) -> Any: # pragma: no cover - protocol
...
@dataclass
class Receipt:
"""A signed receipt produced by the listener.
Attributes:
kind: One of ``"tool"``, ``"mcp_tool"``, ``"a2a_delegation"``.
action: Action name used when signing (e.g. the tool name).
payload: Canonical dict passed to the signing agent covering both the
input (from the ``Started`` event) and the output (from the
``Completed`` event).
receipt: The raw object returned by the signing agent.
error: ``True`` if the receipt was produced from an error/failed event.
"""
kind: str
action: str
payload: dict[str, Any]
receipt: Any
error: bool = False
class SignetEventListener(BaseEventListener):
"""Event listener that produces Signet receipts for governed actions.
Args:
config: :class:`SignetConfig` controlling which event surfaces are
signed and how the ``SigningAgent`` is built.
signing_agent: Optional pre-built signing agent. When provided the
``signet_auth`` package is not imported. Must expose
``sign(action, params=...)``.
"""
verbose: bool = False
def __init__(
self,
config: SignetConfig,
*,
signing_agent: Any | None = None,
) -> None:
self.config = config
self._injected_signing_agent = signing_agent
self._signing_agent: Any | None = signing_agent
self._pending: dict[str, BaseEvent] = {}
self._pending_lock = threading.Lock()
self.receipts: list[Receipt] = []
self._receipts_lock = threading.Lock()
super().__init__()
def _get_signing_agent(self) -> Any:
"""Return the active signing agent, lazily building one if needed."""
if self._signing_agent is not None:
return self._signing_agent
try:
signet_auth = importlib.import_module("signet_auth")
except ImportError as exc: # pragma: no cover - exercised via stubbed test
raise ImportError(_SIGNET_INSTALL_HINT) from exc
signing_agent_cls = signet_auth.SigningAgent
kwargs: dict[str, Any] = {}
if self.config.audit:
kwargs["audit"] = True
if self.config.policy_path is not None:
kwargs["policy_path"] = self.config.policy_path
if self.config.create_if_missing and hasattr(signing_agent_cls, "create"):
create_kwargs = dict(kwargs)
if self.config.owner is not None:
create_kwargs["owner"] = self.config.owner
self._signing_agent = signing_agent_cls.create(
self.config.key_name, **create_kwargs
)
else:
self._signing_agent = signing_agent_cls(self.config.key_name, **kwargs)
return self._signing_agent
def setup_listeners(self, crewai_event_bus: CrewAIEventsBus) -> None:
"""Register handlers for each enabled event surface."""
if self.config.tool_events:
self._register_tool_handlers(crewai_event_bus)
if self.config.mcp_events:
self._register_mcp_handlers(crewai_event_bus)
if self.config.a2a_events:
self._register_a2a_handlers(crewai_event_bus)
def _register_tool_handlers(self, bus: CrewAIEventsBus) -> None:
@bus.on(ToolUsageStartedEvent)
def _on_tool_start(source: Any, event: ToolUsageStartedEvent) -> None:
self._remember_start(event)
@bus.on(ToolUsageFinishedEvent)
def _on_tool_finish(source: Any, event: ToolUsageFinishedEvent) -> None:
started = self._consume_start(event.started_event_id, ToolUsageStartedEvent)
if started is None:
return
payload = _tool_payload(started, output=event.output, error=None)
self._sign_and_record("tool", started.tool_name, payload, error=False)
@bus.on(ToolUsageErrorEvent)
def _on_tool_error(source: Any, event: ToolUsageErrorEvent) -> None:
started = self._consume_start(event.started_event_id, ToolUsageStartedEvent)
if started is None:
return
payload = _tool_payload(started, output=None, error=str(event.error))
self._sign_and_record("tool", started.tool_name, payload, error=True)
def _register_mcp_handlers(self, bus: CrewAIEventsBus) -> None:
@bus.on(MCPToolExecutionStartedEvent)
def _on_mcp_start(source: Any, event: MCPToolExecutionStartedEvent) -> None:
self._remember_start(event)
@bus.on(MCPToolExecutionCompletedEvent)
def _on_mcp_complete(
source: Any, event: MCPToolExecutionCompletedEvent
) -> None:
started = self._consume_start(
event.started_event_id, MCPToolExecutionStartedEvent
)
if started is None:
return
payload = _mcp_payload(started, result=event.result, error=None)
self._sign_and_record("mcp_tool", started.tool_name, payload, error=False)
@bus.on(MCPToolExecutionFailedEvent)
def _on_mcp_failed(source: Any, event: MCPToolExecutionFailedEvent) -> None:
started = self._consume_start(
event.started_event_id, MCPToolExecutionStartedEvent
)
if started is None:
return
payload = _mcp_payload(started, result=None, error=event.error)
self._sign_and_record("mcp_tool", started.tool_name, payload, error=True)
def _register_a2a_handlers(self, bus: CrewAIEventsBus) -> None:
@bus.on(A2ADelegationStartedEvent)
def _on_a2a_start(source: Any, event: A2ADelegationStartedEvent) -> None:
self._remember_start(event)
@bus.on(A2ADelegationCompletedEvent)
def _on_a2a_complete(source: Any, event: A2ADelegationCompletedEvent) -> None:
started = self._consume_start(
event.started_event_id, A2ADelegationStartedEvent
)
if started is None:
return
payload = _a2a_payload(started, completed=event)
action = f"a2a:{started.agent_id}"
is_error = event.status.lower() not in {"completed", "ok", "success"}
self._sign_and_record("a2a_delegation", action, payload, error=is_error)
def _remember_start(self, event: BaseEvent) -> None:
with self._pending_lock:
self._pending[event.event_id] = event
def _consume_start(
self,
started_event_id: str | None,
expected_type: type[_StartedEventT],
) -> _StartedEventT | None:
if not started_event_id:
return None
with self._pending_lock:
started = self._pending.pop(started_event_id, None)
if not isinstance(started, expected_type):
return None
return started
def _sign_and_record(
self,
kind: str,
action: str,
payload: dict[str, Any],
*,
error: bool,
) -> None:
agent = self._get_signing_agent()
receipt = agent.sign(action, params=payload)
with self._receipts_lock:
self.receipts.append(
Receipt(
kind=kind,
action=action,
payload=payload,
receipt=receipt,
error=error,
)
)
def _tool_payload(
started: ToolUsageStartedEvent,
*,
output: Any,
error: str | None,
) -> dict[str, Any]:
payload: dict[str, Any] = {
"kind": "tool",
"tool_name": started.tool_name,
"tool_class": started.tool_class,
"tool_args": started.tool_args,
"agent_id": started.agent_id,
"agent_role": started.agent_role,
"task_id": started.task_id,
"started_event_id": started.event_id,
}
if error is None:
payload["output"] = output
else:
payload["error"] = error
return payload
def _mcp_payload(
started: MCPToolExecutionStartedEvent,
*,
result: Any,
error: str | None,
) -> dict[str, Any]:
payload: dict[str, Any] = {
"kind": "mcp_tool",
"tool_name": started.tool_name,
"tool_args": started.tool_args,
"server_name": started.server_name,
"server_url": started.server_url,
"transport_type": started.transport_type,
"agent_id": started.agent_id,
"agent_role": started.agent_role,
"task_id": started.task_id,
"started_event_id": started.event_id,
}
if error is None:
payload["result"] = result
else:
payload["error"] = error
return payload
def _a2a_payload(
started: A2ADelegationStartedEvent,
*,
completed: A2ADelegationCompletedEvent,
) -> dict[str, Any]:
return {
"kind": "a2a_delegation",
"endpoint": started.endpoint,
"task_description": started.task_description,
"a2a_agent_id": started.agent_id,
"a2a_agent_name": started.a2a_agent_name,
"context_id": started.context_id,
"is_multiturn": started.is_multiturn,
"turn_number": started.turn_number,
"skill_id": started.skill_id,
"started_event_id": started.event_id,
"status": completed.status,
"result": completed.result,
"error": completed.error,
}

View File

@@ -32,7 +32,6 @@ from pydantic import (
field_validator,
model_validator,
)
from pydantic.functional_serializers import PlainSerializer
from pydantic_core import PydanticCustomError
from typing_extensions import Self
@@ -87,22 +86,6 @@ from crewai.utilities.printer import PRINTER
from crewai.utilities.string_utils import interpolate_only
def _serialize_model_class(v: type[BaseModel] | None) -> dict[str, Any] | None:
"""Serialize a Pydantic model class reference to its JSON schema."""
return v.model_json_schema() if v else None
def _deserialize_model_class(v: Any) -> type[BaseModel] | None:
"""Hydrate a model class reference from checkpoint data."""
if v is None or isinstance(v, type):
return v
if isinstance(v, dict):
from crewai.utilities.pydantic_schema_utils import create_model_from_schema
return create_model_from_schema(v)
return None
class Task(BaseModel):
"""Class that represents a task to be executed.
@@ -158,33 +141,15 @@ class Task(BaseModel):
description="Whether the task should be executed asynchronously or not.",
default=False,
)
output_json: Annotated[
type[BaseModel] | None,
BeforeValidator(_deserialize_model_class),
PlainSerializer(
_serialize_model_class, return_type=dict | None, when_used="json"
),
] = Field(
output_json: type[BaseModel] | None = Field(
description="A Pydantic model to be used to create a JSON output.",
default=None,
)
output_pydantic: Annotated[
type[BaseModel] | None,
BeforeValidator(_deserialize_model_class),
PlainSerializer(
_serialize_model_class, return_type=dict | None, when_used="json"
),
] = Field(
output_pydantic: type[BaseModel] | None = Field(
description="A Pydantic model to be used to create a Pydantic output.",
default=None,
)
response_model: Annotated[
type[BaseModel] | None,
BeforeValidator(_deserialize_model_class),
PlainSerializer(
_serialize_model_class, return_type=dict | None, when_used="json"
),
] = Field(
response_model: type[BaseModel] | None = Field(
description="A Pydantic model for structured LLM outputs using native provider features.",
default=None,
)
@@ -224,13 +189,7 @@ class Task(BaseModel):
description="Whether the task should instruct the agent to return the final answer formatted in Markdown",
default=False,
)
converter_cls: Annotated[
type[Converter] | None,
BeforeValidator(lambda v: v if v is None or isinstance(v, type) else None),
PlainSerializer(
_serialize_model_class, return_type=dict | None, when_used="json"
),
] = Field(
converter_cls: type[Converter] | None = Field(
description="A converter class used to export structured output",
default=None,
)

View File

@@ -1,418 +0,0 @@
"""Tests for the optional Signet integration.
These tests use a lightweight ``FakeSigningAgent`` that satisfies the same
contract as ``signet_auth.SigningAgent`` so the listener can be exercised
without installing the ``crewai[signet]`` extra.
"""
from __future__ import annotations
import sys
import types
from typing import Any
import pytest
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.a2a_events import (
A2ADelegationCompletedEvent,
A2ADelegationStartedEvent,
)
from crewai.events.types.mcp_events import (
MCPToolExecutionCompletedEvent,
MCPToolExecutionFailedEvent,
MCPToolExecutionStartedEvent,
)
from crewai.events.types.tool_usage_events import (
ToolUsageErrorEvent,
ToolUsageFinishedEvent,
ToolUsageStartedEvent,
)
from crewai.integrations.signet import SignetConfig, SignetEventListener, install
from crewai.integrations.signet.listener import _SIGNET_INSTALL_HINT, Receipt
class FakeSigningAgent:
"""Test double matching the ``signet_auth.SigningAgent`` contract."""
def __init__(self, name: str = "fake") -> None:
self.name = name
self.calls: list[tuple[str, dict[str, Any]]] = []
def sign(self, action: str, *, params: dict[str, Any]) -> dict[str, Any]:
self.calls.append((action, params))
return {
"action": action,
"params": params,
"signature": f"sig-{len(self.calls)}",
"signed_by": self.name,
}
def _install(**kwargs: Any) -> tuple[SignetEventListener, FakeSigningAgent]:
agent = FakeSigningAgent()
listener = install(key_name="test-key", signing_agent=agent, **kwargs)
return listener, agent
def _wait() -> None:
"""Block until all pending event handlers have finished."""
crewai_event_bus.flush(timeout=5.0)
def _emit_tool_pair(
tool_name: str = "some_tool",
*,
with_error: bool = False,
output: Any = "ok",
) -> tuple[ToolUsageStartedEvent, ToolUsageFinishedEvent | ToolUsageErrorEvent]:
started = ToolUsageStartedEvent(
tool_name=tool_name,
tool_args={"x": 1},
tool_class="SomeTool",
agent_id="agent-1",
agent_role="analyst",
)
crewai_event_bus.emit(source=None, event=started)
finished: ToolUsageFinishedEvent | ToolUsageErrorEvent
if with_error:
finished = ToolUsageErrorEvent(
tool_name=tool_name,
tool_args={"x": 1},
tool_class="SomeTool",
agent_id="agent-1",
agent_role="analyst",
error="boom",
started_event_id=started.event_id,
)
else:
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
finished = ToolUsageFinishedEvent(
tool_name=tool_name,
tool_args={"x": 1},
tool_class="SomeTool",
agent_id="agent-1",
agent_role="analyst",
started_at=now,
finished_at=now,
output=output,
started_event_id=started.event_id,
)
crewai_event_bus.emit(source=None, event=finished)
_wait()
return started, finished
def _emit_mcp_pair(
*,
with_error: bool = False,
tool_name: str = "mcp_echo",
) -> tuple[
MCPToolExecutionStartedEvent,
MCPToolExecutionCompletedEvent | MCPToolExecutionFailedEvent,
]:
started = MCPToolExecutionStartedEvent(
server_name="server-a",
server_url="http://localhost:8080",
transport_type="http",
tool_name=tool_name,
tool_args={"q": "hi"},
)
crewai_event_bus.emit(source=None, event=started)
completed: MCPToolExecutionCompletedEvent | MCPToolExecutionFailedEvent
if with_error:
completed = MCPToolExecutionFailedEvent(
server_name="server-a",
tool_name=tool_name,
tool_args={"q": "hi"},
error="server crashed",
started_event_id=started.event_id,
)
else:
completed = MCPToolExecutionCompletedEvent(
server_name="server-a",
tool_name=tool_name,
tool_args={"q": "hi"},
result={"echo": "hi"},
started_event_id=started.event_id,
)
crewai_event_bus.emit(source=None, event=completed)
_wait()
return started, completed
def _emit_a2a_pair(
*,
status: str = "completed",
) -> tuple[A2ADelegationStartedEvent, A2ADelegationCompletedEvent]:
started = A2ADelegationStartedEvent(
endpoint="https://remote/agent",
task_description="summarize",
agent_id="remote-agent-1",
context_id="ctx-1",
)
crewai_event_bus.emit(source=None, event=started)
completed = A2ADelegationCompletedEvent(
status=status,
result="done" if status == "completed" else None,
error=None if status == "completed" else "refused",
context_id="ctx-1",
endpoint="https://remote/agent",
started_event_id=started.event_id,
)
crewai_event_bus.emit(source=None, event=completed)
_wait()
return started, completed
class TestSignetConfig:
def test_defaults(self) -> None:
cfg = SignetConfig(key_name="k")
assert cfg.key_name == "k"
assert cfg.audit is True
assert cfg.create_if_missing is True
assert cfg.tool_events and cfg.mcp_events and cfg.a2a_events
def test_key_name_required(self) -> None:
with pytest.raises(ValueError):
SignetConfig(key_name="")
def test_frozen(self) -> None:
cfg = SignetConfig(key_name="k")
with pytest.raises(Exception):
cfg.key_name = "other" # type: ignore[misc]
class TestToolSigning:
def test_signs_one_receipt_per_tool_call(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, agent = _install()
started, finished = _emit_tool_pair(output={"answer": 42})
assert len(listener.receipts) == 1
rec = listener.receipts[0]
assert isinstance(rec, Receipt)
assert rec.kind == "tool"
assert rec.action == "some_tool"
assert rec.error is False
assert rec.payload["tool_name"] == "some_tool"
assert rec.payload["tool_args"] == {"x": 1}
assert rec.payload["output"] == {"answer": 42}
assert rec.payload["started_event_id"] == started.event_id
assert rec.payload["agent_id"] == "agent-1"
assert rec.receipt["signature"] == "sig-1"
assert agent.calls[0][0] == "some_tool"
def test_signs_error_event(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install()
_emit_tool_pair(with_error=True)
assert len(listener.receipts) == 1
rec = listener.receipts[0]
assert rec.error is True
assert rec.payload["error"] == "boom"
assert "output" not in rec.payload
def test_finished_without_started_is_skipped(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install()
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
orphan = ToolUsageFinishedEvent(
tool_name="orphan",
tool_args={},
started_at=now,
finished_at=now,
output="x",
started_event_id="does-not-exist",
)
crewai_event_bus.emit(source=None, event=orphan)
assert listener.receipts == []
def test_pairs_are_independent_across_calls(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install()
_emit_tool_pair(tool_name="t1", output="a")
_emit_tool_pair(tool_name="t2", output="b")
actions = [r.action for r in listener.receipts]
outputs = [r.payload["output"] for r in listener.receipts]
assert actions == ["t1", "t2"]
assert outputs == ["a", "b"]
class TestMCPSigning:
def test_signs_mcp_tool_execution(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install()
_emit_mcp_pair()
assert len(listener.receipts) == 1
rec = listener.receipts[0]
assert rec.kind == "mcp_tool"
assert rec.action == "mcp_echo"
assert rec.payload["server_name"] == "server-a"
assert rec.payload["result"] == {"echo": "hi"}
assert rec.error is False
def test_signs_mcp_failure(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install()
_emit_mcp_pair(with_error=True)
assert len(listener.receipts) == 1
rec = listener.receipts[0]
assert rec.error is True
assert rec.payload["error"] == "server crashed"
class TestA2ASigning:
def test_signs_successful_a2a_delegation(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install()
_emit_a2a_pair(status="completed")
assert len(listener.receipts) == 1
rec = listener.receipts[0]
assert rec.kind == "a2a_delegation"
assert rec.action == "a2a:remote-agent-1"
assert rec.payload["status"] == "completed"
assert rec.payload["endpoint"] == "https://remote/agent"
assert rec.error is False
def test_flags_failed_a2a_delegation_as_error(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install()
_emit_a2a_pair(status="failed")
assert len(listener.receipts) == 1
assert listener.receipts[0].error is True
assert listener.receipts[0].payload["status"] == "failed"
class TestSurfaceToggles:
def test_disabling_tool_events_skips_tool_signing(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install(tool_events=False)
_emit_tool_pair()
_emit_mcp_pair()
assert len(listener.receipts) == 1
assert listener.receipts[0].kind == "mcp_tool"
def test_disabling_mcp_events_skips_mcp_signing(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install(mcp_events=False)
_emit_tool_pair()
_emit_mcp_pair()
assert len(listener.receipts) == 1
assert listener.receipts[0].kind == "tool"
def test_disabling_a2a_events_skips_a2a_signing(self) -> None:
with crewai_event_bus.scoped_handlers():
listener, _ = _install(a2a_events=False)
_emit_a2a_pair()
assert listener.receipts == []
class TestLazyImport:
def test_missing_signet_auth_raises_clear_import_error(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""When no signing_agent is injected and signet_auth isn't installed,
``_get_signing_agent`` must raise a clear ImportError — not at import
or registration time.
"""
monkeypatch.setitem(sys.modules, "signet_auth", None)
with crewai_event_bus.scoped_handlers():
listener = install(key_name="real-key")
assert listener.receipts == []
with pytest.raises(ImportError, match="signet-auth"):
listener._get_signing_agent()
def test_builds_signing_agent_from_fake_signet_auth(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""If signet_auth is importable, SigningAgent.create is used with the
config-derived kwargs."""
captured: dict[str, Any] = {}
class _SigningAgent:
def __init__(self, name: str, **kwargs: Any) -> None:
self.name = name
self.kwargs = kwargs
@classmethod
def create(cls, name: str, **kwargs: Any) -> "_SigningAgent":
captured["create"] = (name, kwargs)
return cls(name, **kwargs)
def sign(self, action: str, *, params: dict[str, Any]) -> dict[str, Any]:
return {"action": action, "params": params, "by": self.name}
fake_module = types.ModuleType("signet_auth")
fake_module.SigningAgent = _SigningAgent # type: ignore[attr-defined]
monkeypatch.setitem(sys.modules, "signet_auth", fake_module)
with crewai_event_bus.scoped_handlers():
listener = install(
key_name="team-agent",
owner="alice",
audit=True,
policy_path="/tmp/policy.yaml",
)
agent = listener._get_signing_agent()
assert captured["create"][0] == "team-agent"
assert captured["create"][1] == {
"audit": True,
"policy_path": "/tmp/policy.yaml",
"owner": "alice",
}
assert isinstance(agent, _SigningAgent)
# Subsequent calls must reuse the same instance.
assert listener._get_signing_agent() is agent
def test_loads_existing_identity_when_create_if_missing_false(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""When ``create_if_missing=False`` the listener instantiates
``SigningAgent`` directly instead of calling ``create``."""
init_args: dict[str, Any] = {}
class _SigningAgent:
def __init__(self, name: str, **kwargs: Any) -> None:
init_args["name"] = name
init_args["kwargs"] = kwargs
def sign(self, action: str, *, params: dict[str, Any]) -> dict[str, Any]:
return {"action": action, "params": params}
fake_module = types.ModuleType("signet_auth")
fake_module.SigningAgent = _SigningAgent # type: ignore[attr-defined]
monkeypatch.setitem(sys.modules, "signet_auth", fake_module)
with crewai_event_bus.scoped_handlers():
listener = install(
key_name="existing-agent",
create_if_missing=False,
audit=False,
)
listener._get_signing_agent()
assert init_args["name"] == "existing-agent"
assert init_args["kwargs"] == {}
def test_hint_message_mentions_extra(self) -> None:
assert "crewai[signet]" in _SIGNET_INSTALL_HINT

View File

@@ -0,0 +1,57 @@
"""
Test script for LinearTool — runs against the real Linear API.
Usage:
LINEAR_API_KEY=lin_api_xxxxxxxxxxxx python tests/tools/test_linear_tool.py
Set LINEAR_API_KEY to your actual Personal API key from:
https://linear.app/settings/api (Profile → API → Personal API keys)
"""
import json
import os
import sys
from crewai.tools.linear_tool import LinearAction, LinearTool
def pretty(data: object) -> str:
return json.dumps(data, indent=2, default=str)
def run_test(tool: LinearTool, label: str, action: LinearAction, first: int = 5) -> None:
print(f"\n{'' * 60}")
print(f" {label}")
print(f"{'' * 60}")
try:
result = tool._run(action=action, first=first)
if not result:
print(" (no records returned)")
else:
print(pretty(result))
except Exception as exc:
print(f" ERROR: {exc}", file=sys.stderr)
def main() -> None:
if not os.environ.get("LINEAR_API_KEY"):
print(
"ERROR: Set LINEAR_API_KEY before running.\n"
" export LINEAR_API_KEY=lin_api_xxxxxxxxxxxx",
file=sys.stderr,
)
sys.exit(1)
tool = LinearTool()
run_test(tool, "My assigned issues (up to 5)", LinearAction.MY_ISSUES, first=5)
run_test(tool, "Teams (up to 10)", LinearAction.LIST_TEAMS, first=10)
run_test(tool, "Projects (up to 10)", LinearAction.LIST_PROJECTS, first=10)
print(f"\n{'' * 60}")
print(" All tests complete.")
print(f"{'' * 60}\n")
if __name__ == "__main__":
main()

20
uv.lock generated
View File

@@ -13,7 +13,7 @@ resolution-markers = [
]
[options]
exclude-newer = "2026-04-18T00:00:00Z"
exclude-newer = "2026-04-18T07:00:00Z"
[manifest]
members = [
@@ -1330,9 +1330,6 @@ qdrant = [
qdrant-edge = [
{ name = "qdrant-edge-py" },
]
signet = [
{ name = "signet-auth" },
]
tools = [
{ name = "crewai-tools" },
]
@@ -1390,7 +1387,6 @@ requires-dist = [
{ name = "qdrant-client", extras = ["fastembed"], marker = "extra == 'qdrant'", specifier = "~=1.14.3" },
{ name = "qdrant-edge-py", marker = "extra == 'qdrant-edge'", specifier = ">=0.6.0" },
{ name = "regex", specifier = "~=2026.1.15" },
{ name = "signet-auth", marker = "extra == 'signet'", specifier = ">=0.5.0" },
{ name = "textual", specifier = ">=7.5.0" },
{ name = "tiktoken", marker = "extra == 'embeddings'", specifier = "~=0.8.0" },
{ name = "tokenizers", specifier = ">=0.21,<1" },
@@ -1399,7 +1395,7 @@ requires-dist = [
{ name = "uv", specifier = "~=0.11.6" },
{ name = "voyageai", marker = "extra == 'voyageai'", specifier = "~=0.3.5" },
]
provides-extras = ["a2a", "anthropic", "aws", "azure-ai-inference", "bedrock", "docling", "embeddings", "file-processing", "google-genai", "litellm", "mem0", "openpyxl", "pandas", "qdrant", "qdrant-edge", "signet", "tools", "voyageai", "watson"]
provides-extras = ["a2a", "anthropic", "aws", "azure-ai-inference", "bedrock", "docling", "embeddings", "file-processing", "google-genai", "litellm", "mem0", "openpyxl", "pandas", "qdrant", "qdrant-edge", "tools", "voyageai", "watson"]
[[package]]
name = "crewai-devtools"
@@ -8099,18 +8095,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
]
[[package]]
name = "signet-auth"
version = "0.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b8/09/68036ddb2d00985d1081f205e2480368ed28294a864bc87e44796ec6685b/signet_auth-0.9.0.tar.gz", hash = "sha256:c650db7d16236448234a2d356245e19bdb5b48fe78cc3436311db8b002867885", size = 88768, upload-time = "2026-04-13T09:33:56.925Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3a/8d/800651df03709729ae52c89310c71eaf3aea4efadb183e67a9d7f002cba6/signet_auth-0.9.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:013152b26415ceb89cf2969e69a03aa254328f2d7f656a080842c967e2c7d1e1", size = 1730524, upload-time = "2026-04-13T09:33:49.261Z" },
{ url = "https://files.pythonhosted.org/packages/78/61/59f87d18c76f9dff467b62a48afade139460c090f91ad13c56cb52dd2b46/signet_auth-0.9.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:4c3ce87dc6265993ddc4f56d6ef5ad867b66d74cc8713c23f1fe47d6317b1d55", size = 1614461, upload-time = "2026-04-13T09:33:51.016Z" },
{ url = "https://files.pythonhosted.org/packages/39/a4/b0d887fda7e25647629be600cd38e8f1fc129f6bc4c5bdefdac61531a915/signet_auth-0.9.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2950761b4199d764902520749c60d668ae7ade91de8347e89253a94f102c94d", size = 1802423, upload-time = "2026-04-13T09:33:53.338Z" },
{ url = "https://files.pythonhosted.org/packages/e4/60/71f8884256523a57646fd0e216b7a267cae69e4751f4bf0e5703bd9b1d5f/signet_auth-0.9.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2139507c8c0d44b74fa6d73f1812446223c1fa381f0d2e811a9a411302378deb", size = 1859359, upload-time = "2026-04-13T09:33:55.506Z" },
]
[[package]]
name = "singlestoredb"
version = "1.16.9"