# Anthropic Client

Interact with Anthropic's Claude API for powerful AI conversations and text generation.

## Methods

| Method                                   | Description                                           |
| ---------------------------------------- | ----------------------------------------------------- |
| `apiRequest(options, schema, metadata?)` | Make any Anthropic API request with schema validation |

## Usage

### Create a Message

```typescript
import { api, z, anthropic } from "@superblocksteam/sdk-api";

// Integration ID from the integrations panel
const PROD_ANTHROPIC = "a1b2c3d4-5678-90ab-cdef-444444444444";

const MessageResponseSchema = z.object({
  id: z.string(),
  type: z.literal("message"),
  role: z.literal("assistant"),
  content: z.array(
    z.object({
      type: z.literal("text"),
      text: z.string(),
    }),
  ),
  model: z.string(),
  stop_reason: z.string().nullable(),
  stop_sequence: z.string().nullable(),
  usage: z.object({
    input_tokens: z.number(),
    output_tokens: z.number(),
  }),
});

export default api({
  name: "AnthropicExample",
  integrations: {
    ai: anthropic(PROD_ANTHROPIC),
  },
  input: z.object({
    prompt: z.string(),
  }),
  output: z.object({
    response: z.string(),
  }),
  async run(ctx, { prompt }) {
    const result = await ctx.integrations.ai.apiRequest(
      {
        method: "POST",
        path: "/v1/messages",
        body: {
          model: "claude-3-5-sonnet-20241022",
          max_tokens: 1024,
          messages: [{ role: "user", content: prompt }],
        },
      },
      { response: MessageResponseSchema },
    );

    const textContent = result.content.find((c) => c.type === "text");
    return { response: textContent?.text ?? "" };
  },
});
```

### Conversation with System Prompt

```typescript
const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: {
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 2048,
      system:
        "You are a helpful coding assistant. Always provide code examples.",
      messages: [
        { role: "user", content: "How do I read a file in Python?" },
        {
          role: "assistant",
          content: "Here's how to read a file in Python...",
        },
        { role: "user", content: "What about writing to a file?" },
      ],
    },
  },
  { response: MessageResponseSchema },
);
```

### Tool Use (Function Calling)

```typescript
const ToolUseResponseSchema = z.object({
  id: z.string(),
  type: z.literal("message"),
  content: z.array(
    z.discriminatedUnion("type", [
      z.object({
        type: z.literal("text"),
        text: z.string(),
      }),
      z.object({
        type: z.literal("tool_use"),
        id: z.string(),
        name: z.string(),
        input: z.record(z.unknown()),
      }),
    ]),
  ),
  stop_reason: z.string().nullable(),
  usage: z.object({
    input_tokens: z.number(),
    output_tokens: z.number(),
  }),
});

const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: {
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 1024,
      tools: [
        {
          name: "get_weather",
          description: "Get the current weather for a location",
          input_schema: {
            type: "object",
            properties: {
              location: {
                type: "string",
                description: "City and state, e.g., San Francisco, CA",
              },
            },
            required: ["location"],
          },
        },
      ],
      messages: [
        { role: "user", content: "What's the weather in San Francisco?" },
      ],
    },
  },
  { response: ToolUseResponseSchema },
);

// Check if Claude wants to use a tool
const toolUse = result.content.find((c) => c.type === "tool_use");
if (toolUse && toolUse.type === "tool_use") {
  console.log(`Tool: ${toolUse.name}, Input:`, toolUse.input);
}
```

### Vision (Image Analysis)

```typescript
const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: {
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 1024,
      messages: [
        {
          role: "user",
          content: [
            {
              type: "image",
              source: {
                type: "base64",
                media_type: "image/jpeg",
                data: base64ImageData,
              },
            },
            {
              type: "text",
              text: "What's in this image?",
            },
          ],
        },
      ],
    },
  },
  { response: MessageResponseSchema },
);
```

### Using Extended Thinking (Claude 3.5)

```typescript
const ExtendedThinkingResponseSchema = z.object({
  id: z.string(),
  type: z.literal("message"),
  content: z.array(
    z.discriminatedUnion("type", [
      z.object({
        type: z.literal("thinking"),
        thinking: z.string(),
      }),
      z.object({
        type: z.literal("text"),
        text: z.string(),
      }),
    ]),
  ),
  usage: z.object({
    input_tokens: z.number(),
    output_tokens: z.number(),
  }),
});

const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: {
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 16000,
      thinking: {
        type: "enabled",
        budget_tokens: 10000,
      },
      messages: [
        {
          role: "user",
          content: "Solve this complex math problem step by step...",
        },
      ],
    },
  },
  { response: ExtendedThinkingResponseSchema },
);
```

