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

# Claude Managed Agents (Beta)

> Automatic tracking for Claude Managed Agents. Wrap the Anthropic client once to capture events, traces, and tool call spans from managed agent sessions.

The `@raindrop-ai/claude-managed-agents` package automatically instruments
[Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview) sessions to capture events and traces.
Wrap the Anthropic client once, then use it normally — session creation, event streaming, and tool calls are tracked automatically.

**Features:**

* No OpenTelemetry setup required
* Automatic tool call tracing (bash, file operations, web search, grep, MCP tools, custom tools)
* Token usage tracking with cache creation/read breakdown
* Per-session context overrides (userId, convoId, properties)

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @raindrop-ai/claude-managed-agents @anthropic-ai/sdk
  ```

  ```bash yarn theme={null}
  yarn add @raindrop-ai/claude-managed-agents @anthropic-ai/sdk
  ```

  ```bash pnpm theme={null}
  pnpm add @raindrop-ai/claude-managed-agents @anthropic-ai/sdk
  ```

  ```bash bun theme={null}
  bun add @raindrop-ai/claude-managed-agents @anthropic-ai/sdk
  ```
</CodeGroup>

## Quick Start

```typescript theme={null}
import Anthropic from "@anthropic-ai/sdk";
import { createRaindropClaudeManagedAgents } from "@raindrop-ai/claude-managed-agents";

// 1. Create the Raindrop client
const raindrop = createRaindropClaudeManagedAgents({
  writeKey: process.env.RAINDROP_WRITE_KEY,  // Optional
  userId: "user_123",
});

// 2. Wrap the Anthropic SDK
const wrapped = raindrop.wrap(new Anthropic());

// 3. Use the SDK normally — sessions are tracked automatically
const session = await wrapped.beta.sessions.create({
  agent: agentId,
  environment_id: envId,
  title: "My session",
});

const stream = await wrapped.beta.sessions.events.stream(session.id);

await wrapped.beta.sessions.events.send(session.id, {
  events: [
    {
      type: "user.message",
      content: [{ type: "text", text: "List files in the current directory" }],
    },
  ],
});

for await (const event of stream) {
  if (event.type === "agent.message") {
    for (const block of event.content) {
      process.stdout.write(block.text);
    }
  } else if (event.type === "session.status_idle") {
    break;
  }
}

// 4. Flush before shutdown
await raindrop.shutdown();
```

The wrapped client is a transparent proxy — all methods work identically to the original Anthropic client. Session creation, event streaming, and tool calls are instrumented automatically.

***

## Configuration

### Client Options

```typescript theme={null}
const raindrop = createRaindropClaudeManagedAgents({
  writeKey: "your-write-key",   // Optional — omitting disables telemetry
  userId: "user_123",           // Default user ID for all sessions
  convoId: "conv_456",          // Default conversation ID
  projectId: "support-prod",    // Optional: route events to a specific project (slug)
  eventName: "managed_agent_session",  // Default event name

  traces: {
    enabled: true,              // Default: true
    debug: false,               // Logs trace shipping
  },

  events: {
    enabled: true,              // Default: true
    debug: false,               // Logs event shipping
  },
});
```

### Projects

Route events to a specific [project](/platform/projects) by passing its slug as `projectId`:

```typescript theme={null}
const raindrop = createRaindropClaudeManagedAgents({
  writeKey: "your-write-key",
  projectId: "support-prod",
});
```

This sets the `X-Raindrop-Project-Id` header on every event. Omit it (or pass `"default"`) to use your org's default **Production** project, which is the existing behavior. Single-project orgs need nothing new.

### Per-Session Context

Override defaults for specific sessions using the second argument to `wrap()`:

```typescript theme={null}
const wrapped = raindrop.wrap(client, {
  userId: "different_user",
  convoId: "specific_conversation",
  properties: { environment: "production", tier: "enterprise" },
});
```

***

## Identifying Users

Use `users.identify` to associate traits with a user:

```typescript theme={null}
await raindrop.users.identify({
  userId: "user_123",
  traits: {
    email: "user@example.com",
    plan: "pro",
    orgId: "org_456",
  },
});
```

***

## Signals (Feedback)

Track user feedback on agent sessions:

```typescript theme={null}
await raindrop.signals.track({
  name: "thumbs_up",
  type: "feedback",
  sentiment: "POSITIVE",
  comment: "Agent handled the task well",
});

// Thumbs down with comment
await raindrop.signals.track({
  name: "thumbs_down",
  type: "feedback",
  sentiment: "NEGATIVE",
  comment: "Agent used the wrong tool",
});

