Claude-code-plugins-plus anth-sdk-patterns
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/anthropic-pack/skills/anth-sdk-patterns" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-anth-sdk-patterns && rm -rf "$T"
manifest:
plugins/saas-packs/anthropic-pack/skills/anth-sdk-patterns/SKILL.mdsource content
Anthropic SDK Patterns
Overview
Production-ready patterns for the Anthropic SDK covering client management, error handling, type safety, and multi-tenant configurations.
Prerequisites
- Completed
setupanth-install-auth - Familiarity with async/await patterns
- TypeScript 5+ or Python 3.10+
Pattern 1: Typed Wrapper with Retry
import Anthropic from '@anthropic-ai/sdk'; import type { Message, MessageCreateParams } from '@anthropic-ai/sdk/resources/messages'; class ClaudeService { private client: Anthropic; constructor(apiKey?: string) { this.client = new Anthropic({ apiKey: apiKey || process.env.ANTHROPIC_API_KEY, maxRetries: 3, // SDK handles 429 + 5xx automatically timeout: 60_000, }); } async complete( prompt: string, options: Partial<MessageCreateParams> = {} ): Promise<string> { const message = await this.client.messages.create({ model: options.model || 'claude-sonnet-4-20250514', max_tokens: options.max_tokens || 1024, messages: [{ role: 'user', content: prompt }], ...options, }); const textBlock = message.content.find((b) => b.type === 'text'); if (!textBlock || textBlock.type !== 'text') { throw new Error(`No text in response: ${message.stop_reason}`); } return textBlock.text; } async *stream(prompt: string, model = 'claude-sonnet-4-20250514'): AsyncGenerator<string> { const stream = this.client.messages.stream({ model, max_tokens: 4096, messages: [{ role: 'user', content: prompt }], }); for await (const event of stream) { if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') { yield event.delta.text; } } } }
Pattern 2: Multi-Turn Conversation Manager
import anthropic from dataclasses import dataclass, field @dataclass class Conversation: client: anthropic.Anthropic = field(default_factory=anthropic.Anthropic) model: str = "claude-sonnet-4-20250514" system: str = "" messages: list = field(default_factory=list) max_tokens: int = 4096 def say(self, user_message: str) -> str: self.messages.append({"role": "user", "content": user_message}) response = self.client.messages.create( model=self.model, max_tokens=self.max_tokens, system=self.system, messages=self.messages, ) assistant_text = response.content[0].text self.messages.append({"role": "assistant", "content": assistant_text}) return assistant_text @property def token_count(self) -> int: """Estimate total tokens in conversation.""" return sum(len(str(m["content"])) // 4 for m in self.messages) # Usage conv = Conversation(system="You are a helpful coding assistant.") print(conv.say("What is a closure in JavaScript?")) print(conv.say("Can you show me an example?")) # Has full context
Pattern 3: Structured Output with Prefill
import json import anthropic client = anthropic.Anthropic() def extract_structured(text: str, schema_description: str) -> dict: """Force JSON output using assistant prefill technique.""" message = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=1024, messages=[ {"role": "user", "content": f"Extract data from this text as JSON.\n\nSchema: {schema_description}\n\nText: {text}"}, {"role": "assistant", "content": "{"} # Prefill forces JSON output ] ) json_str = "{" + message.content[0].text return json.loads(json_str) # Usage data = extract_structured( "John Smith, 35, lives in NYC and works at Google as a PM.", '{"name": str, "age": int, "city": str, "company": str, "role": str}' ) # {"name": "John Smith", "age": 35, "city": "NYC", "company": "Google", "role": "PM"}
Pattern 4: Multi-Tenant Client Factory
const clients = new Map<string, Anthropic>(); export function getClientForTenant(tenantId: string): Anthropic { if (!clients.has(tenantId)) { const apiKey = getApiKeyForTenant(tenantId); // From your secret store clients.set(tenantId, new Anthropic({ apiKey })); } return clients.get(tenantId)!; }
Pattern 5: Token-Aware Request Sizing
# Use the Token Counting API to pre-check request size count = client.messages.count_tokens( model="claude-sonnet-4-20250514", messages=[{"role": "user", "content": long_document}], system="You are a summarizer." ) print(f"Input will use {count.input_tokens} tokens") # Adjust max_tokens to stay within budget remaining_budget = 200_000 - count.input_tokens max_tokens = min(4096, remaining_budget)
Error Handling
| Pattern | Use Case | Benefit |
|---|---|---|
SDK | 429 / 5xx errors | Built-in exponential backoff |
| Prefill technique | Force JSON output | No regex parsing needed |
| Token counting | Long documents | Prevent context overflow |
| Client factory | Multi-tenant SaaS | Key isolation per customer |
Resources
Next Steps
Apply patterns in
anth-core-workflow-a for tool use workflows.