전제 조건

  • Python 3.10+
  • PyPI의 claude-code-sdk
  • Node.js 18+
  • NPM의 @anthropic-ai/claude-code

Python SDK 소스 코드를 보려면 claude-code-sdk 저장소를 참조하세요.

대화형 개발을 위해서는 IPython을 사용하세요: pip install ipython

설치

PyPI에서 claude-code-sdk를 설치하고 NPM에서 @anthropic-ai/claude-code를 설치하세요:

pip install claude-code-sdk
npm install -g @anthropic-ai/claude-code  # 필수 종속성

(선택사항) 대화형 개발을 위해 IPython을 설치하세요:

pip install ipython

빠른 시작

첫 번째 에이전트를 생성하세요:

# legal-agent.py
import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def main():
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="당신은 법률 보조원입니다. 위험을 식별하고 개선 사항을 제안하세요.",
            max_turns=2
        )
    ) as client:
        # 쿼리 전송
        await client.query(
            "잠재적 문제에 대해 이 계약 조항을 검토하세요: '당사자는 무제한 책임에 동의합니다...'"
        )

        # 응답 스트리밍
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                # 도착하는 스트리밍 콘텐츠 출력
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

if __name__ == "__main__":
    asyncio.run(main())

위 코드를 legal-agent.py로 저장한 다음 실행하세요:

python legal-agent.py

IPython/Jupyter 노트북의 경우, 셀에서 직접 코드를 실행할 수 있습니다:

await main()

이 페이지의 Python 예제는 asyncio를 사용하지만 anyio도 사용할 수 있습니다.

기본 사용법

Python SDK는 두 가지 주요 인터페이스를 제공합니다:

1. ClaudeSDKClient 클래스 (권장)

스트리밍 응답, 다중 턴 대화, 대화형 애플리케이션에 최적:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def main():
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="당신은 성능 엔지니어입니다",
            allowed_tools=["Bash", "Read", "WebSearch"],
            max_turns=5
        )
    ) as client:
        await client.query("시스템 성능 분석")

        # 응답 스트리밍
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

# 스크립트로 실행
asyncio.run(main())

# 또는 IPython/Jupyter에서: await main()

2. query 함수

간단한 일회성 쿼리용:

from claude_code_sdk import query, ClaudeCodeOptions

async for message in query(
    prompt="시스템 성능 분석",
    options=ClaudeCodeOptions(system_prompt="당신은 성능 엔지니어입니다")
):
    if type(message).__name__ == "ResultMessage":
        print(message.result)

구성 옵션

Python SDK는 ClaudeCodeOptions 클래스를 통해 명령줄에서 지원하는 모든 인수를 허용합니다.

ClaudeCodeOptions 매개변수

from claude_code_sdk import ClaudeCodeOptions

options = ClaudeCodeOptions(
    # 핵심 구성
    system_prompt="당신은 도움이 되는 어시스턴트입니다",
    append_system_prompt="추가 시스템 지침",
    max_turns=5,
    model="claude-3-5-sonnet-20241022",
    max_thinking_tokens=8000,
    
    # 도구 관리
    allowed_tools=["Bash", "Read", "Write"],
    disallowed_tools=["WebSearch"],
    
    # 세션 관리
    continue_conversation=False,
    resume="session-uuid",
    
    # 환경
    cwd="/path/to/working/directory",
    add_dirs=["/additional/context/dir"],
    settings="/path/to/settings.json",
    
    # 권한
    permission_mode="acceptEdits",  # "default", "acceptEdits", "plan", "bypassPermissions"
    permission_prompt_tool_name="mcp__approval_tool",
    
    # MCP 통합
    mcp_servers={
        "my_server": {
            "command": "npx",
            "args": ["-y", "@modelcontextprotocol/server-example"],
            "env": {"API_KEY": "your-key"}
        }
    },
    
    # 고급
    extra_args={"--verbose": None, "--custom-flag": "value"}
)

