mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-21 23:05:10 +00:00
Compare commits
2 Commits
devin/1776
...
feature/li
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75ef1ecf91 | ||
|
|
7961678879 |
17
README.md
17
README.md
@@ -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
124
crewai/tools/linear_tool.py
Normal 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 (1–250).",
|
||||
)
|
||||
|
||||
|
||||
_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"])
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -107,9 +107,6 @@ a2a = [
|
||||
file-processing = [
|
||||
"crewai-files",
|
||||
]
|
||||
signet = [
|
||||
"signet-auth>=0.5.0",
|
||||
]
|
||||
qdrant-edge = [
|
||||
"qdrant-edge-py>=0.6.0",
|
||||
]
|
||||
|
||||
@@ -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.
|
||||
"""
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
57
tests/tools/test_linear_tool.py
Normal file
57
tests/tools/test_linear_tool.py
Normal 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
20
uv.lock
generated
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user