Claude Platform Docs
Managed Agents자체 호스팅 샌드박스

자체 호스팅 샌드박스

Claude Managed Agents 세션을 자체 호스팅 샌드박스에서 실행하여 도구 실행, 파일, 네트워크 이그레스를 자체 인프라 내에 유지합니다.

기본적으로 Managed Agents는 Anthropic 관리형 클라우드 샌드박스 내에서 도구와 코드를 실행합니다. "Self-hosted sandboxes"(자체 호스팅 샌드박스)는 오케스트레이션을 Anthropic 측에 유지하면서 도구 실행을 사용자가 제어하는 인프라로 옮깁니다. 따라서 에이전트의 코드, 파일 시스템, "network egress"(네트워크 이그레스)가 사용자 환경을 벗어나지 않습니다.

도구 실행은 사용자의 호스트에서 이루어집니다. 에이전트가 읽고 쓰는 파일 시스템, 에이전트가 생성하는 프로세스, 에이전트가 접근할 수 있는 네트워크는 모두 사용자가 제어합니다. 다만 모델이 결과를 확인하고 다음 작업을 결정할 수 있도록, 도구 입력과 출력은 여전히 Anthropic의 "control plane"(컨트롤 플레인, Claude가 실행되는 곳)으로 전달됩니다. 에이전트의 스킬과 세션에 연결된 메모리 스토어의 내용은 Anthropic이 저장하며, 세션 동안 사용자의 샌드박스로 복사됩니다. 에이전트가 메모리 파일에 적용한 변경 사항은 스토어로 다시 동기화됩니다. 전체 데이터 흐름 경계는 보안 모델을 참조하세요.

클라우드 환경과의 차이점

클라우드 환경자체 호스팅 샌드박스
도구 실행 위치Anthropic 관리형 샌드박스사용자 인프라
네트워크 접근 범위Anthropic의 이그레스 제어사용자의 네트워크 정책
파일 및 GitHub 저장소 마운트Anthropic이 관리사용자가 관리
메모리 스토어Anthropic이 /mnt/memory/에 마운트/mnt/memory/에 다운로드되며 SDK 워커가 동기화
수명 주기Anthropic이 관리사용자가 관리

자체 호스팅은 에이전트가 네트워크 경계를 벗어날 수 없는 데이터를 다뤄야 하거나, 공개적으로 라우팅되지 않는 내부 서비스에 접근해야 하거나, 조직 자체의 규정 준수 및 감사 통제하에서 실행되어야 할 때 적합합니다.

Zero Data Retention 및 HIPAA BAA 적격성에 대해서는 API 및 데이터 보존을 참조하세요.

MCP 터널과 함께 사용해야 하는 경우

자체 호스팅은 에이전트의 코드가 실행되는 위치를 제어합니다. MCP 터널은 Anthropic이 사용자 네트워크의 MCP 서버에 접근하는 방식을 제어합니다. 두 기능은 서로 독립적입니다. Anthropic의 클라우드 샌드박스에서 실행되는 세션도 터널을 통해 비공개 MCP 서버에 접근할 수 있으며, 자체 호스팅 세션은 터널링된 MCP 서버와 공개 MCP 서버 중 어느 것이든 사용할 수 있습니다. 실행과 도구 접근을 모두 경계 내부에 유지하려면 두 기능을 함께 사용하세요. 터널을 실행하지 않고 네트워크 내부의 MCP 서버에서 에이전트에 도구를 제공하려면, 워커가 제공하는 커스텀 도구로 서버를 래핑할 수도 있습니다.

환경 워커

"Environment worker"(환경 워커)는 사용자가 자체 인프라에서 실행하는 프로세스입니다. Anthropic으로부터 도구 실행 요청을 받아 로컬에서 실행합니다. self_hosted 환경은 "work queue"(작업 큐) 역할을 합니다. 세션이 환경에 할당되면 Anthropic은 해당 세션을 "work item"(작업 항목)으로 큐에 넣습니다. 워커는 이 큐에서 작업 항목을 가져와(claim) 각 항목에 대한 실행 컨텍스트를 생성하고, 에이전트의 스킬(에이전트에 도메인별 전문성을 제공하는 재사용 가능한 파일 시스템 기반 리소스)을 다운로드하고, 도구 호출을 실행한 뒤 결과를 다시 게시합니다.

작업 항목은 환경의 큐를 폴링하여 가져옵니다. 지속적으로 폴링하는 상시 실행 워커를 사용하거나, session.status_run_started에서 깨어나 폴링을 시작하는 웹훅 트리거 핸들러를 사용할 수 있습니다.

CLI와 SDK 모두 사전 구축된 워커를 제공합니다. ant CLI는 상시 실행 패턴만 지원하며, SDK는 상시 실행과 웹훅 트리거 방식을 모두 지원합니다. 둘 다 구성할 수 있습니다. CLI 플래그는 레퍼런스의 자체 호스팅 워커를, SDK 옵션은 이 페이지의 SDK 헬퍼를 참조하세요. 더 세밀하게 제어하려면 Environments Work 엔드포인트를 직접 호출하여 자체 워커를 구현하세요.

샌드박스 파일 시스템

  • /workspace: 도구 실행 및 스킬 다운로드를 위한 시스템 기본 작업 디렉터리입니다. CLI의 --workdir 플래그는 기본적으로 현재 디렉터리를 사용하므로, 시스템 기본값과 맞추려면 --workdir /workspace를 전달하세요. 스킬은 <workdir>/skills/<name>/에 다운로드됩니다. 다른 작업 디렉터리를 사용하는 경우 Claude가 스킬 파일을 찾을 수 있도록 에이전트의 시스템 프롬프트를 업데이트하세요.
  • 출력: 자체 호스팅 환경에서는 세션의 시스템 프롬프트에 Anthropic 관리형 샌드박스에서 사용하는 /mnt/session/outputs 지침이 포함되지 않습니다. 따라서 최종 결과물은 에이전트가 샌드박스 파일 시스템에 기록하는 위치, 일반적으로 작업 디렉터리 아래에 저장됩니다.
  • /mnt/memory/: 세션에 연결된 메모리 스토어는 SDK 워커가 이곳에 구체화하며, 스토어마다 해당 스토어의 mount_path에 디렉터리 하나가 생성됩니다(예: /mnt/memory/user-preferences/). 워커는 세션을 가져올 때 이 디렉터리를 생성하고 세션이 끝나면 제거합니다. 메모리 스토어 사용을 참조하세요.

시작하기 전에

