Skip to content
Back to skills

Claude Api

ASecurity

Build, refine, and master agents using the Anthropic Claude API and @anthropic-ai/sdk. Covers messages API, tool use, extended thinking, prompt caching, streaming, and multi-agent patterns. Fetches live docs from docs.anthropic.com. Use when building or debugging anything on the Claude API.

  • 2 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added October 26, 2026
ai-agentstypescriptbashnextjsdebuggingapi

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 26, 2026

npx -y skills add JoshuaShepherd/my-skills --skill claude-api --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Claude Api?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Claude Api
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/joshuashepherd-claude-api/badge)](https://www.skillsdirectory.com/skills/joshuashepherd-claude-api)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: claude-api
description: Build, refine, and master agents using the Anthropic Claude API and @anthropic-ai/sdk. Covers messages API, tool use, extended thinking, prompt caching, streaming, and multi-agent patterns. Fetches live docs from docs.anthropic.com. Use when building or debugging anything on the Claude API.
user-invocable: true
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebFetch, WebSearch
---

Build or refine a Claude API agent: $ARGUMENTS

$ARGUMENTS can include:
- What to build (e.g. `tool-use agent`, `streaming chatbot`, `multi-step reasoning agent`)
- A file path to existing code to refine
- A capability to add (e.g. `add caching`, `add extended thinking`, `add computer use`)
- A model preference (e.g. `--model opus`, `--model sonnet`, `--model haiku`)
- Empty — scaffold a starter agent with best-practice defaults

---

## Authoritative Sources

Always fetch fresh docs before generating or advising on code. These are the canonical Anthropic sources:

| Resource | URL |
|----------|-----|
| Model overview (current IDs) | `https://docs.anthropic.com/en/docs/about-claude/models/overview` |
| Messages API reference | `https://docs.anthropic.com/en/api/messages` |
| Tool use guide | `https://docs.anthropic.com/en/docs/build-with-claude/tool-use/overview` |
| Extended thinking | `https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking` |
| Prompt caching | `https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching` |
| Streaming | `https://docs.anthropic.com/en/docs/build-with-claude/streaming` |
| Computer use | `https://docs.anthropic.com/en/docs/build-with-claude/computer-use` |
| Batch API | `https://docs.anthropic.com/en/docs/build-with-claude/message-batches` |
| Errors & rate limits | `https://docs.anthropic.com/en/api/errors` |
| Pricing | `https://www.anthropic.com/pricing` |

**Fetch the model overview and the relevant API reference page before writing any code.** Model IDs change — never use memory alone.

---

## Environment Setup

```bash
pnpm add @anthropic-ai/sdk
```

```env
# .env.local
ANTHROPIC_API_KEY=sk-ant-...
```

```typescript
// src/lib/ai/anthropic.ts
import Anthropic from '@anthropic-ai/sdk';

export const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});
```

**Current model IDs (verify against live docs before using):**
- `claude-opus-4-6` — 1M context, 128k output, $5/$25 per 1M — highest capability
- `claude-sonnet-4-6` — 1M context, 64k output, $3/$15 per 1M — best balanced (Jan 2026 cutoff)
- `claude-haiku-4-5-20251001` — 200k context, 64k output, $1/$5 per 1M — fastest/cheapest

---

## Phase 1 — Load Context

1. Fetch the model overview page to confirm current model IDs
2. Read any existing code files specified in $ARGUMENTS
3. Read `_docs/ai-intelligence/` for latest pricing/capability snapshot if available
4. Identify the pattern needed: basic call / tool use / thinking / caching / streaming / computer use / multi-agent

---

## Phase 2 — Core Patterns

### Pattern 1: Basic Message

```typescript
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const message = await client.messages.create({
  model: 'claude-sonnet-4-6',
  max_tokens: 4096,
  system: 'You are a helpful assistant.',
  messages: [
    { role: 'user', content: 'Hello, world!' }
  ],
});

console.log(message.content[0].type === 'text' ? message.content[0].text : '');
```

### Pattern 2: Tool Use (Function Calling)

```typescript
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

const tools: Anthropic.Tool[] = [
  {
    name: 'get_weather',
    description: 'Get current weather for a location.',
    input_schema: {
      type: 'object' as const,
      properties: {
        location: {
          type: 'string',
          description: 'City and state, e.g. "San Francisco, CA"',
        },
        unit: {
          type: 'string',
          enum: ['celsius', 'fahrenheit'],
          description: 'Temperature unit',
        },
      },
      required: ['location'],
    },
  },
];

async function runWithTools(userMessage: string) {
  const messages: Anthropic.MessageParam[] = [
    { role: 'user', content: userMessage }
  ];

  while (true) {
    const response = await client.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 4096,
      tools,
      messages,
    });

    // If no tool use, we're done
    if (response.stop_reason === 'end_turn') {
      const textBlock = response.content.find(b => b.type === 'text');
      return textBlock?.type === 'text' ? textBlock.text : '';
    }

    // Collect all tool_use blocks
    const toolUseBlocks = response.content.filter(
      (b): b is Anthropic.ToolUseBlock => b.type === 'tool_use'
    );

    if (toolUseBlocks.length === 0) break;

    // Add assistant turn
    messages.push({ role: 'assistant', content: response.content });

    // Execute tools and collect results
    const toolResults: Anthropic.ToolResultBlockParam[] = await Promise.all(
      toolUseBlocks.map(async (toolUse) => {
        const result = await executeTool(toolUse.name, toolUse.input);
        return {
          type: 'tool_result' as const,
          tool_use_id: toolUse.id,
          content: JSON.stringify(result),
        };
      })
    );

    // Add user turn with all tool results
    messages.push({ role: 'user', content: toolResults });
  }
}

async function executeTool(name: string, input: unknown): Promise<unknown> {
  // Dispatch to real tool implementations
  if (name === 'get_weather') {
    return { temperature: 72, unit: 'fahrenheit', condition: 'sunny' };
  }
  throw new Error(`Unknown tool: ${name}`);
}
```

### Pattern 3: Extended Thinking

```typescript
const response = await client.messages.create({
  model: 'claude-opus-4-6',    // or claude-sonnet-4-6
  max_tokens: 16000,
  thinking: {
    type: 'enabled',
    budget_tokens: 10000,       // how many tokens Claude can think with
  },
  messages: [{ role: 'user', content: 'Solve this step by step: ...' }],
});

// Thinking blocks come before text blocks
for (const block of response.content) {
  if (block.type === 'thinking') {
    console.log('Thinking:', block.thinking);
  } else if (block.type === 'text') {
    console.log('Answer:', block.text);
  }
}
```

**Adaptive Thinking (Opus 4.6 + Sonnet 4.6 only):**
```typescript
thinking: {
  type: 'auto',    // Claude allocates budget dynamically per task complexity
}
```

### Pattern 4: Prompt Caching

Cache large, reused content to save 90% on cached token costs.

```typescript
const response = await client.messages.create({
  model: 'claude-sonnet-4-6',
  max_tokens: 4096,
  system: [
    {
      type: 'text',
      text: LARGE_SYSTEM_PROMPT,          // e.g. 10k token brand voice instructions
      cache_control: { type: 'ephemeral' }, // cached for 5 minutes (default)
    },
  ],
  messages: [{ role: 'user', content: userQuery }],
});
```

**Cache durations:**
- `ephemeral` (default) — 5 minutes
- For 1-hour cache: check current docs — Anthropic extended cache TTL to 1 hour for some plans

**When to cache:**
- System prompts > 1,024 tokens that don't change between requests
- Large document context fed to every request
- Tool definitions (cache the full tools array)

**Cost with caching:**
- Write: $3.75/M (Sonnet) — slightly above standard on first write
- Read: $0.30/M (Sonnet) — 90% cheaper than standard $3.00

### Pattern 5: Streaming

```typescript
const stream = client.messages.stream({
  model: 'claude-sonnet-4-6',
  max_tokens: 4096,
  messages: [{ role: 'user', content: 'Write a long essay about...' }],
});

// Preferred: use the helper methods
const text = await stream.finalText();

// Or process events manually
for await (const event of stream) {
  if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
    process.stdout.write(event.delta.text);
  }
}

const finalMessage = await stream.finalMessage();
console.log('Usage:', finalMessage.usage);
```

**Next.js streaming (App Router):**
```typescript
// app/api/chat/route.ts
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

export async function POST(req: Request) {
  const { messages } = await req.json();

  const stream = client.messages.stream({
    model: 'claude-sonnet-4-6',
    max_tokens: 4096,
    messages,
  });

  const encoder = new TextEncoder();
  const readable = new ReadableStream({
    async start(controller) {
      for await (const event of stream) {
        if (
          event.type === 'content_block_delta' &&
          event.delta.type === 'text_delta'
        ) {
          controller.enqueue(encoder.encode(event.delta.text));
        }
      }
      controller.close();
    },
  });

  return new Response(readable, {
    headers: { 'Content-Type': 'text/plain; charset=utf-8' },
  });
}
```

### Pattern 6: Multi-Turn Conversation State

```typescript
class ClaudeConversation {
  private messages: Anthropic.MessageParam[] = [];
  private client = new Anthropic();

  async send(userContent: string): Promise<string> {
    this.messages.push({ role: 'user', content: userContent });

    const response = await this.client.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 4096,
      system: this.systemPrompt,
      messages: this.messages,
    });

    const assistantText = response.content
      .filter((b): b is Anthropic.TextBlock => b.type === 'text')
      .map(b => b.text)
      .join('');

    this.messages.push({ role: 'assistant', content: response.content });
    return assistantText;
  }

  reset() {
    this.messages = [];
  }

  constructor(private systemPrompt: string) {}
}
```

### Pattern 7: Batch API (50% off, async)

```typescript
// Submit batch
const batch = await client.beta.messages.batches.create({
  requests: items.map((item, i) => ({
    custom_id: `item-${i}`,
    params: {
      model: 'claude-haiku-4-5-20251001',
      max_tokens: 1024,
      messages: [{ role: 'user', content: item.prompt }],
    },
  })),
});

// Poll until complete
let result = await client.beta.messages.batches.retrieve(batch.id);
while (result.processing_status === 'in_progress') {
  await new Promise(r => setTimeout(r, 60000));
  result = await client.beta.messages.batches.retrieve(batch.id);
}

// Stream results
for await (const entry of client.beta.messages.batches.results(batch.id)) {
  if (entry.result.type === 'succeeded') {
    console.log(entry.custom_id, entry.result.message.content);
  }
}
```

---

## Phase 3 — Agent Patterns

### Autonomous Tool-Loop Agent

```typescript
async function runAgent(
  task: string,
  tools: Anthropic.Tool[],
  toolHandlers: Record<string, (input: unknown) => Promise<unknown>>,
  maxTurns = 10,
): Promise<string> {
  const client = new Anthropic();
  const messages: Anthropic.MessageParam[] = [
    { role: 'user', content: task }
  ];

  for (let turn = 0; turn < maxTurns; turn++) {
    const response = await client.messages.create({
      model: 'claude-opus-4-6',
      max_tokens: 8192,
      tools,
      tool_choice: { type: 'auto' },
      messages,
    });

    messages.push({ role: 'assistant', content: response.content });

    if (response.stop_reason === 'end_turn') {
      return response.content
        .filter((b): b is Anthropic.TextBlock => b.type === 'text')
        .map(b => b.text)
        .join('');
    }

    if (response.stop_reason !== 'tool_use') break;

    const toolResults: Anthropic.ToolResultBlockParam[] = [];
    for (const block of response.content) {
      if (block.type !== 'tool_use') continue;
      const handler = toolHandlers[block.name];
      if (!handler) throw new Error(`No handler for tool: ${block.name}`);
      const output = await handler(block.input);
      toolResults.push({
        type: 'tool_result',
        tool_use_id: block.id,
        content: JSON.stringify(output),
      });
    }

    messages.push({ role: 'user', content: toolResults });
  }

  throw new Error('Agent exceeded max turns');
}
```

### Multi-Agent Orchestration (Claude-as-Orchestrator)

```typescript
// Orchestrator delegates to specialized subagents
const orchestratorTools: Anthropic.Tool[] = [
  {
    name: 'delegate_to_researcher',
    description: 'Delegate a research subtask to the researcher agent.',
    input_schema: {
      type: 'object' as const,
      properties: {
        task: { type: 'string', description: 'The research task' },
      },
      required: ['task'],
    },
  },
  {
    name: 'delegate_to_writer',
    description: 'Delegate a writing subtask to the writer agent.',
    input_schema: {
      type: 'object' as const,
      properties: {
        task: { type: 'string', description: 'The writing task' },
        context: { type: 'string', description: 'Background context from research' },
      },
      required: ['task', 'context'],
    },
  },
];

const subagentHandlers = {
  delegate_to_researcher: async ({ task }: { task: string }) => {
    return runAgent(task, researcherTools, researcherHandlers);
  },
  delegate_to_writer: async ({ task, context }: { task: string; context: string }) => {
    return runAgent(`${task}\n\nContext: ${context}`, writerTools, writerHandlers);
  },
};
```

---

## Phase 4 — Provider-Specific Optimizations

### 1M Context Usage (Opus/Sonnet 4.6)

```typescript
// Read entire codebase into context — no chunking needed
import fs from 'fs';
import path from 'path';

function readDirectory(dir: string): string {
  const files = fs.readdirSync(dir, { recursive: true }) as string[];
  return files
    .filter(f => f.endsWith('.ts') || f.endsWith('.tsx'))
    .map(f => {
      const fullPath = path.join(dir, f);
      const content = fs.readFileSync(fullPath, 'utf-8');
      return `\`\`\`typescript\n// ${f}\n${content}\n\`\`\``;
    })
    .join('\n\n');
}