매개변수 세부사항

  • system_prompt: str | None - 에이전트의 역할을 정의하는 맞춤 시스템 프롬프트
  • append_system_prompt: str | None - 시스템 프롬프트에 추가되는 추가 텍스트
  • max_turns: int | None - 최대 대화 턴 수 (None이면 무제한)
  • model: str | None - 사용할 특정 Claude 모델
  • max_thinking_tokens: int - Claude의 사고 과정을 위한 최대 토큰 수 (기본값: 8000)
  • allowed_tools: list[str] - 사용이 특별히 허용된 도구
  • disallowed_tools: list[str] - 사용하지 않아야 할 도구
  • continue_conversation: bool - 가장 최근 대화 계속하기 (기본값: False)
  • resume: str | None - 특정 대화를 재개할 세션 UUID
  • cwd: str | Path | None - 세션의 작업 디렉토리
  • add_dirs: list[str | Path] - 컨텍스트에 포함할 추가 디렉토리
  • settings: str | None - 설정 파일 경로 또는 설정 JSON 문자열
  • permission_mode: str | None - 권한 처리 모드
  • permission_prompt_tool_name: str | None - 맞춤 권한 프롬프트 도구 이름
  • mcp_servers: dict | str | Path - MCP 서버 구성
  • extra_args: dict[str, str | None] - 기본 Claude Code CLI에 임의의 CLI 플래그 전달

권한 모드

  • "default": 위험한 도구에 대해 CLI 프롬프트 (기본 동작)
  • "acceptEdits": 프롬프트 없이 파일 편집 자동 승인
  • "plan": 계획 모드 - 변경 없이 분석
  • "bypassPermissions": 프롬프트 없이 모든 도구 허용 (주의해서 사용)

고급 구성 예제

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def advanced_agent():
    """고급 구성 옵션을 보여주는 예제"""
    
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            # 맞춤 작업 디렉토리 및 추가 컨텍스트
            cwd="/project/root",
            add_dirs=["/shared/libs", "/common/utils"],
            
            # 모델 및 사고 구성
            model="claude-3-5-sonnet-20241022",
            max_thinking_tokens=12000,
            
            # 고급 도구 제어
            allowed_tools=["Read", "Write", "Bash", "Grep"],
            disallowed_tools=["WebSearch", "Bash(rm*)"],
            
            # 맞춤 설정 및 CLI 인수
            settings='{"editor": "vim", "theme": "dark"}',
            extra_args={
                "--verbose": None,
                "--timeout": "300"
            }
        )
    ) as client:
        await client.query("코드베이스 구조 분석")
        
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

asyncio.run(advanced_agent())

구조화된 메시지 및 이미지 입력

SDK는 구조화된 메시지와 이미지 입력 전달을 지원합니다:

from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async with ClaudeSDKClient() as client:
    # 텍스트 메시지
    await client.query("보안 문제에 대해 이 코드를 분석하세요")

    # 이미지 참조가 있는 메시지 (이미지는 Claude의 Read 도구로 읽힘)
    await client.query("screenshot.png에 무엇이 표시되어 있는지 설명하세요")

    # 순차적으로 여러 메시지
    messages = [
        "먼저 diagram.png의 아키텍처 다이어그램을 분석하세요",
        "이제 다이어그램을 기반으로 개선 사항을 제안하세요",
        "마지막으로 구현 코드를 생성하세요"
    ]

    for msg in messages:
        await client.query(msg)
        async for response in client.receive_response():
            # 각 응답 처리
            pass

# SDK는 Claude의 내장 Read 도구를 통해 이미지 파일을 처리합니다
# 지원 형식: PNG, JPG, PDF 및 기타 일반적인 형식

다중 턴 대화

방법 1: 지속적인 대화를 위한 ClaudeSDKClient 사용

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions, query

# 방법 1: 지속적인 대화를 위한 ClaudeSDKClient 사용
async def multi_turn_conversation():
    async with ClaudeSDKClient() as client:
        # 첫 번째 쿼리
        await client.query("결제 모듈을 리팩토링해 봅시다")
        async for msg in client.receive_response():
            # 첫 번째 응답 처리
            pass

        # 같은 세션에서 계속
        await client.query("이제 포괄적인 오류 처리를 추가하세요")
        async for msg in client.receive_response():
            # 연속 처리
            pass

        # 대화 컨텍스트가 전체적으로 유지됩니다

# 방법 2: 세션 관리와 함께 query 함수 사용
async def resume_session():
    # 가장 최근 대화 계속
    async for message in query(
        prompt="이제 더 나은 성능을 위해 이것을 리팩토링하세요",
        options=ClaudeCodeOptions(continue_conversation=True)
    ):
        if type(message).__name__ == "ResultMessage":
            print(message.result)

    # 특정 세션 재개
    async for message in query(
        prompt="테스트를 업데이트하세요",
        options=ClaudeCodeOptions(
            resume="550e8400-e29b-41d4-a716-446655440000",
            max_turns=3
        )
    ):
        if type(message).__name__ == "ResultMessage":
            print(message.result)

