> ## 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 Agent SDK (Beta)

> Automatic tracking for the Claude Agent SDK. Wrap once, then pass eventMetadata() on query() calls to capture events and traces across the session.

The `@raindrop-ai/claude-agent-sdk` package automatically instruments the
[Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview) to capture events and traces from wrapped `query` calls.
Add `eventMetadata()` (with a `userId`) to the specific queries you want tracked in Raindrop.

**Features:**

* No OpenTelemetry setup required
* Automatic tool call tracing
* Automatic conversation tracking using Claude Agent SDK's `session_id`
* Subagent span hierarchies for Task tool invocations
* Wrap once, then optionally pass `eventMetadata()` to each `query()` call you want Raindrop to track

## Installation

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

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

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

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

## Quick Start

```typescript theme={null}
import * as claudeAgentSDK from "@anthropic-ai/claude-agent-sdk";
import { createRaindropClaudeAgentSDK, eventMetadata } from "@raindrop-ai/claude-agent-sdk";

// 1. Create the Raindrop client
const raindrop = createRaindropClaudeAgentSDK({
  writeKey: process.env.RAINDROP_WRITE_KEY!,
});

// 2. Wrap the Claude Agent SDK
const { query, createSdkMcpServer, ReadTool, EditTool } = raindrop.wrap(claudeAgentSDK);

// 3. Add eventMetadata() on queries you want tracked
for await (const message of query(
  {
    prompt: "List files in the current directory",
    options: {
      allowedTools: ["Bash"],
      model: "claude-sonnet-4.5",
    },
  },
  eventMetadata({
    userId: "user_123",
    eventName: "agent_query",
  })
)) {
  console.log(message);
}

// Optional: identify user traits
await raindrop.users.identify({
  userId: "user_123",
  traits: {
    plan: "pro",
    email: "user@example.com",
  },
});

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

That's it. Calls with `eventMetadata()` are tracked with events and traces. Calls without it run normally and are not tracked in Raindrop.

***

## Configuration

### Client Options

```typescript theme={null}
const raindrop = createRaindropClaudeAgentSDK({
  writeKey: "your-write-key",        // Required
  endpoint: "https://api.raindrop.ai/v1",  // Optional, defaults to production
  projectId: "support-prod",         // Optional: route events to a specific project (slug)

  traces: {
    enabled: true,                   // Default: true
    flushIntervalMs: 5000,           // Default: 5000
    maxBatchSize: 100,               // Default: 100
    maxQueueSize: 1000,              // Default: 1000
    debug: false,                    // Logs trace shipping
    debugSpans: false,               // Logs span parent relationships
  },

  events: {
    enabled: true,                   // Default: true
    partialFlushMs: 1000,            // Debounce for streaming updates
    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 = createRaindropClaudeAgentSDK({
  writeKey: "your-write-key",
  projectId: "support-prod",
});
```

This sets the `X-Raindrop-Project-Id` header on every outbound 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.

### Self Diagnostics (Optional)

Self Diagnostics lets your agent **proactively report its own issues** — capability gaps, missing context, persistent tool failures — back to your team. It's most valuable when an agent's self-reflection provides signal you can't easily get otherwise.

The tool is delivered as an in-process MCP server, so it works alongside any other MCP servers you configure.
The tool name defaults to `__raindrop_report`, and can be overridden with `selfDiagnostics.toolName`.
The SDK automatically prompt-engineers the tool description from your `signals` definitions; `selfDiagnostics.guidance` is optional extra guidance.

```typescript theme={null}
import * as claudeAgentSDK from "@anthropic-ai/claude-agent-sdk";
import { createRaindropClaudeAgentSDK, eventMetadata } from "@raindrop-ai/claude-agent-sdk";

const raindrop = createRaindropClaudeAgentSDK({
  writeKey: process.env.RAINDROP_WRITE_KEY!,
});

const wrapped = raindrop.wrap(claudeAgentSDK, {
  selfDiagnostics: {
    // Default signals (used when `signals` is omitted):
    signals: {
      missing_context: {
        description:
          "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. " +
          "Do NOT report this for normal clarifying questions — only when you are blocked.",
        sentiment: "NEGATIVE",
      },
      repeatedly_broken_tool: {
        description:
          "A tool has failed or not returned the expected response on multiple distinct attempts in this conversation, preventing task completion. " +
          "A single tool error is NOT enough — the tool must be persistently broken or aberrantly behaving across retries.",
        sentiment: "NEGATIVE",
      },
      capability_gap: {
        description:
          "The task requires a tool, permission, or capability that you do not have. " +
          "For example, the user asks you to perform an action but no suitable tool exists, or you lack the necessary access. " +
          "Do NOT report this if you simply need more information from the user — only when the gap is in your own capabilities.",
        sentiment: "NEGATIVE",
      },
      complete_task_failure: {
        description:
          "You were unable to accomplish what the user asked despite making genuine attempts. This might be things like, you genuinely do not have the capabilities the user is asking for. You have tried but run into a persistent bug in the environment etc. " +
          "This is NOT a refusal or policy block — you tried and failed to deliver the result.",
        sentiment: "NEGATIVE",
      },
    },
  },
});
```

When invoked, the injected tool tracks a signal on the same `eventId`:

```json theme={null}
{
  "event_id": "evt_...",
  "signal_name": "self diagnostics - missing_context",
  "signal_type": "agent",
  "properties": {
    "source": "agent_reporting_tool",
    "category": "missing_context",
    "signal_description": "You cannot complete the task because critical information, credentials, or access is missing and the user cannot provide it. Do NOT report this for normal clarifying questions — only when you are blocked.",
    "detail": "User asked to fix deployment but no access to logs or SSH credentials."
  }
}
```

The self-diagnostics tool is invisible to your integration: it is auto-approved in the PreToolUse hook and its
tool\_use/tool\_result blocks are stripped from the SDK stream, so you never see it and don't need to handle it.

***

## Per-Query Context

Use `eventMetadata()` as the second argument when you want a query tracked. `userId` is required:

```typescript theme={null}
import { eventMetadata } from "@raindrop-ai/claude-agent-sdk";

const wrapped = raindrop.wrap(claudeAgentSDK);

for await (const msg of wrapped.query(
  { prompt: "Hello", options: { model: "claude-opus-4-6" } },
  eventMetadata({
    userId: "user_123",
    convoId: "conv-123",
    eventName: "agent_query",
    properties: { tier: "pro" },
  })
)) {
  console.log(msg);
}
```

You can also set `convoId`, `eventName`, `properties`, or `eventId` per query.

**Defaults:**

* If `eventMetadata()` is omitted, the query runs normally but no events or traces are captured by Raindrop
* `eventName` defaults to `"agent_query"`
* `convoId` is auto-captured from Claude's `session_id` if not provided
* `eventId` is auto-generated if not provided

***

## 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 AI responses using the same `eventId`:

```typescript theme={null}
import { eventMetadata } from "@raindrop-ai/claude-agent-sdk";

const eventId = crypto.randomUUID();
const wrapped = raindrop.wrap(claudeAgentSDK);

for await (const msg of wrapped.query(
  { prompt: userMessage, options: { model: "claude-sonnet-4.5" } },
  eventMetadata({ userId: "user_123", eventId })
)) {
  console.log(msg);
}

// Later, when user gives feedback
await raindrop.signals.track({
  eventId,
  name: "thumbs_down",
  comment: "Answer was incorrect",
});
```

### Signal Types

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

```typescript theme={null}
// Thumbs up
await raindrop.signals.track({
  eventId,
  name: "thumbs_up",
  sentiment: "POSITIVE",
});

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

***

## Manual Event Updates

Update events after they're created (use `eventId` from `eventMetadata`):

```typescript theme={null}
const eventId = crypto.randomUUID();
const wrapped = raindrop.wrap(claudeAgentSDK);

for await (const msg of wrapped.query(
  { prompt, options },
  eventMetadata({ userId: "user_123", eventId })
)) {
  // ...
}

// Add properties after the query completes
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>
  The event is automatically finalized when the query stream completes. Use `finish()` only when you need to force immediate shipping before the stream ends.
</Info>

***

## Flush & Shutdown

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

```typescript theme={null}
import { eventMetadata } from "@raindrop-ai/claude-agent-sdk";

// Serverless: flush at end of request
export async function POST(request: Request) {
  const wrapped = raindrop.wrap(claudeAgentSDK);
  const messages = [];
  for await (const msg of wrapped.query(
    { prompt, options },
    eventMetadata({ userId: "user_123" })
  )) {
    messages.push(msg);
  }
  await raindrop.flush();
  return Response.json({ messages });
}

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

***

## Debugging

Enable debug logging to troubleshoot issues:

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

Or via environment variable:

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

This logs:

* Every event sent to Raindrop
* Every trace batch shipped
* Span parent/child relationships (with `debugSpans`)

***

## Troubleshooting

### Events not appearing in dashboard

1. **Ensure tracked queries include `eventMetadata()`**
2. **Check your write key** - Ensure `RAINDROP_WRITE_KEY` is set correctly
3. **Flush before exit** - Call `await raindrop.flush()` before your process ends
4. **Enable debug logging** - Set `events: { debug: true }` to see what's being sent

### Traces missing or incomplete

1. **Enable trace debugging** - Set `traces: { debug: true, debugSpans: true }`
2. **Check for errors** - Look for `[raindrop-ai/claude-agent-sdk]` prefixed logs

### Wrapper not capturing tool calls

Ensure the wrapped `query` is the one you call. Tools are traced via SDK hooks; if you call the unwrapped SDK directly, no tracing occurs.

***

That's it! You're ready to explore your events and traces in the Raindrop dashboard. Ping us on Slack or [email us](mailto:founders@raindrop.ai) if you get stuck!
