SuperAPI AI Directory

claude-code-sdk

Agents & models

View repository

Updated 10-16 01:15

About this skill

Claude Code SDK Integration

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 login stored in ~/.claude/ credentials
  • API Key Authentication: Via ANTHROPIC_API_KEY environment 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 operations
  • BASH - Terminal command execution
  • GLOB, GREP - File search
  • WEB_FETCH - Web scraping

Best Practices

  1. 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
  1. 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}")
  1. 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
  1. 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 (anthropic package) - Direct HTTP API, requires API Key
    • Claude Code SDK (claude-code-sdk) - Agent functionality, supports OAuth
  • 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 anthropic package
  • 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

References