// User edit
await raindrop.signals.track({
  name: "user_edit",
  type: "edit",
  after: "The corrected response text",
});
```

### Signal Types

| Type         | Use Case                         |
| ------------ | -------------------------------- |
| `"default"`  | Generic signals (thumbs up/down) |
| `"feedback"` | User comments about quality      |
| `"edit"`     | User corrected the output        |

***

## Manual Event Updates

Update events after they're created:

```typescript theme={null}
// Add properties
await raindrop.events.setProperties(eventId, {
  latencyMs: 1234,
  cached: true,
});

// Add attachments
await raindrop.events.addAttachments(eventId, [
  {
    type: "code",
    name: "generated.py",
    value: "print('hello')",
    role: "output",
    language: "python",
  },
]);

// Mark as complete (triggers immediate shipping)
await raindrop.events.finish(eventId);
```

<Info>
  Events are automatically finalized when the session stream completes. Use `finish()` only when you need to force immediate shipping.
</Info>

***

## Flush & Shutdown

Always flush before your process exits to ensure all data is sent:

```typescript theme={null}
// Serverless: flush at end of request
await raindrop.flush();

// Long-running: shutdown gracefully
process.on("SIGTERM", async () => {
  await raindrop.shutdown();
  process.exit(0);
});
```

***

## What Gets Captured

Each managed agent session produces:

* **One Raindrop event** with input (user message), output (agent response), model, and token counts
* **A root trace span** (`managed_agent.session`) with the session ID
* **Child trace spans** for each tool call (`managed_agent.toolCall`, `managed_agent.mcpToolCall`, `managed_agent.customToolCall`)
* **Token usage** aggregated across all model requests, including cache creation/read tokens
* **Session errors** captured in event properties

***

## Debugging

Enable debug logging to troubleshoot issues:

```typescript theme={null}
const raindrop = createRaindropClaudeManagedAgents({
  writeKey: process.env.RAINDROP_WRITE_KEY,
  events: { debug: true },
  traces: { debug: true },
});
```

Or via environment variable:

```bash theme={null}
RAINDROP_AI_DEBUG=1 node your-script.js
```

***

## Troubleshooting

### Events not appearing in dashboard

1. **Check your write key** — Ensure `RAINDROP_WRITE_KEY` is set correctly
2. **Call shutdown before exit** — `await raindrop.shutdown()` flushes all pending data
3. **Enable debug logging** — Set `events: { debug: true }` to see what's being shipped

### Traces or tool calls missing

1. **Enable trace debugging** — Set `traces: { debug: true }` to see span shipping
2. **Check the stream was consumed** — Tool call spans are only created when events are iterated

### Wrapper not capturing sessions

Ensure you call `sessions.create()` on the **wrapped** client, not the original. Only sessions created through the wrapped client are tracked.

***

## Self Diagnostics (Optional)

Self Diagnostics lets your agent proactively report its own issues -- capability gaps, missing context, persistent tool failures -- back to your team as Raindrop signals.

A custom tool (`__raindrop_report`) is automatically injected into the agent definition. When the agent calls it, the wrapper ships a signal, auto-responds, and filters the tool call from the consumer stream. The agent never mentions the tool to the user.

```typescript theme={null}
const raindrop = createRaindropClaudeManagedAgents({
  writeKey: process.env.RAINDROP_WRITE_KEY,
  userId: "user_123",
  selfDiagnostics: {
    // Default signals (used when `signals` is omitted):
    signals: {
      missing_context: {
        description: "Critical information or access is missing and the user cannot provide it.",
        sentiment: "NEGATIVE",
      },
      repeatedly_broken_tool: {
        description: "A tool has failed on multiple attempts, preventing task completion.",
        sentiment: "NEGATIVE",
      },
      capability_gap: {
        description: "The task requires a capability you do not have.",
        sentiment: "NEGATIVE",
      },
      complete_task_failure: {
        description: "You tried and failed to deliver what the user asked.",
        sentiment: "NEGATIVE",
      },
    },
  },
});

// IMPORTANT: create the agent through the wrapped client so the tool is injected
const wrapped = raindrop.wrap(new Anthropic());
const agent = await wrapped.beta.agents.create({
  name: "My Agent",
  model: "claude-sonnet-4-6",
  tools: [{ type: "agent_toolset_20260401" }],
});
```

When invoked, the signal appears in Raindrop with the category and detail:

```json theme={null}
{
  "signal_name": "self diagnostics - missing_context",
  "signal_type": "agent",
  "properties": {
    "category": "missing_context",
    "detail": "User asked to fix deployment but no access to logs."
  }
}
```

<Warning>
  The agent must be created through the **wrapped** client (`wrapped.beta.agents.create()`), not the original. Otherwise the reporting tool won't be injected.
</Warning>