다음이 필요합니다.

  • 기존 에이전트. 에이전트가 없다면 먼저 빠른 시작을 완료하고 에이전트 ID를 기록해 두세요.
  • 정확히 해당 경로에 /bin/bash가 있는 Linux 호스트. 워커의 bash 도구는 PATH를 참조하지 않고 이를 직접 호출합니다. TypeScript SDK는 추가로 PATH에 unzip과 tar가 있어야 하며 Node.js 22 이상이 필요합니다. Python 및 Go SDK는 아카이브 추출에 표준 라이브러리를 사용하므로 추가 바이너리 요구 사항이 없습니다.
  • 워커 호스트에 설치된 ant CLI 또는 Anthropic SDK(Python, TypeScript 또는 Go).
  • 자격 증명: 환경 키(이후 단계에서 Console에서 생성)는 워커를 해당 큐에 인증합니다. Claude API 키는 워커 호스트 외부에서 세션을 생성하고 큐 통계를 읽는 데 사용됩니다. 키 생성은 Console에서만 가능합니다. 가져온 작업 항목에는 워커가 메모리 스토어를 마운트하는 데 사용하는 세션별 secret도 포함됩니다. 이 값은 직접 생성하지 않지만, 세션별 샌드박스 패턴에서는 사용자가 직접 샌드박스로 전달해야 합니다(세션당 하나의 샌드박스 실행 참조).
  • 메모리 스토어를 사용하는 경우, 준비된 호스트. 이 환경의 세션에 메모리 스토어를 연결할 예정이라면 워커를 시작하기 전에 워커 호스트에 /mnt/memory를 준비하세요. 호스트 준비를 참조하세요.
  1. 자체 호스팅 환경 생성

    Console에서: Workspace > Environments > New > Self-hosted

    또는 API를 통해:

    ant apply environment.yaml
    environment.yaml
    # yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json
    name: self-hosted
    config:
      type: self_hosted
  2. 환경 키 생성

    Console에서 환경을 열고 Generate environment key를 클릭하세요. 환경을 Console에서 생성했든 API로 생성했든 관계없이 키 생성은 Console에서만 가능합니다. 그런 다음 워커 호스트에서 환경 ID와 키를 내보내세요.

    export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..."
    export ANTHROPIC_ENVIRONMENT_ID="env_..."

워커 실행

가장 간단하게 설정하려면 상시 실행을 선택하세요. 장기 실행 프로세스가 큐를 지속적으로 폴링하며 아웃바운드 HTTPS만 필요합니다. 유휴 상태의 폴러를 실행하지 않으려면 웹훅 트리거 방식을 선택하세요. 이 방식에는 Anthropic이 접근할 수 있는 웹훅 엔드포인트가 필요합니다(엔드포인트 설정 및 서명 검증은 웹훅 참조).

  1. ant CLI 설치

    워커 호스트에서 다음을 실행하세요.

    Linux 환경에서는 릴리스 바이너리를 직접 다운로드하세요.

    VERSION=1.38.0
    OS=$(uname -s | tr '[:upper:]' '[:lower:]')
    case $(uname -m) in
      x86_64) ARCH=amd64 ;;
      aarch64) ARCH=arm64 ;;
    esac
    curl -fsSL "https://github.com/anthropics/anthropic-cli/releases/download/v${VERSION}/ant_${VERSION}_${OS}_${ARCH}.tar.gz" \
      | sudo tar -xz -C /usr/local/bin ant

    모든 릴리스는 GitHub 릴리스 페이지에서 확인할 수 있습니다.

  2. 워커 실행

    인프로세스

    ant beta:worker poll은 환경에 할당된 작업 항목을 가져오고, 스킬을 다운로드하고, 작업 디렉터리에서 도구 호출을 실행한 뒤 결과를 다시 게시합니다. 환경 변수에서 ANTHROPIC_ENVIRONMENT_KEY와 ANTHROPIC_ENVIRONMENT_ID를 읽습니다.

    ant beta:worker poll --workdir "/workspace"

    워커는 SIGTERM 또는 SIGINT를 받으면 정상적으로 종료됩니다. 진행 중인 도구 호출을 취소하고, 오류 결과를 게시하고, 작업 항목을 해제한 후 중지합니다.

    세션별 샌드박스

    더 강력한 격리(새로운 파일 시스템, 리소스 제한 또는 세션별 네트워크 제어)가 필요하다면 각 세션을 자체 샌드박스에서 실행하세요. ant가 설치되어 있고 ant beta:worker run을 엔트리포인트로 하는 이미지를 빌드하세요. 베이스 이미지는 /bin/bash를 제공해야 하며, curl은 빌드 시에만 사용됩니다. 샌드박스가 시작되면 환경 변수에서 세션 세부 정보를 읽고, 해당 세션을 처리한 뒤 종료합니다.

    FROM your-base-image
    ARG ANT_VERSION=1.38.0
    ARG TARGETARCH
    RUN ARCH=$([ "$TARGETARCH" = "arm64" ] && echo arm64 || echo amd64) && \
        curl -fsSL "https://github.com/anthropics/anthropic-cli/releases/download/v${ANT_VERSION}/ant_${ANT_VERSION}_linux_${ARCH}.tar.gz" \
          | tar -xz -C /usr/local/bin ant
    WORKDIR /workspace
    VOLUME /workspace
    ENTRYPOINT ["ant", "beta:worker", "run"]

    그런 다음 세션 세부 정보를 새 샌드박스로 전달하는 스폰 스크립트를 작성하세요. 폴러는 스크립트의 환경에 ANTHROPIC_SESSION_ID, ANTHROPIC_WORK_ID, ANTHROPIC_ENVIRONMENT_ID, ANTHROPIC_ENVIRONMENT_KEY를 주입하고, 가져온 작업 항목을 JSON 형식으로 스크립트의 표준 입력에 기록합니다. 여기에는 Anthropic이 발급한 경우 작업 항목의 세션별 secret도 포함됩니다. ANTHROPIC_BASE_URL은 선택 사항이며 폴러 호스트에 설정된 경우에만 전달되고, 기본 API 엔드포인트를 재정의합니다. 예제에서 /host/outputs는 사용자가 선택한 호스트 디렉터리로, 샌드박스의 작업 디렉터리(/workspace)에 바인드 마운트되어 샌드박스가 종료된 후 세션 결과물을 가져올 수 있습니다. 자체 호스팅 환경에서 에이전트는 /mnt/session/outputs가 아닌 작업 디렉터리 아래에 결과물을 기록하므로(샌드박스 파일 시스템 참조), 작업 디렉터리를 마운트해야 결과물을 확보할 수 있습니다. 이 마운트에는 다운로드된 skills/ 트리와 에이전트가 생성하는 중간 파일도 포함됩니다.

    #!/bin/bash
    # spawn.sh: 할당된 작업 항목마다 한 번씩 호출됩니다
    mkdir -p "/host/outputs/$ANTHROPIC_SESSION_ID"
    exec docker run --rm \
      -e ANTHROPIC_SESSION_ID -e ANTHROPIC_ENVIRONMENT_KEY \
      -e ANTHROPIC_WORK_ID -e ANTHROPIC_ENVIRONMENT_ID -e ANTHROPIC_BASE_URL \
      -v "/host/outputs/$ANTHROPIC_SESSION_ID":/workspace \
      your-image

    ant beta:worker run 엔트리포인트는 메모리 스토어를 마운트하지 않습니다. 이 환경의 세션에 메모리 스토어를 연결하는 경우, 폴러는 그대로 두되 세션별 이미지를 SDK 워커 기반으로 빌드하고, 세션당 하나의 샌드박스 실행에 나온 것처럼 작업 항목의 secret을 샌드박스로 전달하도록 스폰 스크립트를 확장하세요.

    스크립트를 지정하여 폴러를 시작하세요.

    ant beta:worker poll --on-work ./spawn.sh

SDK 헬퍼