const response = await client.messages.create({
  model: 'claude-opus-4-6',
  max_tokens: 8192,
  messages: [{
    role: 'user',
    content: `Here is the entire codebase:\n\n${readDirectory('./src')}\n\nNow refactor...`
  }],
});
```

### Computer Use (claude-opus-4-6 / claude-sonnet-4-6)

```typescript
const response = await client.beta.messages.create({
  model: 'claude-opus-4-6',
  max_tokens: 4096,
  betas: ['computer-use-2025-01-24'],   // check docs for latest beta string
  tools: [
    { type: 'computer_20250124', name: 'computer', display_width_px: 1280, display_height_px: 800 },
    { type: 'text_editor_20250429', name: 'str_replace_based_edit_tool' },
    { type: 'bash_20250124', name: 'bash' },
  ],
  messages: [{ role: 'user', content: 'Open the browser and navigate to docs.anthropic.com' }],
});
```

---

## Phase 5 — Error Handling & Rate Limits

```typescript
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

async function callWithRetry(
  params: Anthropic.MessageCreateParamsNonStreaming,
  maxRetries = 3,
): Promise<Anthropic.Message> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await client.messages.create(params);
    } catch (error) {
      if (error instanceof Anthropic.RateLimitError) {
        const delay = Math.pow(2, attempt) * 1000;
        console.warn(`Rate limited. Waiting ${delay}ms...`);
        await new Promise(r => setTimeout(r, delay));
        continue;
      }
      if (error instanceof Anthropic.APIError) {
        console.error(`API error ${error.status}: ${error.message}`);
        if (error.status >= 500 && attempt < maxRetries) continue;
        throw error;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}
```

**HTTP status codes:**
- `400` — Bad request (invalid params)
- `401` — Invalid API key
- `403` — Permission denied
- `404` — Resource not found
- `429` — Rate limit (retry with backoff)
- `500/529` — Overloaded (retry with backoff)

---

## Phase 6 — Cost Reference (verify at anthropic.com/pricing)

| Model | Input | Cache Write | Cache Read | Output | Batch In | Batch Out |
|-------|-------|-------------|------------|--------|----------|-----------|
| Opus 4.6 | $5/M | $6.25/M | $0.50/M | $25/M | $2.50/M | $12.50/M |
| Sonnet 4.6 | $3/M | $3.75/M | $0.30/M | $15/M | $1.50/M | $7.50/M |
| Haiku 4.5 | $1/M | $1.25/M | $0.10/M | $5/M | $0.50/M | $2.50/M |

**Fast Mode (Opus 4.6, beta):** $30 input / $150 output — ~6× faster, does not stack with Batch.

**Cost formula:**
```
cost = (input_tokens / 1_000_000 × input_price)
     + (output_tokens / 1_000_000 × output_price)
     - (cached_tokens / 1_000_000 × (input_price - cache_read_price))
```

---

## Output

When building or scaffolding:
1. Fetch the model overview URL to confirm current model IDs
2. Read any existing code files before modifying
3. Generate TypeScript with strict types — no `any`
4. Include error handling and rate limit retry logic
5. Add a cost comment showing tokens × price for the chosen model
6. Write files to their appropriate path (e.g. `src/lib/ai/`, `src/agents/`, `src/app/api/`)

Always state which model ID you used and why.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.