## Trace Metadata

All methods accept an optional `metadata` parameter as the last argument for diagnostics labeling. See the [root SDK README](../../../README.md#trace-metadata) for details.

## Common Pitfalls

### Streaming Is Not Supported

`apiRequest()` does not support streaming or Server-Sent Events. Do not set
`stream: true` — streaming responses fail schema validation. Every call
returns the complete response; if a UI needs real-time token streaming,
handle it at the frontend layer, not through the SDK.

### No Specialized Methods

The Anthropic client only provides `apiRequest()`. There are no other specialized methods:

```typescript
// WRONG - These methods do not exist
await anthropic.createMessage({ ... });
await anthropic.complete({ ... });

// CORRECT - Use apiRequest for all operations
await ctx.integrations.ai.apiRequest(
  { method: "POST", path: "/v1/messages", body: { ... } },
  { response: MessageResponseSchema }
);
```

### max_tokens is Required

Unlike some other APIs, `max_tokens` is a required parameter:

```typescript
// WRONG - Missing max_tokens
const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: {
      model: "claude-3-5-sonnet-20241022",
      messages: [{ role: "user", content: "Hello" }],
      // max_tokens is missing!
    },
  },
  { response: MessageResponseSchema },
);

// CORRECT - Include max_tokens
const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: {
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 1024, // Required!
      messages: [{ role: "user", content: "Hello" }],
    },
  },
  { response: MessageResponseSchema },
);
```

### Content is an Array

Claude's response content is always an array, even for simple text responses:

```typescript
// WRONG - Expecting a string
const schema = z.object({
  content: z.string(),
});

// CORRECT - Content is an array
const schema = z.object({
  content: z.array(
    z.object({
      type: z.literal("text"),
      text: z.string(),
    }),
  ),
});

// To extract text:
const text = result.content
  .filter((c) => c.type === "text")
  .map((c) => c.text)
  .join("");
```

### System Prompt is Separate

Unlike OpenAI, the system prompt is a separate field, not a message:

```typescript
// WRONG - System as a message
const body = {
  messages: [
    { role: "system", content: "You are helpful" }, // Won't work!
    { role: "user", content: "Hello" },
  ],
};

// CORRECT - System as a separate field
const body = {
  system: "You are helpful", // Separate field
  messages: [{ role: "user", content: "Hello" }],
};
```

### API Version Header

For specific API versions or beta features:

```typescript
const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: { ... },
    headers: {
      "anthropic-version": "2023-06-01",
      "anthropic-beta": "tools-2024-05-16", // For beta features
    },
  },
  { response: MessageResponseSchema }
);
```

### Model Token Limits

| Model             | Max Output Tokens |
| ----------------- | ----------------- |
| claude-3-5-sonnet | 8,192             |
| claude-3-opus     | 4,096             |
| claude-3-haiku    | 4,096             |

```typescript
// Be mindful of token limits
const result = await ctx.integrations.ai.apiRequest(
  {
    method: "POST",
    path: "/v1/messages",
    body: {
      model: "claude-3-5-sonnet-20241022",
      max_tokens: 8192, // Max for Sonnet
      messages: [...],
    },
  },
  { response: MessageResponseSchema }
);
```

## Error Handling

```typescript
import { RestApiValidationError } from "@superblocksteam/sdk-api";

try {
  const result = await ctx.integrations.ai.apiRequest(
    { method: "POST", path: "/v1/messages", body: { ... } },
    { response: MessageResponseSchema }
  );
} catch (error) {
  if (error instanceof RestApiValidationError) {
    console.error("Validation failed:", error.details.zodError);
    console.error("Actual response:", error.details.data);
  }
}
```

## API Reference

- [Anthropic API Reference](https://docs.anthropic.com/en/api)
- [Messages API](https://docs.anthropic.com/en/api/messages)
- [Tool Use](https://docs.anthropic.com/en/docs/tool-use)
- [Vision](https://docs.anthropic.com/en/docs/vision)