SDK는 제어 수준이 서로 다른 세 가지 헬퍼를 제공합니다. 대부분의 사용 사례는 EnvironmentWorker로 충분하며, 세션별 프로세스를 직접 실행하거나 이미 가져온 세션에 대해 도구를 실행해야 할 때는 하위 수준 헬퍼를 사용하세요.

  • EnvironmentWorker: 바로 사용할 수 있는 워커입니다. 폴링, 설정, 실행을 처음부터 끝까지 처리합니다.
    • .run(): 무기한 실행되며 세션이 도착하는 대로 처리합니다.
    • .handle_item(): 가져온 단일 작업 항목을 처리하고 종료합니다. 작업, 세션, 환경 식별자를 명시적으로 전달하거나, ant beta:worker poll --on-work가 생성하는 프로세스에 설정하는 ANTHROPIC_* 변수를 읽도록 할 수 있습니다. 세션이 메모리 스토어를 마운트할 수 있도록 하려면 작업 항목의 secret도 work_secret으로 전달하거나 ANTHROPIC_WORK_SECRET을 설정하세요. ant beta:worker poll --on-work는 이 변수를 설정하지 않으므로, 세션당 하나의 샌드박스 실행에 나온 것처럼 스크립트의 표준 입력에 기록되는 작업 항목 JSON에서 secret을 읽으세요.
    • memory_sync_interval 및 memory_sync_deletions: 세션 실행 중 연결된 메모리 스토어가 서버와 조정되는 빈도, 그리고 에이전트가 로컬에서 삭제한 파일을 스토어에서도 삭제할지 여부입니다. 단위, 기본값, 메모리 지원을 비활성화하는 방법은 동기화 구성을 참조하세요.
  • work.poller(): 사용자를 대신해 작업 큐를 폴링하고 가져온 각 세션을 전달합니다. 예를 들어 도구를 인프로세스로 실행하는 대신 샌드박스를 실행하는 등, 각 세션에 대해 수행할 작업을 직접 결정하려는 경우에 사용하세요.
    • drain: 새 작업을 기다리지 않고 큐가 비면 폴링을 중지할지 여부입니다.
    • block_ms: 반환하기 전에 작업이 도착하기를 기다리는 시간(밀리초)입니다. 1에서 999 사이여야 합니다(폴링당 대기 시간이며, 헬퍼가 자동으로 다시 폴링합니다). 비차단 확인을 하려면 None을 전달하세요. 매개변수를 생략하면 기본값인 999ms 롱 폴링이 사용됩니다.
    • reclaim_older_than_ms: 가져왔지만 이 밀리초 내에 확인(acknowledge)되지 않은 작업 항목을 다시 가져옵니다.
    • auto_stop: 루프 본문이 각 작업 항목 처리를 마치면 해당 항목에 대한 중지 신호를 게시할지 여부입니다. 작업 항목을 실행하는 주체가 직접 중지 신호를 게시하는 경우에는 이 옵션을 끄세요. handle_item()이 직접 게시하므로, 이 페이지의 웹훅 핸들러처럼 가져온 항목을 handle_item()에 전달할 때는 false로 설정하세요. 중지 호출을 담당하는 샌드박스를 실행하는 경우에도 마찬가지입니다.
  • client.beta.sessions.events.tool_runner(): 세션 ID와 도구 목록이 주어지면 단일 세션에 대한 도구 호출을 실행합니다. 이미 작업을 가져왔고 실행 계층만 필요한 경우에 사용하세요.

세션별 프로세스를 직접 실행하려는 경우, 예를 들어 가져온 각 세션마다 샌드박스를 띄우려는 경우에는 work.poller()를 직접 사용하세요.

import asyncio
import os

from anthropic import AsyncAnthropic
from anthropic.types.beta.environments import BetaSelfHostedWork

SANDBOX_ENV = (
    "ANTHROPIC_ENVIRONMENT_ID",
    "ANTHROPIC_ENVIRONMENT_KEY",
    "ANTHROPIC_WORK_ID",
    "ANTHROPIC_SESSION_ID",
    "ANTHROPIC_WORK_SECRET",
    "ANTHROPIC_BASE_URL",  # forwarded only when set on this host
)


async def launch_container(work: BetaSelfHostedWork) -> None:
    print(f"claimed session {work.data.id}")
    # `docker run`을 자체 샌드박스 실행기로 바꾸세요. 환경
    # 키(API 키는 절대 전달하지 마세요)와 작업 항목의 세션별 시크릿을 전달하세요. 내부의
    # 워커가 세션의 메모리 저장소를 마운트하려면 이 시크릿이 필요합니다.
    env = os.environ | {
        "ANTHROPIC_WORK_ID": work.id,
        "ANTHROPIC_SESSION_ID": work.data.id,
        "ANTHROPIC_WORK_SECRET": work.secret or "",
    }
    forward = [arg for name in SANDBOX_ENV for arg in ("-e", name)]
    launcher = await asyncio.create_subprocess_exec(
        "docker", "run", "--rm", "--detach", *forward, "your-sdk-worker-image", env=env
    )
    await launcher.wait()


async def main() -> None:
    environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
    environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
    async with AsyncAnthropic(auth_token=environment_key) as client:
        async for work in client.beta.environments.work.poller(
            environment_id=environment_id,
            environment_key=environment_key,
            auto_stop=False,  # the launched sandbox owns the stop call
        ):
            await launch_container(work)


asyncio.run(main())

샌드박스를 실행하는 주체는 내부의 워커가 세션의 메모리 스토어를 마운트할 수 있도록 세션, 작업, 환경 식별자와 함께 가져온 작업 항목의 secret을 샌드박스로 전달해야 합니다(예: ANTHROPIC_WORK_SECRET으로). 세션당 하나의 샌드박스 실행을 참조하세요.

AgentToolContext는 도구 호출을 위한 실행 컨텍스트입니다. 작업 디렉터리와 경로 정책을 정의하며, 세션의 스킬을 다운로드할 수 있습니다. 파일 도구(read, write, edit, glob, grep)는 작업 디렉터리와 allowed_roots에 나열된 디렉터리로 제한되며, write와 edit는 추가로 read_only_roots 아래의 경로를 거부합니다. EnvironmentWorker는 세션의 메모리 스토어 디렉터리를 이 목록에 직접 추가합니다. 이 제한은 파일 도구에만 적용되는 안전장치일 뿐 샌드박스가 아니며, bash는 제한하지 않습니다. beta_agent_toolset_20260401(env)는 AgentToolContext를 받아 표준 도구 구현(bash, read, write, edit, glob, grep)을 반환합니다.

EnvironmentWorker 사용 시: 둘 다 자동으로 관리됩니다. 도구 목록을 사용자 지정하려면 tools 팩토리를 전달하세요.

EnvironmentWorker(client, ..., tools=lambda env: [beta_bash_tool(env), my_custom_tool])

work.poller() 및 tool_runner() 사용 시: client.beta.sessions.events.tool_runner()에 도구 목록을 tools로 전달하세요. 이 목록을 만들려면 AgentToolContext를 직접 설정하고 beta_agent_toolset_20260401(env)를 호출하세요.

from anthropic.lib.tools.agent_toolset import (
    AgentToolContext,
    beta_agent_toolset_20260401,
)

async with AgentToolContext(
    workdir="/workspace", client=client, session_id=work.data.id
) as env:
    # skills downloaded to /workspace/skills/<name>/
    tools = beta_agent_toolset_20260401(env)

워커 연결 확인

