> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracepilotai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add TracePilot AI span tracing to your CrewAI agents

> Trace CrewAI agent tool calls with TracePilot by wrapping each tool's execute method — and flag destructive operations before they cause problems.

CrewAI agents do their work through tools. TracePilot traces those tool invocations using `tp.wrapToolCall`, which captures the tool name, inputs, outputs, and latency for every call your agents make. Wrap at the tool level, not the agent level — this gives you precise visibility into which tool ran, what it received, and what it returned.

## How the pattern works

Each CrewAI tool has an `_run` method where the actual work happens. You wrap the underlying call inside `_run` with `tp.wrapToolCall`. The method still returns its result unchanged; TracePilot captures the span in the background and links it to the parent trace via `parentSpanId`.

<Note>
  Wrap at the tool level, not the agent level. CrewAI agents orchestrate multiple tools — if you only wrap the agent, you lose per-tool visibility.
</Note>

## Setup

<Steps>
  <Step title="Install dependencies">
    ```bash theme={null}
    npm install tracepilot-sdk crewai openai
    ```
  </Step>

  <Step title="Initialize TracePilot">
    Create a shared `tp` instance your tools can import.

    ```typescript theme={null}
    // lib/tracer.ts
    import { TracePilot } from 'tracepilot-sdk';

    export const tp = new TracePilot(process.env.TRACEPILOT_API_KEY!);
    ```
  </Step>

  <Step title="Wrap your tool's _run method">
    Pass the trace's root `spanId` as `parentSpanId` so the tool span appears nested under the agent trace in the dashboard.

    ```typescript theme={null}
    import { Tool } from 'crewai';
    import { tp } from '../lib/tracer';

    export class WebSearchTool extends Tool {
      name = 'web-search';
      description = 'Search the web for up-to-date information on a topic.';

      constructor(private parentSpanId: string) {
        super();
      }

      async _run(query: string): Promise<string> {
        const { result } = await tp.wrapToolCall(
          'web-search',
          () => fetchSearchResults(query), // your actual search implementation
          this.parentSpanId,
          1, // step order within this agent's execution
        );

        return JSON.stringify(result);
      }
    }

    async function fetchSearchResults(query: string): Promise<{ results: string[] }> {
      // Replace with your real search client
      return { results: [`Result for: ${query}`] };
    }
    ```
  </Step>

  <Step title="Mark destructive tools">
    Tools that write data — sending emails, updating records, posting messages — should set `isDestructive: true`. This adds a warning badge in the dashboard so you can review these calls before forking and re-running an execution.

    ```typescript theme={null}
    export class SendEmailTool extends Tool {
      name = 'send-email';
      description = 'Send an email to a customer.';

      constructor(private parentSpanId: string) {
        super();
      }

      async _run(input: string): Promise<string> {
        const { to, subject, body } = JSON.parse(input);

        const { result } = await tp.wrapToolCall(
          'send-email',
          () => emailClient.send({ to, subject, body }),
          this.parentSpanId,
          2,
          true, // isDestructive — marks this span with a ⚠ badge in the dashboard
        );

        return `Email sent to ${to}`;
      }
    }
    ```
  </Step>
</Steps>

## Full example: CrewAI agent with traced tools

This example shows a CrewAI research agent that uses two traced tools — a read-only web search and a destructive database write.

```typescript theme={null}
import { Agent, Task, Crew } from 'crewai';
import { tp } from './lib/tracer';
import { WebSearchTool } from './tools/web-search';
import { SaveResultTool } from './tools/save-result';

async function runResearchCrew(topic: string) {
  await tp.startTrace('research-crew');

  // Start a root span to group all tool calls under one trace
  const openai = new (await import('openai')).default({
    apiKey: process.env.OPENAI_API_KEY,
  });

  const messages = [{ role: 'user' as const, content: `Research this topic: ${topic}` }];

  const { result: plan, spanId: rootSpanId } = await tp.wrapOpenAI(
    () =>
      openai.chat.completions.create({
        model: 'gpt-4o-mini',
        messages,
      }),
    messages,
    undefined,
    1,
  );

  // Pass rootSpanId to tools so their spans appear nested in the dashboard
  const searchTool = new WebSearchTool(rootSpanId);
  const saveTool = new SaveResultTool(rootSpanId);

  const researcher = new Agent({
    role: 'Research Analyst',
    goal: 'Find accurate, up-to-date information on any topic.',
    backstory: 'You are an expert at synthesizing information from multiple sources.',
    tools: [searchTool, saveTool],
  });

  const task = new Task({
    description: `Research "${topic}" and save a summary to the database.`,
    agent: researcher,
  });

  const crew = new Crew({
    agents: [researcher],
    tasks: [task],
  });

  return crew.kickoff();
}

runResearchCrew('autonomous AI agents in production');
```

## Tracing multiple agents

When your crew has more than one agent, start a trace once and pass the root `spanId` to each agent's tools. All tool spans will appear under the same trace in the dashboard, grouped by agent.

```typescript theme={null}
async function runMultiAgentCrew(brief: string) {
  await tp.startTrace('multi-agent-crew');

  const openai = new (await import('openai')).default({
    apiKey: process.env.OPENAI_API_KEY,
  });

  const messages = [{ role: 'user' as const, content: brief }];

  // One root span for the entire crew run
  const { spanId: crewSpanId } = await tp.wrapOpenAI(
    () => openai.chat.completions.create({ model: 'gpt-4o-mini', messages }),
    messages,
    undefined,
    1,
  );

  // Each agent's tools get the shared crewSpanId as parent
  const researcherTools = [new WebSearchTool(crewSpanId)];
  const writerTools = [new SaveResultTool(crewSpanId)];

  // ... build and run your crew
}
```

<Tip>
  Use `isDestructive: true` for any tool that modifies external state — database writes, API calls that send messages, file deletions. This prevents accidental side effects when you fork and re-run a failing execution from the dashboard.
</Tip>
