Install
$ agentstack add skill-agents-inc-skills-ai-provider-anthropic-sdk ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Anthropic SDK Patterns
> Quick Guide: Use the official @anthropic-ai/sdk package to interact with Claude models directly. Use client.messages.create() for single-turn and multi-turn conversations. Use client.messages.stream() for streaming with event-based consumption. max_tokens is always required. Content blocks are typed unions (text, tool_use, thinking). Use client.messages.parse() with zodOutputFormat() for structured outputs. Tool use requires a tool-result loop -- Claude returns tool_use blocks, you execute the tool and send back tool_result blocks. Extended thinking adds thinking content blocks before the response.
CRITICAL: Before Using This Skill
> All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
(You MUST always provide max_tokens in every messages.create() / messages.stream() call -- it is required and has no default)
(You MUST handle the stop_reason field to detect end_turn, max_tokens, tool_use, and stop_sequence -- ignoring it causes silent truncation or broken tool loops)
(You MUST iterate over response.content blocks (not assume a single text block) -- responses can contain text, tool_use, and thinking blocks mixed together)
(You MUST handle errors using Anthropic.APIError and its subclasses -- never use bare catch blocks without error type checking)
(You MUST never hardcode API keys -- always use environment variables via process.env.ANTHROPIC_API_KEY)
Auto-detection: Anthropic, @anthropic-ai/sdk, client.messages.create, client.messages.stream, client.messages.parse, client.messages.countTokens, client.messages.batches, ANTHROPICAPIKEY, claude-sonnet, claude-opus, claude-haiku, ContentBlock, ToolUseBlock, tooluse, toolresult, thinking, budgettokens, adaptive, cachecontrol, zodOutputFormat, betaZodTool, toolRunner
When to use:
- Building applications that call Claude models directly (Opus, Sonnet, Haiku families)
- Implementing streaming chat responses with event-based text accumulation
- Using tool use / function calling where Claude decides which tools to invoke
- Processing images, PDFs, or documents alongside text prompts
- Enabling extended thinking for complex reasoning tasks
- Extracting structured data from responses with Zod schema validation
- Caching large system prompts or conversation prefixes for cost savings
- Running batch jobs for high-volume, asynchronous processing
- Counting tokens before sending requests for cost estimation
Key patterns covered:
- Client initialization and configuration (retries, timeouts, API key)
- Messages API (
messages.create, system prompts, multi-turn conversations) - Streaming with
.stream()helper andstream: truelow-level SSE - Tool use / function calling (tools array,
tool_use/tool_resultcontent blocks) - Vision (base64 images, URL images, PDFs/documents)
- Extended thinking (
thinkingconfig,budget_tokens, thinking content blocks) - Structured outputs (
zodOutputFormat,messages.parse,output_config) - Prompt caching (
cache_control: { type: "ephemeral" }) - Batch API (
messages.batches.create) - Token counting (
messages.countTokens) - Error handling, retries, and production best practices
When NOT to use:
- Multi-provider applications where you need to switch between multiple LLM providers -- use a unified provider SDK instead
- React-specific chat UI hooks (
useChat,useCompletion) -- use a framework-integrated AI SDK - When you need a higher-level agent framework -- consider the Claude Agent SDK (
@anthropic-ai/claude-agent-sdk)
Examples Index
- [Core: Setup & Configuration](examples/core.md) -- Client init, production config, error handling, token counting
- [Streaming](examples/streaming.md) --
.stream()helper,stream: trueSSE, event types, abort - [Tool Use / Function Calling](examples/tool-use.md) -- Tool definitions, tool loops, parallel tool calls, automated tool runner
- [Vision & Documents](examples/vision-documents.md) -- Base64 images, URL images, PDFs, multi-modal
- [Extended Thinking](examples/extended-thinking.md) -- Thinking config, streaming thinking, thinking with tool use
- [Quick API Reference](reference.md) -- Model IDs, method signatures, error types, streaming events, content block types
Philosophy
The official Anthropic SDK provides direct, typed access to the Claude API. It is auto-generated from Anthropic's API specification using Stainless, giving you the exact API surface that Anthropic documents with full TypeScript types.
Core principles:
- Content blocks, not strings -- Responses are arrays of typed content blocks (
TextBlock,ToolUseBlock,ThinkingBlock), not plain strings. Always iterate overresponse.contentand switch onblock.type. - Explicit resource limits --
max_tokensis always required. There is no default. The API will reject requests without it. - Tool use is a conversation loop -- When
stop_reason === "tool_use", Claude is requesting you execute a tool. You must send the result back as atool_resultcontent block to continue the conversation. - Built-in resilience -- The SDK retries 2 times by default on 429, 409, 408, 529, and 5xx errors with exponential backoff.
- Streaming as a first-class pattern -- Use
.stream()for an event-based API with.on("text", ...), orstream: truefor raw SSE iteration.
When to use the Anthropic SDK directly:
- You only use Claude models and want the simplest, most direct integration
- You need access to Anthropic-specific features (extended thinking, prompt caching, batch API)
- You want minimal dependencies and zero abstraction overhead
- You need the latest API features on day one
When NOT to use:
- You need to switch between multiple LLM providers -- use a unified provider SDK
- You want React-specific chat UI hooks -- use a framework-integrated AI SDK
- You want a higher-level agent framework -- consider the Claude Agent SDK
Core Patterns
Pattern 1: Client Setup
Initialize the Anthropic client. It auto-reads ANTHROPIC_API_KEY from the environment.
// lib/anthropic.ts -- basic setup
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic();
export { client };
// lib/anthropic.ts -- production configuration
const TIMEOUT_MS = 30_000;
const MAX_RETRIES = 3;
const client = new Anthropic({ timeout: TIMEOUT_MS, maxRetries: MAX_RETRIES });
Why good: Minimal setup, env var auto-detected, named constants for production settings
// BAD: Hardcoded API key
const client = new Anthropic({ apiKey: "sk-ant-api03-..." });
Why bad: Hardcoded keys get committed to version control, causing security breaches
See: [examples/core.md](examples/core.md) for per-request overrides, error handling patterns, token counting
Pattern 2: Messages API
All interactions use client.messages.create(). max_tokens is always required.
const MAX_TOKENS = 1024;
const message = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
system: "You are a helpful coding assistant.",
messages: [{ role: "user", content: "Explain TypeScript generics." }],
});
// Response is an array of content blocks -- iterate, don't assume
for (const block of message.content) {
if (block.type === "text") {
console.log(block.text);
}
}
Why good: Named constant for max_tokens, system prompt separated from messages, content blocks iterated
// BAD: Assuming content is a single text string
const text = message.content[0].text; // Crashes if block is tool_use or thinking
Why bad: Content can contain multiple blocks of different types -- direct index access without type checking crashes at runtime
See: [examples/core.md](examples/core.md) for multi-turn conversations, system prompts, token tracking
Pattern 3: Streaming
Use .stream() for event-based streaming with text accumulation helpers.
const MAX_TOKENS = 1024;
const stream = client.messages.stream({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
messages: [{ role: "user", content: "Explain async/await." }],
});
stream.on("text", (text) => {
process.stdout.write(text);
});
const finalMessage = await stream.finalMessage();
Why good: Event-based API handles accumulation, finalMessage() gives the complete response object
// BAD: Using stream: true without consuming events
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
messages: [{ role: "user", content: "Hello" }],
stream: true,
});
// Response is an async iterable, not a Message -- must iterate
Why bad: stream: true returns an async iterable of raw SSE events, not a Message. Treating it as a Message silently breaks.
See: [examples/streaming.md](examples/streaming.md) for raw SSE iteration, abort, stream events, streaming with thinking
Pattern 4: Tool Use / Function Calling
Define tools Claude can invoke. Handle the tool_use -> tool_result conversation loop.
const tools: Anthropic.Messages.Tool[] = [
{
name: "get_weather",
description: "Get current weather for a location",
input_schema: {
type: "object" as const,
properties: {
location: { type: "string", description: "City name" },
},
required: ["location"],
},
},
];
const MAX_TOKENS = 1024;
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
tools,
messages: [{ role: "user", content: "Weather in Paris?" }],
});
// Check stop_reason to know if Claude wants to call a tool
if (response.stop_reason === "tool_use") {
const toolBlock = response.content.find(
(block): block is Anthropic.Messages.ToolUseBlock =>
block.type === "tool_use",
);
if (toolBlock) {
console.log(`Call ${toolBlock.name} with:`, toolBlock.input);
}
}
Why good: Typed tool definitions, stop_reason checked, type guard for ToolUseBlock
// BAD: Not checking stop_reason, not sending tool_result back
const response = await client.messages.create({
/* ... with tools */
});
console.log(response.content[0]); // May be a tool_use block, not text!
Why bad: When Claude wants to call a tool, there is no text content -- only tool_use blocks. You must execute the tool and send back a tool_result to get the final answer.
See: [examples/tool-use.md](examples/tool-use.md) for complete tool loops, parallel tool calls, automated tool runner
Pattern 5: Vision & Documents
Pass images and PDFs as content blocks alongside text.
import { readFileSync } from "node:fs";
const MAX_TOKENS = 1024;
const imageData = readFileSync("photo.jpg").toString("base64");
const message = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
messages: [
{
role: "user",
content: [
{
type: "image",
source: { type: "base64", media_type: "image/jpeg", data: imageData },
},
{ type: "text", text: "What's in this image?" },
],
},
],
});
Why good: Multi-part content array, explicit media type, text and image combined in one message
See: [examples/vision-documents.md](examples/vision-documents.md) for URL images, PDFs, multiple images
Pattern 6: Extended Thinking
Enable extended thinking for complex reasoning. Responses include thinking content blocks. Use adaptive thinking on Opus 4.6 and Sonnet 4.6 (recommended). Use manual budget_tokens on older models.
const MAX_TOKENS = 16_000;
// Adaptive thinking (recommended for 4.6 models)
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
thinking: { type: "adaptive" },
messages: [
{ role: "user", content: "Prove there are infinitely many primes." },
],
} as unknown as Anthropic.MessageCreateParamsNonStreaming);
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);
}
}
Why good: Adaptive thinking lets Claude decide how much to reason, iterates content blocks, handles both thinking and text blocks
// Manual thinking (deprecated on 4.6 models, required on older models)
const THINKING_BUDGET = 10_000;
const response = await client.messages.create({
model: "claude-sonnet-4-5",
max_tokens: MAX_TOKENS,
thinking: { type: "enabled", budget_tokens: THINKING_BUDGET },
messages: [
{ role: "user", content: "Prove there are infinitely many primes." },
],
});
Note: The TypeScript SDK does not yet have "adaptive" in its type definitions. The as unknown as Anthropic.MessageCreateParamsNonStreaming assertion is required until the SDK types are updated.
See: [examples/extended-thinking.md](examples/extended-thinking.md) for streaming thinking, thinking with tools, display options
Pattern 7: Structured Outputs
Use zodOutputFormat() and messages.parse() for type-safe structured responses.
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { z } from "zod";
const ContactInfo = z.object({
name: z.string(),
email: z.string(),
topics: z.array(z.string()),
});
const MAX_TOKENS = 1024;
const response = await client.messages.parse({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
messages: [
{
role: "user",
content:
"Extract info: John (john@example.com) asked about billing and API limits.",
},
],
output_config: { format: zodOutputFormat(ContactInfo) },
});
const parsed = response.parsed_output; // Fully typed: { name, email, topics }
Why good: Auto-converts Zod schema, validates output, fully typed result
See: [examples/core.md](examples/core.md) for raw JSON schema, combined with tool use
Pattern 8: Prompt Caching
Cache large system prompts and conversation prefixes for cost savings.
const MAX_TOKENS = 1024;
const response = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: MAX_TOKENS,
system: [
{
type: "text",
text: "You are a legal document analyst.",
},
{
type: "text",
text: largeDocumentText, // 50+ pages of legal text
cache_control: { type: "ephemeral" },
},
],
messages: [{ role: "user", content: "What are the key terms?" }],
});
// Check cache performance
console.log("Cache read tokens:", response.usage.cache_read_input_tokens);
console.log("Cache write tokens:", response.usage.cache_creation_input_tokens);
Why good: Cache breakpoint on the large static content, cache metrics tracked
See: [reference.md](reference.md) for cache pricing, TTL options, automatic caching
Pattern 9: Error Handling
Always catch Anthropic.APIError and its subclasses. Re-throw unexpected errors.
try {
const message = await client.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [{ role: "user", content: "Hello" }],
});
} catch (error) {
if (error instanceof Anthropic.APIError) {
console.error(`API Error [${error.status}]: ${error.message}`);
if (error instanceof Anthropic.RateLimitError) {
console.error("Rate limited -- SDK will auto-retry 2 times");
}
if (error instanceof Anthropic.AuthenticationError) {
throw new Error("Invalid API key. Check ANTHROPIC_API_KEY.");
}
} else {
throw error; // Re-throw non-API errors
}
}
Why good: Specific error types, status code ac
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: agents-inc
- Source: agents-inc/skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.