별도의 셸에서 ANTHROPIC_API_KEY를 Claude API 키(환경 키가 아님)로 설정한 상태로 workers_polling이 1 이상인지 확인하세요.

ant beta:environments:work stats --environment-id "$ANTHROPIC_ENVIRONMENT_ID"

workers_polling이 계속 0이라면 워커가 큐에 도달하지 못하고 있는 것입니다. 워커 호스트에 ANTHROPIC_ENVIRONMENT_KEY와 ANTHROPIC_ENVIRONMENT_ID가 설정되어 있는지 확인하세요. 전체 통계 응답과 다른 언어 예제는 큐 깊이 읽기를 참조하세요.

세션 시작

워커가 실행되면 해당 환경을 대상으로 하는 세션을 생성하세요. AGENT_ID를 시작하기 전에에서 기록해 둔 에이전트 ID로 설정하세요. 세션은 환경의 작업 큐에 들어가 워커가 가져갈 때까지 대기합니다. 연결된 워커가 없으면 세션은 실패하지 않고 큐에 남아 있습니다.

Anthropic은 자체 호스팅 샌드박스에 파일이나 GitHub 저장소를 마운트하지 않습니다. 세션별 파일을 사용할 수 있게 하려면 세션의 metadata 필드에 파일 참조(예: S3 경로 또는 커밋 SHA)를 전달하세요. 가져온 작업 항목에는 세션의 메타데이터가 포함되지 않지만 세션 ID는 포함됩니다. 스폰 스크립트나 --on-work 핸들러가 세션을 조회(GET /v1/sessions/{session_id})하여 metadata 필드를 읽은 다음, 도구 실행이 시작되기 전에 파일을 작업 디렉터리에 준비합니다.

session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    metadata={"input_file": "s3://my-bucket/data.csv"},
)

전체 CLI 플래그 목록은 레퍼런스의 자체 호스팅 워커를, SDK 헬퍼 옵션은 SDK 헬퍼를 참조하세요.

메모리 스토어 사용

자체 호스팅 환경의 세션은 클라우드 환경의 세션과 똑같은 방식으로 메모리 스토어를 연결합니다. 세션에 메모리 스토어 연결에 나온 것처럼 세션을 생성할 때 resources에 나열하면 됩니다. 세션 하나에는 최대 8개의 메모리 스토어를 연결할 수 있습니다. 자체 호스팅 환경에서는 Anthropic의 인프라가 아닌 SDK 워커가 에이전트를 위해 각 스토어를 구체화하므로, 메모리 스토어를 사용하려면 Python, TypeScript 또는 Go SDK의 EnvironmentWorker(또는 해당 handle_item() 메서드)가 필요합니다.

ant CLI 워커(ant beta:worker poll 및 ant beta:worker run)는 메모리 스토어를 마운트하지 않습니다. CLI 폴러와 메모리 스토어를 함께 사용하려면 세션당 하나의 샌드박스 실행에 설명된 대로 세션별 샌드박스 내에서 SDK 워커를 실행하세요.

Claude Platform on AWS의 자체 호스팅 환경에서는 세션에 메모리 스토어를 연결할 수 없습니다.

워커의 메모리 처리 방식

워커는 메모리 스토어가 연결된 세션의 작업 항목을 가져오면 다음을 수행합니다.

  1. 작업 항목의 세션별 secret으로 인증하여, 연결된 각 스토어를 워커 호스트의 해당 mount_path에 다운로드합니다. mount_path는 클라우드 세션이 사용하는 것과 동일한 /mnt/memory/ 아래의 디렉터리이며(예: "User Preferences"라는 이름의 스토어는 /mnt/memory/user-preferences/), 세션의 시스템 프롬프트가 이를 에이전트에 설명합니다.
  2. 이 디렉터리들을 파일 도구의 허용 루트에 추가하고, access: "read_only"로 연결된 스토어의 디렉터리는 읽기 전용 루트에 추가합니다. 따라서 에이전트는 작업 디렉터리에서 사용하는 것과 동일한 read, write, edit, glob, grep 도구로 메모리를 다룹니다.
  3. 도구 호출 후 동기화 간격(기본값 15초)마다 최대 한 번씩 로컬 및 원격 변경 사항을 조정합니다. 스토어에서 변경된 메모리는 디스크에 기록되고, 에이전트가 변경한 파일은 스토어에 업로드됩니다.
  4. 세션이 끝나면 최종 동기화를 실행하고, 아직 대기 중인 업로드를 최대 30초 동안 플러시한 다음, 생성한 디렉터리를 제거합니다. 세션 실행 중에 취소된 워커는 최종 동기화를 건너뛰지만, 종료하기 전에 변경된 파일을 업로드하고 디렉터리를 제거합니다.

Anthropic 측의 메모리 스토어가 계속해서 신뢰할 수 있는 원본(source of truth)입니다. 메모리 버전, 수정(redaction), Console에서의 메모리 조회 및 편집은 클라우드 세션과 동일하게 작동하며, 에이전트의 메모리 읽기 및 쓰기는 이벤트 스트림에 일반 도구 이벤트로 표시됩니다. 각 워커는 일정 간격으로 동기화하므로, 한 세션에서 기록한 변경 사항은 두 세션이 모두 동기화된 후에야 실행 중인 다른 세션에 표시됩니다. 기본 간격에서는 일반적으로 1분보다 훨씬 짧게 걸립니다. 클라우드 샌드박스의 세션은 서로의 변경 사항을 거의 즉시 확인합니다.

각 스토어 디렉터리에는 디렉터리를 해당 스토어에 연결하는 .anthropic-memory-store라는 마커 파일이 있습니다. 이 파일을 그대로 두세요. 워커는 마커가 없거나 변경된 디렉터리를 동기화하지 않습니다.

호스트 준비

자체 호스팅 샌드박스의 메모리 스토어에는 워커 호스트(시작하기 전에의 Linux 호스트)에 POSIX 파일 시스템이 필요합니다. 워커가 메모리 파일을 열 때 O_NOFOLLOW가 필요하므로 Windows 호스트는 지원되지 않습니다. 대소문자만 다른 메모리 경로가 충돌하지 않도록 대소문자를 구분하는 파일 시스템을 권장합니다.

워커를 시작하기 전에 상위 디렉터리를 생성하고 워커를 실행하는 사용자가 쓸 수 있도록 설정하세요.

sudo mkdir -p /mnt/memory && sudo chown "$USER" /mnt/memory

