Overview
Claude Code SDK (claude-code-sdk) is the official Python package provided by Anthropic for integrating Claude AI Agent functionality into applications. It supports OAuth authentication (via claude login) and API Key authentication, allowing developers to use Claude Max/Pro subscriptions or API billing plans. This SDK is a key tool for building AI-powered applications, especially suited for scenarios requiring code generation, file operations, or multi-turn conversations.
Key Concepts
Authentication Methods
Description: Claude Code SDK supports two authentication methods Key Features:
- OAuth Authentication: Use
claude loginstored in~/.claude/credentials - API Key Authentication: Via
ANTHROPIC_API_KEYenvironment variable
Use Cases:
- OAuth: Individual development, users with Max/Pro subscription
- API Key: Production environments, services requiring metered billing
SDK Core API
Description: query() function is the main asynchronous interface
Key Features:
- Async iterator returns messages
- Supports streaming and complete responses
- Configurable tool permissions and execution limits
Use Cases: Code generation, file operations, research tasks, read-only analysis
Tool Types (ToolType)
Description: SDK supports multiple built-in tools Key Features:
READ,WRITE,EDIT- File operationsBASH- Terminal command executionGLOB,GREP- File searchWEB_FETCH- Web scraping
Best Practices
- Dual authentication check
- Supports both OAuth and API Key
- Prioritize OAuth (free use of Max/Pro quota)
- API Key as fallback
def check_auth_status() -> dict:
status = {
'api_key_set': bool(os.environ.get('ANTHROPIC_API_KEY')),
'oauth_logged_in': False,
}
# 檢查 OAuth 登入狀態
claude_dir = os.path.expanduser('~/.claude')
if os.path.exists(claude_dir):
auth_files = ['credentials.json', 'settings.json', '.credentials.json']
for auth_file in auth_files:
if os.path.exists(os.path.join(claude_dir, auth_file)):
status['oauth_logged_in'] = True
break
# 只需要其中一種認證
status['auth_available'] = status['api_key_set'] or status['oauth_logged_in']
return status
- Increase Buffer Size
- Default buffer is too small; processing large responses causes errors
- Recommend increasing to 50MB
try:
from claude_code_sdk._internal.transport import subprocess_cli
subprocess_cli._MAX_BUFFER_SIZE = 50 * 1024 * 1024 # 50MB
except Exception as e:
print(f"Failed to patch SDK buffer size: {e}")
- Use Singleton Pattern
- Avoid repeated initialization
- Share state and configuration
_claude_service: Optional['ClaudeCodeService'] = None
def get_claude_code() -> 'ClaudeCodeService':
global _claude_service
if _claude_service is None:
_claude_service = ClaudeCodeService()
return _claude_service
- Default configuration pattern
- Limit tool permissions based on usage scenario
- Reduce risk, improve security
@dataclass
class AgentConfig:
allowed_tools: List[str]
max_turns: int = 30
cwd: Optional[str] = None
@classmethod
def coding(cls) -> 'AgentConfig':
"""開發模式:完整權限"""
return cls(
allowed_tools=['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep'],
max_turns=50
)
@classmethod
def research(cls) -> 'AgentConfig':
"""研究模式:可上網"""
return cls(
allowed_tools=['Read', 'Glob', 'Grep', 'WebFetch'],
max_turns=20
)
@classmethod
def readonly(cls) -> 'AgentConfig':
"""唯讀模式:最安全"""
return cls(
allowed_tools=['Read', 'Glob', 'Grep'],
max_turns=10
)
Common Pitfalls
Pitfall 1: Calling SDK directly on the frontend
- Problem: claude-code-sdk is a Python package and cannot run in the browser
- Solution: Must create a backend API service
- Example:
# ❌ 錯誤:嘗試在前端使用
# JavaScript 無法直接使用 Python SDK
# ✅ 正確:建立 FastAPI 後端
from fastapi import FastAPI
from claude_code_sdk import query
app = FastAPI()
@app.post("/api/generate")
async def generate(prompt: str):
result = []
async for msg in query(prompt=prompt):
result.append(msg)
return {"result": result}
Pitfall 2: Forgetting to handle asynchrony
- Problem: SDK is asynchronous; forgetting await causes errors
- Solution: Use async/await or asyncio
# ❌ 錯誤
def run_sync():
for msg in query(prompt="Hello"): # TypeError
print(msg)
# ✅ 正確
async def run_async():
async for msg in query(prompt="Hello"):
print(msg)
Pitfall 3: Confusing Claude API with Claude Code SDK
- Problem:
- Claude API (
anthropicpackage) - Direct HTTP API, requires API Key - Claude Code SDK (
claude-code-sdk) - Agent functionality, supports OAuth
- Claude API (
- Solution: Choose the correct package based on requirements
| Feature | Claude API | Claude Code SDK |
|---|---|---|
| Authentication | API Key only | OAuth + API Key |
| Function | Pure conversation | Agent (file/terminal operations) |
| Billing | API usage billing | Max/Pro subscription available |
| Package | anthropic |
claude-code-sdk |
Patterns & Anti-Patterns
Patterns (Do This)
Complete service wrapper:
from dataclasses import dataclass, field
from typing import AsyncIterator, Optional, List, Dict, Any
from claude_code_sdk import query, ClaudeCodeOptions, AssistantMessage, TextBlock
@dataclass
class ClaudeCodeService:
"""Claude Code 服務封裝"""
async def run(self, prompt: str, config: Optional[AgentConfig] = None) -> Dict[str, Any]:
"""執行並收集完整結果"""
options = ClaudeCodeOptions(
allowed_tools=config.allowed_tools if config else None,
max_turns=config.max_turns if config else 30,
cwd=config.cwd
)
result_texts = []
tool_calls = []
async for message in query(prompt=prompt, options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
result_texts.append(block.text)
return {
'status': 'success',
'result': '\n'.join(result_texts),
'tool_calls': tool_calls
}
async def stream(self, prompt: str, config: Optional[AgentConfig] = None) -> AsyncIterator[dict]:
"""串流執行,即時回傳"""
options = ClaudeCodeOptions(
allowed_tools=config.allowed_tools if config else None,
max_turns=config.max_turns if config else 30
)
async for message in query(prompt=prompt, options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
yield {'type': 'text', 'content': block.text}
FastAPI streaming endpoint:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import json
app = FastAPI()
@app.post("/api/agent/stream")
async def agent_stream(prompt: str):
async def event_generator():
service = ClaudeCodeService()
async for msg in service.stream(prompt):
yield f"data: {json.dumps(msg)}\n\n"
yield "data: [DONE]\n\n"
return StreamingResponse(
event_generator(),
media_type="text/event-stream"
)
Anti-Patterns (Avoid This)
# ❌ 不要在每次請求都建立新服務
@app.post("/api/generate")
async def bad_generate(prompt: str):
service = ClaudeCodeService() # 每次都新建
return await service.run(prompt)
# ❌ 不要忽略錯誤處理
async def bad_run(prompt: str):
async for msg in query(prompt=prompt): # 可能拋出異常
print(msg)
# ❌ 不要給予過多權限
config = AgentConfig(
allowed_tools=['Read', 'Write', 'Edit', 'Bash', 'WebFetch'], # 全開
max_turns=100 # 太多
)
Tools & Resources
| Tool | Purpose | Link |
|---|---|---|
| claude-code-sdk | Official Python SDK | pip install claude-code-sdk |
| Claude Code CLI | Command-line tool | npm install -g @anthropic-ai/claude-code |
| FastAPI | Backend framework | fastapi.tiangolo.com |
Decision Guide
Use Claude Code SDK when:
- need Agent functionality (file operations, terminal commands)
- want to use Max/Pro subscription instead of API billing
- build AI applications requiring multi-turn interaction
- need streaming real-time responses
Consider alternatives when:
- only need simple conversation → use
anthropicpackage - Pure frontend application → use
anthropic+anthropic-dangerous-direct-browser-access - No Agent functionality needed → use the standard Claude API
Examples
Example 1: Full backend service
Context: Build a backend API that can utilize a Claude Max subscription
Implementation:
# backend/main.py
import os
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
import json
from claude_code_sdk import query, ClaudeCodeOptions, AssistantMessage, TextBlock
app = FastAPI()
# CORS 設定
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
class GenerateRequest(BaseModel):
prompt: str
max_turns: int = 30
def check_auth():
"""檢查認證狀態"""
api_key = bool(os.environ.get('ANTHROPIC_API_KEY'))
oauth = os.path.exists(os.path.expanduser('~/.claude/credentials.json'))
return api_key or oauth
@app.get("/api/health")
async def health():
return {
"status": "ok",
"auth_available": check_auth()
}
@app.post("/api/generate")
async def generate(request: GenerateRequest):
if not check_auth():
raise HTTPException(401, "請先執行 claude login 或設置 ANTHROPIC_API_KEY")
options = ClaudeCodeOptions(max_turns=request.max_turns)
results = []
async for message in query(prompt=request.prompt, options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
results.append(block.text)
return {"result": "\n".join(results)}
@app.post("/api/stream")
async def stream(request: GenerateRequest):
if not check_auth():
raise HTTPException(401, "未認證")
async def event_generator():
options = ClaudeCodeOptions(max_turns=request.max_turns)
async for message in query(prompt=request.prompt, options=options):
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
yield f"data: {json.dumps({'text': block.text})}\n\n"
yield "data: [DONE]\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
requirements.txt:
fastapi>=0.100.0
uvicorn>=0.23.0
claude-code-sdk>=0.1.0
python-dotenv>=1.0.0
Explanation: This backend automatically detects OAuth or API Key authentication, and supports regular requests and streaming responses.
Example 2: Frontend integration
Context: The frontend calls the backend API
Implementation:
// 一般請求
async function generate(prompt) {
const response = await fetch('http://localhost:8000/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt })
});
const data = await response.json();
return data.result;
}
// 串流請求
async function streamGenerate(prompt, onChunk) {
const response = await fetch('http://localhost:8000/api/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ prompt })
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ') && line !== 'data: [DONE]') {
const data = JSON.parse(line.slice(6));
onChunk(data.text);
}
}
}
}
Example 3: Docker deployment
Context: Containerized deployment includes the Claude Code CLI
Implementation:
# Dockerfile
FROM python:3.11-slim
# 安裝 Node.js (Claude Code CLI 需要)
RUN apt-get update && apt-get install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
# 安裝 Claude Code CLI
RUN npm install -g @anthropic-ai/claude-code
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# docker-compose.yml
version: '3.8'
services:
backend:
build: .
ports:
- "8000:8000"
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
volumes:
- ~/.claude:/root/.claude:ro # 掛載 OAuth 憑證
Related Skills
- [[api-design]] - Design REST API endpoints
- [[backend]] - Python/FastAPI backend development
- [[devops-cicd]] - Docker deployment and CI/CD
- [[security-practices]] - API authentication and security