# 예제 실행
asyncio.run(multi_turn_conversation())

맞춤 시스템 프롬프트

시스템 프롬프트는 에이전트의 역할, 전문성 및 동작을 정의합니다:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def specialized_agents():
    # 스트리밍을 사용한 SRE 인시던트 대응 에이전트
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="당신은 SRE 전문가입니다. 문제를 체계적으로 진단하고 실행 가능한 솔루션을 제공하세요.",
            max_turns=3
        )
    ) as sre_agent:
        await sre_agent.query("API가 다운되었습니다. 조사하세요")

        # 진단 과정 스트리밍
        async for message in sre_agent.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

    # 맞춤 프롬프트를 사용한 법률 검토 에이전트
    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            append_system_prompt="항상 포괄적인 오류 처리와 단위 테스트를 포함하세요.",
            max_turns=2
        )
    ) as dev_agent:
        await dev_agent.query("이 함수를 리팩토링하세요")

        # 전체 응답 수집
        full_response = []
        async for message in dev_agent.receive_response():
            if type(message).__name__ == "ResultMessage":
                print(message.result)

asyncio.run(specialized_agents())

MCP를 통한 맞춤 도구

Model Context Protocol (MCP)을 사용하면 에이전트에게 맞춤 도구와 기능을 제공할 수 있습니다:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def mcp_enabled_agent():
    # 문서 액세스 및 스트리밍을 사용한 법률 에이전트
    # 참고: 필요에 따라 MCP 서버를 구성하세요
    mcp_servers = {
        # 예제 구성 - 필요에 따라 주석을 해제하고 구성하세요:
        # "docusign": {
        #     "command": "npx",
        #     "args": ["-y", "@modelcontextprotocol/server-docusign"],
        #     "env": {"API_KEY": "your-key"}
        # }
    }

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            mcp_servers=mcp_servers,
            allowed_tools=["mcp__docusign", "mcp__compliance_db"],
            system_prompt="당신은 계약 검토를 전문으로 하는 기업 변호사입니다.",
            max_turns=4
        )
    ) as client:
        await client.query("규정 준수 위험에 대해 이 계약을 검토하세요")

        # 도구 사용 및 응답 모니터링
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'type'):
                        if block.type == 'tool_use':
                            print(f"\n[도구 사용: {block.name}]\n")
                        elif hasattr(block, 'text'):
                            print(block.text, end='', flush=True)
                    elif hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

            if type(message).__name__ == "ResultMessage":
                print(f"\n\n검토 완료. 총 비용: ${message.total_cost_usd:.4f}")

asyncio.run(mcp_enabled_agent())

맞춤 권한 프롬프트 도구

도구 호출에 대한 맞춤 권한 처리를 구현하세요:

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def use_permission_prompt():
    """맞춤 권한 프롬프트 도구를 사용하는 예제"""

    # MCP 서버 구성
    mcp_servers = {
        # 예제 구성 - 필요에 따라 주석을 해제하고 구성하세요:
        # "security": {
        #     "command": "npx",
        #     "args": ["-y", "@modelcontextprotocol/server-security"],
        #     "env": {"API_KEY": "your-key"}
        # }
    }

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            permission_prompt_tool_name="mcp__security__approval_prompt",  # permission_prompt_tool에서 변경됨
            mcp_servers=mcp_servers,
            allowed_tools=["Read", "Grep"],
            disallowed_tools=["Bash(rm*)", "Write"],
            system_prompt="당신은 보안 감사관입니다"
        )
    ) as client:
        await client.query("보안 문제를 분석하고 수정하세요")

        # 도구 사용 및 권한 모니터링
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'type'):  # 'type' 속성 확인 추가
                        if block.type == 'tool_use':
                            print(f"[도구: {block.name}] ", end='')
                    if hasattr(block, 'text'):
                        print(block.text, end='', flush=True)

            # 오류 메시지에서 권한 거부 확인
            if type(message).__name__ == "ErrorMessage":
                if hasattr(message, 'error') and "Permission denied" in str(message.error):
                    print(f"\n⚠️ 권한 거부: {message.error}")

# 예제 MCP 서버 구현 (Python)
# 이것은 MCP 서버 코드에 있을 것입니다
async def approval_prompt(tool_name: str, input: dict, tool_use_id: str = None):
    """맞춤 권한 프롬프트 핸들러"""
    # 여기에 맞춤 로직
    if "allow" in str(input):
        return json.dumps({
            "behavior": "allow",
            "updatedInput": input
        })
    else:
        return json.dumps({
            "behavior": "deny",
            "message": f"{tool_name}에 대한 권한이 거부되었습니다"
        })