스토어별 디렉터리는 직접 생성하지 마세요. 워커는 세션이 시작될 때 각 스토어의 mount_path 디렉터리(예: /mnt/memory/user-preferences)를 생성하고, 해당 경로에 이미 무언가가 존재하면 세션 작업 시작을 거부하며, 세션이 끝나면 디렉터리를 제거합니다. 이에 따라 두 가지 운영 규칙이 적용됩니다.

  • 세션들이 동일한 스토어를 연결하는 경우 파일 시스템당 하나의 세션을 실행하세요. 두 세션 모두 동일한 경로가 필요하므로, 한 호스트에서 두 세션이 동시에 같은 스토어를 마운트할 수 없습니다. 세션당 하나의 샌드박스 실행에 설명된 대로 각 세션에 자체 샌드박스를 제공하면 이 규칙을 충족합니다.
  • 워커를 정상적으로 중지하세요. 세션 실행 중에 워커를 중지할 때, EnvironmentWorker는 강제 종료(kill)가 아니라 취소(cancel)된 경우에만 세션의 변경된 메모리 파일을 업로드하고 스토어 디렉터리를 제거합니다. 강제 종료된 프로세스는 정리 작업을 실행하지 않으며, 워커는 자체적으로 시그널 핸들러를 설치하지 않습니다. 워커를 실행하는 프로세스에서 SIGTERM과 SIGINT를 취소에 연결하세요. TypeScript에서는 워커에 전달한 signal을 중단(abort)하고, Go에서는 컨텍스트를 취소하고, Python에서는 run() 또는 handle_item()을 실행하는 태스크를 취소하세요. 이 페이지의 독립 실행형 워커처럼 워커 자체가 프로세스인 경우에는 시그널 핸들러에서 이를 수행하고, 워커가 웹훅 핸들러 내부에서 실행되는 경우에는 서버의 시그널을 가로채서는 안 되므로 서버 자체의 종료 훅에서 수행하세요. 그런 다음 SIGTERM으로 워커를 중지하고, 최종 업로드에 그만큼 시간이 걸릴 수 있으므로 강제 종료하기 전에 최소 30초의 종료 시간을 주세요. 정리 작업이 실행되기 전에 워커가 강제 종료되었다면, 해당 스토어를 연결하는 다음 세션 전에 /mnt/memory/ 아래에 남아 있는 스토어 디렉터리를 제거하세요. 그 안에서 동기화되지 않은 편집 내용은 손실됩니다.

세션당 하나의 샌드박스 실행

워커 실행에서 설명한 세션당 샌드박스 패턴은 각 세션에 새 파일 시스템을 제공합니다. 이는 여러 세션이 같은 스토어를 연결할 때 호스트 준비에서 요구하는 조건입니다. 호스트의 폴러로는 계속 ant beta:worker poll --on-work(또는 SDK의 work.poller())를 사용하세요.

해당 섹션에 나온 ant beta:worker run 엔트리포인트는 메모리 스토어를 마운트하지 않습니다. 따라서 세션별 이미지는 SDK 워커를 기반으로 빌드하세요. 이 이미지의 엔트리포인트는 EnvironmentWorker를 생성하고 handle_item()을 호출합니다. 이 메서드는 ANTHROPIC_* 변수에서 세션, 작업, 환경 식별자를 읽고, ANTHROPIC_WORK_SECRET에서 작업 항목의 세션별 secret을 읽습니다. 시크릿을 work_secret으로 명시적으로 전달할 수도 있습니다.

import asyncio
import contextlib
import os
import signal
from anthropic import AsyncAnthropic
from anthropic.lib.environments import EnvironmentWorker


async def main() -> None:
    async with AsyncAnthropic(auth_token=os.environ["ANTHROPIC_ENVIRONMENT_KEY"]) as client:
        worker = EnvironmentWorker(client, workdir="/workspace")
        # With no arguments, handle_item() reads the ANTHROPIC_* variables the spawn
        # script forwarded, including ANTHROPIC_WORK_SECRET.
        task = asyncio.create_task(worker.handle_item())
        # Cancelling the task when the container is stopped lets the worker upload
        # changed memory files and remove the store directories before it exits.
        loop = asyncio.get_running_loop()
        for signum in (signal.SIGINT, signal.SIGTERM):
            loop.add_signal_handler(signum, task.cancel)
        with contextlib.suppress(asyncio.CancelledError):
            await task


asyncio.run(main())

ant beta:worker poll --on-work는 자신이 실행하는 스크립트에 ANTHROPIC_WORK_SECRET을 설정하지 않습니다. 따라서 실행 스크립트가 표준 입력으로 받은 작업 항목 JSON에서 시크릿을 읽어 샌드박스에 전달해야 합니다.

#!/bin/bash
# spawn.sh: 클레임한 작업 항목마다 한 번씩 호출됩니다
# 클레임한 작업 항목은 stdin을 통해 JSON으로 전달됩니다. 이 항목의 secret은
# 메모리 스토어 엔드포인트에서 요구하는 세션별 자격 증명입니다.
ANTHROPIC_WORK_SECRET="$(jq -r '.secret // empty')"
export ANTHROPIC_WORK_SECRET
mkdir -p "/host/outputs/$ANTHROPIC_SESSION_ID"
exec docker run --rm \
  -e ANTHROPIC_SESSION_ID -e ANTHROPIC_ENVIRONMENT_KEY \
  -e ANTHROPIC_WORK_ID -e ANTHROPIC_ENVIRONMENT_ID -e ANTHROPIC_BASE_URL \
  -e ANTHROPIC_WORK_SECRET \
  -v "/host/outputs/$ANTHROPIC_SESSION_ID":/workspace \
  your-sdk-worker-image

SDK의 work.poller()로 작업을 가져오는 경우에도, 가져온 각 항목의 secret을 같은 방식으로 실행하는 샌드박스에 전달하세요. 시크릿은 해당 세션을 처리하는 샌드박스에만 전달하고, 절대 로그에 기록하지 마세요.

샌드박스 이미지에도 쓰기 가능한 /mnt/memory가 있어야 합니다(호스트 준비 참조). 각 샌드박스는 하나의 세션만 처리한 뒤 폐기되므로 남은 디렉터리를 정리할 필요가 없습니다. 또한 워커가 샌드박스 종료 전에 메모리 디렉터리의 내용을 스토어에 업로드하므로, 이 디렉터리를 호스트에 바인드 마운트할 필요도 없습니다. 세션이 끝나기 전에 컨테이너를 중지해야 한다면, 업로드가 실행될 수 있도록 강제 종료하지 말고 엔트리포인트가 취소로 변환하는 시그널을 보내세요(호스트 준비 참조). 업로드를 마칠 시간도 충분히 주어야 합니다. Docker는 기본적으로 중지 시그널을 보낸 뒤 10초가 지나면 SIGKILL을 보냅니다. 따라서 docker run의 --stop-timeout 또는 오케스트레이터의 종료 유예 기간을 사용해 이 제한을 호스트 준비에서 요구하는 최소 30초 이상으로 늘리세요.

동기화 구성

두 가지 EnvironmentWorker 옵션으로 메모리 동작을 제어합니다.

  • memory_sync_interval(Python에서는 초, TypeScript에서는 밀리초, Go에서는 duration): 세션이 실행되는 동안 연결된 스토어를 서버와 얼마나 자주 조정(reconcile)할지 지정합니다. 기본값은 15초이며 최솟값은 5초입니다. 간격을 짧게 하면 다른 세션이 오래된 메모리를 보게 되는 시간이 줄어들지만, 메모리 스토어 요청이 늘어납니다. Python에서 None, TypeScript에서 null, Go에서 음수 duration을 지정하면 메모리 지원이 완전히 비활성화됩니다. 이 경우 워커는 스토어를 다운로드하거나 동기화하지 않습니다. 메모리 스토어가 연결된 세션은 시스템 프롬프트에 여전히 스토어가 설명되어 있는데도 스토어 없이 실행됩니다. 따라서 메모리 스토어를 연결하지 않는 세션만 처리하는 워커에서만 메모리 지원을 비활성화하세요. 메모리 지원이 활성화된 상태에서, 스토어가 연결된 세션의 작업 항목이 세션별 secret 없이 도착하면 메모리 없이 실행되지 않고 실패합니다(메모리 마운트 문제 해결 참조).
  • memory_sync_deletions: 에이전트가 로컬에서 삭제한 파일을 스토어에서도 삭제할지 지정합니다. Python과 TypeScript에서는 "enabled"(기본값), "log_only", "disabled" 중 하나를 사용하고, Go에서는 상수 environments.MemorySyncDeletionsEnabled(제로 값), environments.MemorySyncDeletionsLogOnly, environments.MemorySyncDeletionsDisabled 중 하나를 사용합니다. 활성화 모드에서는 이후 동기화에서 파일이 여전히 없는 것이 확인되면 워커가 스토어에서 해당 메모리를 삭제합니다. 로그 전용 모드에서는 같은 검사를 실행하되 삭제했을 항목을 로그에만 기록합니다. 이를 통해 활성화 모드를 신뢰하기 전에 워커가 무엇을 삭제할지 미리 확인할 수 있습니다. 비활성화 모드에서는 스토어에서 아무것도 삭제하지 않습니다. 이 설정은 업로드와 다운로드에는 영향을 주지 않습니다.

이 옵션은 워커를 생성하는 곳에서 설정하세요. EnvironmentWorker 생성자를 사용하거나, Python과 TypeScript에서는 웹훅 핸들러가 사용하는 client.beta.environments.work.worker() 팩토리를 사용할 수 있습니다.

예를 들어, 10초마다 동기화하고 워커가 수행했을 삭제를 로그에만 기록하려면 다음과 같이 설정합니다.

worker = EnvironmentWorker(
    client,
    environment_id=environment_id,
    environment_key=environment_key,
    workdir="/workspace",
    memory_sync_interval=10,  # seconds
    memory_sync_deletions="log_only",
)

읽기 전용 스토어와 충돌

access: "read_only"로 연결된 스토어의 경우, write 및 edit 도구는 해당 디렉터리 안의 파일 변경을 거부하며, 워커는 그 디렉터리에서 아무것도 업로드하지 않습니다.

반면 bash를 통한 변경이나, 샌드박스에서 제공하는 사용자 정의 도구 또는 MCP 서버를 통한 변경은 로컬에서 차단되지 않습니다. 이러한 변경은 스토어에 동기화되지 않으며, 해당 메모리에 대한 다음 원격 변경이 이를 덮어씁니다.

세션 동안 로컬 사본 자체가 변경되지 않아야 한다면, 해당 에이전트의 bash 도구를 비활성화하고 샌드박스 파일 시스템에 쓰는 사용자 정의 도구를 제공하지 마세요. 스토어 경로를 읽기 전용으로 마운트해서는 안 됩니다. 워커가 직접 디렉터리를 생성하고 다운로드한 메모리를 그 안에 써야 하기 때문입니다.

"Conflict"(충돌)는 스토어 쪽이 우선하도록 해결됩니다. 세션이 마지막으로 동기화한 이후 스토어에서도 변경된 메모리 파일을 에이전트가 변경하면, 워커는 다음 동기화에서 다음과 같이 처리합니다.

  • 스토어의 버전을 유지합니다.
  • 로컬 파일을 스토어 버전으로 덮어씁니다.
  • 경고를 로그에 기록합니다.

이때 write 및 edit 도구 호출 자체는 성공하며, 에이전트에는 오류가 전달되지 않습니다. 에이전트의 변경이 여전히 필요하다면, 동기화 후 파일을 다시 읽고 변경을 다시 적용하면 됩니다.

메모리 마운트 문제 해결

워커는 마운트 실패와 백그라운드 동기화 실패를 세션에 보고하지 않고 로그에 기록합니다. 에이전트에 전달되는 것은 읽기 전용 거부뿐이며, 이는 도구 오류로 전달됩니다(읽기 전용 스토어와 충돌 참조). 워커가 세션을 가져올 때 메모리 스토어를 마운트할 수 없으면 워커는 작업 항목을 실패 처리합니다. 이 경우 세션은 오류 이벤트를 내보내지 않고 유휴 상태로 남습니다.

증상원인해결 방법
워커 로그에 the work item carried no sessions token(Go에서는 ErrSessionMemoryNoToken 오류)이 기록되고 작업 항목이 실패합니다.작업 항목의 세션별 secret이 워커에 전달되지 않았습니다. 조직에서 자체 호스팅 샌드박스의 메모리 스토어가 활성화되지 않았거나, 실행 스크립트가 시크릿을 샌드박스에 전달하지 않았습니다.세션당 샌드박스 패턴에서는 세션당 하나의 샌드박스 실행에 나온 대로 ANTHROPIC_WORK_SECRET을 샌드박스에 전달하세요. 워커가 하나의 프로세스에서 폴링과 세션 실행을 모두 수행하는데도 이 로그가 기록된다면 지원팀에 문의하세요.
워커 로그에 something already exists at the memory store's path가 기록됩니다.이전 세션에서 남은 디렉터리가 있습니다. 대개 정리 작업이 실행되기 전에 워커가 강제 종료된 세션의 디렉터리입니다.로그 줄에 표시된 잔여 디렉터리를 제거하세요. 그 안에서 동기화되지 않은 편집 내용은 손실됩니다.
워커 로그에 cannot create the memory store's folder와 the worker host must make this mount path writable이 기록됩니다.워커를 실행하는 사용자가 /mnt/memory 아래에 디렉터리를 생성할 수 없습니다./mnt/memory를 생성하고 해당 사용자에게 chown하세요. 호스트 준비를 참조하세요.
워커가 세션을 가져온 직후, 세션이 오류 이벤트 없이 requires_action 중지 사유와 함께 idle 상태로 머뭅니다.앞의 원인 중 하나로 메모리 스토어를 마운트할 수 없어 워커가 작업 항목을 실패 처리했습니다.호스트에서 원인을 해결한 다음 user.interrupt 이벤트를 보내세요. 세션의 작업이 다시 대기열에 추가되고, 다음에 이를 가져가는 워커가 마운트를 재시도합니다.

샌드박스에서 사용자 정의 도구 제공

"Custom tools"(사용자 정의 도구)는 사용자의 코드가 직접 실행하는 도구입니다. 에이전트가 agent.custom_tool_use 이벤트를 내보내면, 이에 대응하는 user.custom_tool_result를 받을 때까지 기다립니다. 워커가 바로 그 코드가 될 수 있습니다. 워커는 샌드박스 안에서 실행되므로, 도구는 샌드박스에 구성한 내부 서비스, 자격 증명, 네트워크 이그레스에만 접근할 수 있습니다. 사용자 정의 도구 결과는 환경 키로 게시할 수 있으므로, Claude API 키를 워커 호스트에 둘 필요가 없습니다.

  1. 에이전트에 도구 선언

    에이전트의 tools에 custom 항목을 추가하고, 그 name을 워커가 등록하는 도구 이름과 일치시키세요. 전체 선언 형식은 사용자 정의 도구를 참조하세요.

    {
      "type": "custom",
      "name": "get_order_status",
      "description": "Look up an order in the internal fulfillment system by order ID.",
      "input_schema": {
        "type": "object",
        "properties": {
          "order_id": { "type": "string", "description": "The order ID" }
        },
        "required": ["order_id"]
      }
    }
  2. 워커에 구현 등록

    워커의 tools 팩토리(SDK 헬퍼 참조)를 통해 내장 도구 세트와 함께 도구를 전달하세요.

    import asyncio
    import os
    from anthropic import AsyncAnthropic, beta_async_tool
    from anthropic.lib.environments import EnvironmentWorker
    from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401
    
    
    @beta_async_tool
    async def get_order_status(order_id: str) -> str:
        """Look up an order in the internal fulfillment system by order ID."""
        # Runs on the worker host: call anything the sandbox can reach.
        return f"Order {order_id}: shipped"
    
    
    async def main() -> None:
        environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
        environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
        async with AsyncAnthropic(auth_token=environment_key) as client:
            await EnvironmentWorker(
                client,
                environment_id=environment_id,
                environment_key=environment_key,
                workdir="/workspace",
                tools=lambda env: [*beta_agent_toolset_20260401(env), get_order_status],
            ).run()
    
    
    asyncio.run(main())