asyncio.run(use_permission_prompt())

출력 형식

스트리밍을 사용한 텍스트 출력

# 스트리밍을 사용한 기본 텍스트 출력
async with ClaudeSDKClient() as client:
    await client.query("src/components/Header.tsx 파일을 설명하세요")

    # 도착하는 텍스트 스트리밍
    async for message in client.receive_response():
        if hasattr(message, 'content'):
            for block in message.content:
                if hasattr(block, 'text'):
                    print(block.text, end='', flush=True)
                    # 출력이 실시간으로 스트리밍됩니다: 이것은 React 컴포넌트로...

메타데이터가 포함된 JSON 출력

# 메타데이터와 함께 모든 메시지 수집
async with ClaudeSDKClient() as client:
    await client.query("데이터 레이어는 어떻게 작동합니까?")

    messages = []
    result_data = None

    async for message in client.receive_messages():
        messages.append(message)

        # 메타데이터와 함께 결과 메시지 캡처
        if type(message).__name__ == "ResultMessage":
            result_data = {
                "result": message.result,
                "cost": message.total_cost_usd,
                "duration": message.duration_ms,
                "num_turns": message.num_turns,
                "session_id": message.session_id
            }
            break

    print(result_data)

입력 형식

import asyncio
from claude_code_sdk import ClaudeSDKClient

async def process_inputs():
    async with ClaudeSDKClient() as client:
        # 텍스트 입력
        await client.query("이 코드를 설명하세요")
        async for message in client.receive_response():
            # 스트리밍 응답 처리
            pass

        # 이미지 입력 (Claude가 자동으로 Read 도구 사용)
        await client.query("이 다이어그램에 무엇이 있습니까? screenshot.png")
        async for message in client.receive_response():
            # 이미지 분석 처리
            pass

        # 혼합 콘텐츠가 있는 여러 입력
        inputs = [
            "diagram.png의 아키텍처를 분석하세요",
            "모범 사례와 비교하세요",
            "개선된 버전을 생성하세요"
        ]

        for prompt in inputs:
            await client.query(prompt)
            async for message in client.receive_response():
                # 각 응답 처리
                pass

asyncio.run(process_inputs())

에이전트 통합 예제

SRE 인시던트 대응 에이전트

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def investigate_incident(incident_description: str, severity: str = "medium"):
    """실시간 스트리밍을 사용한 자동화된 인시던트 대응 에이전트"""

    # 모니터링 도구용 MCP 서버 구성
    mcp_servers = {
        # 예제 구성 - 필요에 따라 주석을 해제하고 구성하세요:
        # "datadog": {
        #     "command": "npx",
        #     "args": ["-y", "@modelcontextprotocol/server-datadog"],
        #     "env": {"API_KEY": "your-datadog-key", "APP_KEY": "your-app-key"}
        # }
    }

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="당신은 SRE 전문가입니다. 문제를 체계적으로 진단하고 실행 가능한 솔루션을 제공하세요.",
            max_turns=6,
            allowed_tools=["Bash", "Read", "WebSearch", "mcp__datadog"],
            mcp_servers=mcp_servers
        )
    ) as client:
        # 인시던트 세부사항 전송
        prompt = f"인시던트: {incident_description} (심각도: {severity})"
        print(f"🚨 조사 중: {prompt}\n")
        await client.query(prompt)

        # 조사 과정 스트리밍
        investigation_log = []
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'type'):
                        if block.type == 'tool_use':
                            print(f"[{block.name}] ", end='')
                    if hasattr(block, 'text'):
                        text = block.text
                        print(text, end='', flush=True)
                        investigation_log.append(text)

            # 최종 결과 캡처
            if type(message).__name__ == "ResultMessage":
                return {
                    'analysis': ''.join(investigation_log),
                    'cost': message.total_cost_usd,
                    'duration_ms': message.duration_ms
                }

# 사용법
result = await investigate_incident("결제 API가 500 오류를 반환합니다", "high")
print(f"\n\n조사 완료. 비용: ${result['cost']:.4f}")

자동화된 보안 검토