워커는 자신에게 등록된 도구에만 응답합니다. 에이전트에 선언되었지만 어떤 워커나 클라이언트에도 등록되지 않은 사용자 정의 도구가 호출되면, 누군가 결과를 게시할 때까지 세션은 requires_action 중지 사유와 함께 일시 중지된 상태로 남습니다. 이벤트 흐름은 사용자 정의 도구 호출 처리를 참조하세요.

MCP 서버를 사용자 정의 도구로 래핑

MCP 커넥터는 Anthropic 측에서 MCP 서버에 연결합니다. 따라서 서버는 Anthropic이 직접 또는 MCP 터널을 통해 접근할 수 있는 HTTP 엔드포인트를 노출해야 합니다. 사용자의 네트워크에서만 접근할 수 있는 서버를 사용하려면, 대신 워커를 MCP 클라이언트로 만들고 서버의 도구를 사용자 정의 도구로 선언하세요. 이 경우 MCP 서버는 네트워크 외부로부터의 인바운드 연결이 필요하지 않습니다. Anthropic이 받는 것은 에이전트에 선언한 도구 정의, 각 호출의 입력, 워커가 게시하는 결과뿐입니다. 런타임에 모델은 래핑된 도구를 다른 사용자 정의 도구와 똑같이 호출합니다.

  1. 에이전트가 agent.custom_tool_use 이벤트를 내보냅니다.
  2. 샌드박스 안의 워커가 열려 있는 MCP 세션을 통해 네트워크의 서버로 호출을 전달합니다.
  3. 워커가 서버의 응답을 user.custom_tool_result로 게시합니다.

SDK의 클라이언트 측 MCP 헬퍼는 서버의 도구를 워커가 받아들이는 실행 가능한 도구로 변환합니다. Anthropic SDK와 함께 MCP SDK를 설치하세요(pip install "anthropic[mcp]" "mcp>=1.24", npm install @modelcontextprotocol/sdk, go get github.com/modelcontextprotocol/go-sdk). 예제는 인증 없이 연결합니다. 자격 증명을 보내려면 MCP 전송 계층에 전달하는 HTTP 클라이언트 또는 요청 옵션(http_client)을 구성하세요.

  1. 에이전트에 서버의 도구 선언

    MCP 서버의 도구 목록을 가져와 각 도구를 custom 도구로 선언하세요. MCP의 name, description, inputSchema는 사용자 정의 도구의 필드에 일대일로 대응합니다. 서버가 도구 목록을 페이지로 나누어 반환한다면 모든 페이지의 도구를 선언하세요. 워커도 같은 페이지를 모두 나열해야 합니다.

    import asyncio
    from typing import Any, cast
    from anthropic import AsyncAnthropic
    from anthropic.types.beta import BetaManagedAgentsCustomToolParams
    from mcp import ClientSession, types
    # Requires mcp >= 1.24, which renamed streamablehttp_client to streamable_http_client.
    from mcp.client.streamable_http import streamable_http_client
    
    MCP_SERVER_URL = "http://mcp.internal.example.com:8000/mcp"
    
    
    def to_custom_tool(tool: types.Tool) -> BetaManagedAgentsCustomToolParams:
        # The MCP fields map one to one onto a custom tool declaration. The cast
        # hands the schema dictionary to the SDK's typed parameter unchanged.
        return {
            "type": "custom",
            "name": tool.name,
            "description": tool.description or tool.name,
            "input_schema": cast(Any, tool.inputSchema),
        }
    
    
    async def main() -> None:
        # Run this wherever you create agents, not on the worker host: it
        # authenticates with your Claude API key (ANTHROPIC_API_KEY).
        async with (
            streamable_http_client(MCP_SERVER_URL) as (read, write, _),
            ClientSession(read, write) as mcp_session,
            AsyncAnthropic() as client,
        ):
            await mcp_session.initialize()
            listed = await mcp_session.list_tools()
            agent = await client.beta.agents.create(
                name="Internal tools agent",
                model="claude-opus-5-5",
                tools=[
                    {"type": "agent_toolset_20260401"},
                    *[to_custom_tool(tool) for tool in listed.tools],
                ],
            )
            print(agent.id)
    
    
    asyncio.run(main())
  2. 워커에서 도구 제공

    시작 시 같은 MCP 서버에 연결하고, async_mcp_tool로 서버의 도구를 변환한 다음, beta_agent_toolset_20260401과 함께 등록하세요. 워커가 실행되는 동안 MCP 세션 하나를 계속 열어 두세요.

    import asyncio
    import os
    from datetime import timedelta
    from anthropic import AsyncAnthropic
    from anthropic.lib.environments import EnvironmentWorker
    from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401
    from anthropic.lib.tools.mcp import async_mcp_tool
    from mcp import ClientSession
    # Requires mcp >= 1.24, which renamed streamablehttp_client to streamable_http_client.
    from mcp.client.streamable_http import streamable_http_client
    
    MCP_SERVER_URL = "http://mcp.internal.example.com:8000/mcp"
    
    
    async def main() -> None:
        environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"]
        environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"]
        # Connect to the MCP server once at startup and keep the session open for
        # the life of the worker. The timeout turns a hung tool call into an error
        # result instead of a stalled call.
        async with (
            streamable_http_client(MCP_SERVER_URL) as (read, write, _),
            ClientSession(read, write, read_timeout_seconds=timedelta(seconds=60)) as mcp_session,
            AsyncAnthropic(auth_token=environment_key) as client,
        ):
            await mcp_session.initialize()
            listed = await mcp_session.list_tools()
            mcp_tools = [async_mcp_tool(tool, mcp_session) for tool in listed.tools]
            await EnvironmentWorker(
                client,
                environment_id=environment_id,
                environment_key=environment_key,
                workdir="/workspace",
                tools=lambda env: [*beta_agent_toolset_20260401(env), *mcp_tools],
            ).run()
    
    
    asyncio.run(main())

MCP 서버를 래핑할 때는 다음 사항에 유의하세요.

  • 도구는 런타임에 검색되지 않으며, 미리 선언해야 합니다. 워커는 시작 시 MCP 서버의 도구 목록을 한 번만 가져오며, 실행 중인 세션에 도구를 추가할 수 없습니다. 서버의 도구가 변경되면 에이전트에 다시 선언하거나 에이전트 구성 업데이트를 통해 유휴 세션에 다시 선언한 다음, 워커를 다시 시작하세요.
  • 이름과 설명은 Managed Agents API의 요구 사항을 충족해야 합니다. 사용자 정의 도구 이름은 에이전트 내에서 고유해야 하며 문자, 숫자, 밑줄, 하이픈만 사용할 수 있습니다(1–128자). 비어 있지 않은 설명이 필요하며, 에이전트의 tools 배열에는 최대 128개의 항목을 넣을 수 있습니다(래핑된 각 도구가 항목 하나이며, 내장 도구 세트도 항목 하나를 차지합니다). API는 도구 이름을 중복 사용하거나, bash 또는 read 같은 내장 에이전트 도구의 이름을 사용자 정의 도구에 사용하거나, 예약된 mcp__ 접두사를 사용하는 선언을 거부합니다. MCP 헬퍼는 서버의 이름과 설명을 그대로 유지하므로, 필요한 경우 이름을 바꾸거나 설명을 줄이세요. 두 서버가 같은 도구 이름을 노출한다면, 접두사를 붙인 이름으로 래퍼를 직접 정의하고 그 래퍼가 서버의 원래 도구 이름을 호출하도록 하세요.
  • 대부분의 스키마는 변경 없이 그대로 사용할 수 있습니다. API는 additionalProperties, title 등 MCP 서버가 흔히 내보내는 JSON Schema 키워드를 허용합니다. 하지만 사용자 정의 도구의 input_schema 어디에서든 $ref 같은 참조 키워드는 거부하므로, pydantic 같은 생성기가 $defs로 분리한 스키마는 인라인으로 펼치세요. 또한 최상위 수준의 oneOf, anyOf, allOf와, 문자, 숫자, 밑줄, 점, 하이픈 이외의 문자를 포함하는 속성 이름(1–64자)도 거부합니다.
  • 도구 실패는 오류 도구 결과로 표시됩니다. MCP 서버가 도구 오류를 보고하면, 워커는 모델이 대응할 수 있는 오류 도구 결과를 게시합니다. 오디오 블록이나 리소스 링크처럼 도구 결과에 대응하는 형식이 없는 MCP 콘텐츠도 오류로 표시됩니다. 더 빠르고 명확하게 실패하도록 MCP 클라이언트에 타임아웃을 설정하세요. Python 워커 예제에서는 read_timeout_seconds로 이를 설정합니다. 타임아웃을 설정하지 않으면, 응답 없는 호출은 TypeScript MCP SDK의 기본 요청 타임아웃(약 1분)이 발생하거나 워커 자체의 안전장치가 작동할 때에만 오류 결과가 됩니다. 워커의 안전장치는 Python에서는 약 2분 30초, Go에서는 2분 후에 작동합니다. Go에서는 기본값인 120초를 넘긴 도구 호출을 워커가 취소하고 오류 결과를 게시합니다.
  • 직접 운영하거나 신뢰하는 서버만 래핑하세요. 래핑된 도구의 이름, 설명, 결과는 다른 도구와 마찬가지로 모델의 컨텍스트에 들어갑니다. 즉, 워커 호스트의 bash를 포함해 에이전트가 다른 도구로 수행하는 작업에 영향을 줄 수 있는 신뢰할 수 없는 입력이 됩니다. 에이전트가 사용하도록 의도한 도구만 선언하세요.
  • 권한 정책은 사용자 정의 도구에 적용되지 않습니다. 권한 정책은 내장 도구 세트와 MCP 도구 세트에만 적용됩니다. 워커는 모델이 수행하는 모든 래핑된 도구 호출을 실행하므로, 승인 단계가 필요하다면 직접 작성한 도구 코드에 넣으세요.

모니터링 및 운영

이 호출들은 모니터링 또는 운영 도구에서 Claude API 키로 인증하여 실행하며, 워커 플릿을 관찰하고 관리하는 데 사용합니다. 작업 가져오기와 keep-alive 루프는 워커 헬퍼 내부에서 처리되므로, 해당 엔드포인트를 직접 호출할 필요는 없습니다.

대기열 깊이 읽기

work.stats는 환경의 대기열 상태를 반환합니다.

  • depth는 가져가기를 기다리는 항목의 수입니다. 이 값을 기준으로 워커 플릿을 확장하거나 백로그 알림을 설정하세요.
  • pending은 워커가 가져갔지만 아직 확인(acknowledge)하지 않은 항목의 수입니다. 워커 헬퍼는 각 항목을 처리하기 전에 확인하므로, 정상 운영 시 이 값은 0에 가깝게 유지됩니다. 0이 아닌 값이 계속 유지된다면 워커가 항목을 가져간 뒤 확인하기 전에 멈췄다는 뜻입니다.
  • oldest_queued_at은 대기열에 남아 있는 가장 오래된 항목의 타임스탬프입니다. 여기에는 가져가기를 기다리는 항목과, 가져갔지만 아직 확인되지 않은 항목이 포함됩니다. 해당 항목이 없으면 null입니다.
  • workers_polling은 최근 30초 이내에 폴링한 워커의 수입니다. 활성 상태(liveness) 알림에 사용하세요.
import os

import anthropic

client = anthropic.Anthropic()

stats = client.beta.environments.work.stats(os.environ["ANTHROPIC_ENVIRONMENT_ID"])
print(f"depth={stats.depth} pending={stats.pending}")
{
  "type": "work_queue_stats",
  "depth": 0,
  "pending": 0,
  "oldest_queued_at": null,
  "workers_polling": 0
}

세션을 정상적으로 중지

work.stop을 사용하면 특정 세션을 처리하는 워커에 해당 세션의 종료를 요청할 수 있습니다. 기본적으로 다음 순서로 진행됩니다.

  1. 작업 항목이 stopping 상태로 전환됩니다.
  2. 워커가 다음 리스 하트비트에서 이를 감지하고, 세션에서 진행 중인 도구 호출을 취소한 뒤 종료를 확인합니다.
  3. 작업 항목이 stopped 상태가 됩니다.

워커의 확인을 기다리지 않고 작업 항목을 즉시 stopped로 표시하려면 요청 본문에 force: true를 전달하세요(CLI에서는 --force를 전달).

이 호출들은 워커 호스트가 아닌 운영 도구에서 실행되므로 ANTHROPIC_WORK_ID가 자동으로 설정되지 않습니다. 다음 예제를 실행하기 전에 이 변수를 대상 작업 항목의 ID로 설정하세요. 작업 항목의 ID를 찾으려면 Environments Work 엔드포인트를 통해 환경의 작업 항목 목록을 조회하세요.

import os

import anthropic

client = anthropic.Anthropic()

work = client.beta.environments.work.stop(
    os.environ["ANTHROPIC_WORK_ID"],
    environment_id=os.environ["ANTHROPIC_ENVIRONMENT_ID"],
)
print(work.state)

다음 단계

자체 호스팅 샌드박스 환경의 공동 책임 모델입니다.

세션을 생성하여 에이전트를 실행하고 작업 수행을 시작하세요.

인바운드 포트를 열거나 서비스를 공용 인터넷에 노출하지 않고도, 사설 네트워크에서 실행되는 MCP 서버에 Claude를 안전하게 연결하세요.

Was this page helpful?