import subprocess
import asyncio
import json
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def audit_pr(pr_number: int):
    """스트리밍 피드백을 사용한 풀 리퀘스트 보안 감사 에이전트"""
    # PR diff 가져오기
    pr_diff = subprocess.check_output(
        ["gh", "pr", "diff", str(pr_number)],
        text=True
    )

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="당신은 보안 엔지니어입니다. 취약점, 안전하지 않은 패턴 및 규정 준수 문제에 대해 이 PR을 검토하세요.",
            max_turns=3,
            allowed_tools=["Read", "Grep", "WebSearch"]
        )
    ) as client:
        print(f"🔍 PR #{pr_number} 감사 중\n")
        await client.query(pr_diff)

        findings = []
        async for message in client.receive_response():
            if hasattr(message, 'content'):
                for block in message.content:
                    if hasattr(block, 'text'):
                        # 발견 사항이 발견되는 대로 스트리밍
                        print(block.text, end='', flush=True)
                        findings.append(block.text)

            if type(message).__name__ == "ResultMessage":
                return {
                    'pr_number': pr_number,
                    'findings': ''.join(findings),
                    'metadata': {
                        'cost': message.total_cost_usd,
                        'duration': message.duration_ms,
                        'severity': 'high' if 'vulnerability' in ''.join(findings).lower() else 'medium'
                    }
                }

# 사용법
report = await audit_pr(123)
print(f"\n\n감사 완료. 심각도: {report['metadata']['severity']}")
print(json.dumps(report, indent=2))

다중 턴 법률 어시스턴트

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

async def legal_review():
    """지속적인 세션과 스트리밍을 사용한 법률 문서 검토"""

    async with ClaudeSDKClient(
        options=ClaudeCodeOptions(
            system_prompt="당신은 기업 변호사입니다. 상세한 법률 분석을 제공하세요.",
            max_turns=2
        )
    ) as client:
        # 같은 세션에서 다단계 검토
        steps = [
            "책임 조항에 대해 contract.pdf를 검토하세요",
            "GDPR 요구사항 준수를 확인하세요",
            "위험에 대한 경영진 요약을 생성하세요"
        ]

        review_results = []

        for step in steps:
            print(f"\n📋 {step}\n")
            await client.query(step)

            step_result = []
            async for message in client.receive_response():
                if hasattr(message, 'content'):
                    for block in message.content:
                        if hasattr(block, 'text'):
                            text = block.text
                            print(text, end='', flush=True)
                            step_result.append(text)

                if type(message).__name__ == "ResultMessage":
                    review_results.append({
                        'step': step,
                        'analysis': ''.join(step_result),
                        'cost': message.total_cost_usd
                    })

        # 요약
        total_cost = sum(r['cost'] for r in review_results)
        print(f"\n\n✅ 법률 검토 완료. 총 비용: ${total_cost:.4f}")
        return review_results

# 사용법
results = await legal_review()

Python 관련 모범 사례

주요 패턴

import asyncio
from claude_code_sdk import ClaudeSDKClient, ClaudeCodeOptions

# 항상 컨텍스트 매니저 사용
async with ClaudeSDKClient() as client:
    await client.query("이 코드를 분석하세요")
    async for msg in client.receive_response():
        # 스트리밍 메시지 처리
        pass

# 여러 에이전트를 동시에 실행
async with ClaudeSDKClient() as reviewer, ClaudeSDKClient() as tester:
    await asyncio.gather(
        reviewer.query("main.py 검토"),
        tester.query("main.py용 테스트 작성")
    )

# 오류 처리
from claude_code_sdk import CLINotFoundError, ProcessError

try:
    async with ClaudeSDKClient() as client:
        # 여기에 코드
        pass
except CLINotFoundError:
    print("CLI 설치: npm install -g @anthropic-ai/claude-code")
except ProcessError as e:
    print(f"프로세스 오류: {e}")

# 메타데이터와 함께 전체 응답 수집
async def get_response(client, prompt):
    await client.query(prompt)
    text = []
    async for msg in client.receive_response():
        if hasattr(msg, 'content'):
            for block in msg.content:
                if hasattr(block, 'text'):
                    text.append(block.text)
        if type(msg).__name__ == "ResultMessage":
            return {'text': ''.join(text), 'cost': msg.total_cost_usd}

IPython/Jupyter 팁

# Jupyter에서는 셀에서 직접 await 사용
client = ClaudeSDKClient()
await client.connect()
await client.query("data.csv 분석")
async for msg in client.receive_response():
    print(msg)
await client.disconnect()

# 재사용 가능한 헬퍼 함수 생성
async def stream_print(client, prompt):
    await client.query(prompt)
    async for msg in client.receive_response():
        if hasattr(msg, 'content'):
            for block in msg.content:
                if hasattr(block, 'text'):
                    print(block.text, end='', flush=True)

관련 리소스