Sandboxes auto-hébergées
Exécutez des sessions Claude Managed Agents dans des bacs à sable auto-hébergés, en conservant l'exécution des outils, les fichiers et le trafic réseau sortant dans votre propre infrastructure.
Par défaut, Managed Agents exécute les outils et le code dans des bacs à sable cloud gérés par Anthropic. Les « self-hosted sandboxes » (bacs à sable auto-hébergés) conservent l'orchestration du côté d'Anthropic, mais déplacent l'exécution des outils vers une infrastructure que vous contrôlez. Ainsi, le code, le système de fichiers et le « network egress » (trafic réseau sortant) de l'agent ne quittent jamais votre environnement.
L'exécution des outils reste sur votre hôte : vous contrôlez le système de fichiers que l'agent lit et écrit, les processus qu'il lance et le réseau qu'il peut atteindre. Les entrées et sorties des outils transitent toujours vers le plan de contrôle d'Anthropic (où Claude s'exécute), afin que le modèle puisse voir les résultats et déterminer la suite. Les « skills » (compétences) de l'agent et le contenu des « memory stores » (magasins de mémoire) attachés à la session sont stockés par Anthropic et copiés dans votre bac à sable pour la durée de la session. Les modifications que l'agent apporte aux fichiers de mémoire sont synchronisées vers le magasin. Consultez le modèle de sécurité pour connaître la frontière complète des flux de données.
Différences avec les environnements cloud
| Environnement cloud | Bac à sable auto-hébergé | |
|---|---|---|
| Lieu d'exécution des outils | Bacs à sable gérés par Anthropic | Votre infrastructure |
| Portée réseau | Contrôles de trafic sortant d'Anthropic | Votre politique réseau |
| Montage de fichiers et de dépôts GitHub | Géré par Anthropic | Géré par vous |
| Magasins de mémoire | Montés par Anthropic dans /mnt/memory/ | Téléchargés dans /mnt/memory/ et synchronisés par le worker du SDK |
| Cycle de vie | Géré par Anthropic | Géré par vous |
L'auto-hébergement est adapté lorsque l'agent doit traiter des données qui ne peuvent pas quitter le périmètre de votre réseau, atteindre des services internes qui ne sont pas routables publiquement, ou s'exécuter sous les contrôles de conformité et d'audit propres à votre organisation.
Pour l'éligibilité à la conservation zéro des données (Zero Data Retention) et au BAA HIPAA, consultez API et conservation des données.
Quand combiner avec les tunnels MCP
L'auto-hébergement contrôle l'endroit où le code de l'agent s'exécute. Les tunnels MCP contrôlent la manière dont Anthropic atteint les serveurs MCP de votre réseau. Ces deux mécanismes sont indépendants. Une session exécutée dans les bacs à sable cloud d'Anthropic peut tout de même atteindre des serveurs MCP privés via un tunnel. Une session auto-hébergée peut utiliser des serveurs MCP tunnelisés ou publics. Utilisez les deux lorsque vous souhaitez que l'exécution et l'accès aux outils restent à l'intérieur de votre périmètre. Pour fournir à l'agent des outils provenant d'un serveur MCP de votre réseau sans exécuter de tunnel, vous pouvez aussi encapsuler le serveur sous forme d'outils personnalisés servis par votre worker.
Worker d'environnement
Un « environment worker » (worker d'environnement) est un processus que vous exécutez sur votre propre infrastructure. Il reçoit d'Anthropic des requêtes d'exécution d'outils et les exécute localement. L'environnement self_hosted fait office de « work queue » (file de travail) : lorsqu'une session lui est assignée, Anthropic place la session dans la file sous forme d'élément de travail. Votre worker réclame les éléments de travail de cette file et crée un contexte d'exécution pour chacun. Il télécharge ensuite les compétences de l'agent, c'est-à-dire des ressources réutilisables, basées sur le système de fichiers, qui apportent à l'agent une expertise propre à un domaine. Enfin, il exécute les appels d'outils et renvoie les résultats.
Les éléments de travail sont réclamés par « polling » (interrogation) de la file de l'environnement. Deux approches sont possibles : un worker toujours actif (« always-on worker ») qui interroge la file en continu, ou un gestionnaire déclenché par webhook (« webhook-triggered handler ») qui se réveille sur session.status_run_started et commence alors l'interrogation.
La CLI et le SDK fournissent tous deux des workers prêts à l'emploi. La CLI ant ne prend en charge que le modèle toujours actif, tandis que le SDK prend en charge les deux modèles. Les deux sont configurables : consultez Worker auto-hébergé dans la référence pour les options de la CLI, et Utilitaires du SDK sur cette page pour les options du SDK. Pour davantage de contrôle, appelez directement les points de terminaison Environments Work et implémentez votre propre worker.
Système de fichiers du bac à sable
/workspace: le répertoire de travail par défaut du système pour l'exécution des outils et le téléchargement des compétences. L'option--workdirde la CLI utilise par défaut le répertoire courant. Passez--workdir /workspacepour correspondre à la valeur par défaut du système. Les compétences sont téléchargées dans<workdir>/skills/<name>/. Si vous utilisez un autre répertoire de travail, mettez à jour l'invite système de votre agent afin que Claude puisse localiser les fichiers des compétences.- Sorties : sur les environnements auto-hébergés, l'invite système de la session omet l'instruction
/mnt/session/outputsutilisée sur les bacs à sable gérés par Anthropic. Les livrables finaux se retrouvent donc là où l'agent les écrit dans le système de fichiers de votre bac à sable, généralement sous le répertoire de travail. /mnt/memory/: le worker du SDK matérialise ici les magasins de mémoire attachés à la session, avec un répertoire par magasin aumount_pathde celui-ci (par exemple,/mnt/memory/user-preferences/). Le worker crée ces répertoires lorsqu'il réclame la session et les supprime à la fin de celle-ci. Consultez Utiliser les magasins de mémoire.
Avant de commencer
Vous avez besoin des éléments suivants :
- Un agent existant. Si vous n'en avez pas, suivez d'abord le Démarrage rapide et notez l'ID de l'agent.
- Un hôte Linux disposant de
/bin/bashà ce chemin exact. L'outil bash du worker l'invoque directement, sans consulterPATH. Le SDK TypeScript requiert en outreunzipettardans lePATH, ainsi que Node.js 22 ou une version ultérieure. Les SDK Python et Go utilisent leurs bibliothèques standard pour l'extraction des archives et n'exigent aucun binaire supplémentaire. - La CLI
antou un SDK Anthropic (Python, TypeScript ou Go) sur l'hôte du worker. - Des identifiants :
- Une clé d'environnement, générée dans la Console lors des étapes suivantes, authentifie le worker auprès de sa file. La génération de clés se fait uniquement dans la Console.
- Votre clé API Claude permet de créer des sessions et de lire les statistiques de la file depuis l'extérieur de l'hôte du worker.
- Les éléments de travail réclamés contiennent également un
secretpropre à chaque session, que le worker utilise pour monter les magasins de mémoire. Vous ne le générez pas vous-même. En revanche, dans le modèle « un bac à sable par session », c'est à vous de le transmettre au bac à sable (consultez Exécuter un bac à sable par session).
- Pour les magasins de mémoire, un hôte préparé. Si des sessions de cet environnement attachent des magasins de mémoire, préparez
/mnt/memorysur l'hôte du worker avant de démarrer le worker. Consultez Préparer l'hôte.
Créer un environnement auto-hébergé
Dans la Console : Workspace > Environments > New > Self-hosted
Ou via l'API :
ant apply environment.yamlenvironment.yaml# yaml-language-server: $schema=https://platform.claude.com/schemas/ant/beta/environment.json name: self-hosted config: type: self_hostedGénérer une clé d'environnement
Dans la Console, ouvrez l'environnement et cliquez sur Generate environment key. La génération de clés se fait uniquement dans la Console, que vous ayez créé l'environnement via la Console ou via l'API. Exportez ensuite l'ID et la clé de l'environnement sur l'hôte du worker :
export ANTHROPIC_ENVIRONMENT_KEY="sk-ant-oat01-..." export ANTHROPIC_ENVIRONMENT_ID="env_..."
Exécuter un worker
Choisissez le mode toujours actif pour la configuration la plus simple : un processus de longue durée interroge la file en continu et n'a besoin que de HTTPS sortant.
Choisissez le mode déclenché par webhook pour éviter de faire tourner un processus d'interrogation inactif. Ce mode nécessite un point de terminaison de webhook qu'Anthropic peut atteindre (consultez Webhooks pour la configuration du point de terminaison et la vérification des signatures).
Installer la CLI ant
Exécutez ceci sur l'hôte du worker.
curl (Linux/WSL)Pour les environnements Linux, téléchargez directement le binaire de la version.
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 antVous trouverez toutes les versions sur la page des versions GitHub.
Homebrew (macOS)brew install anthropics/tap/antExécuter le worker
Dans le processus
ant beta:worker pollréclame les éléments de travail assignés à l'environnement, télécharge les compétences, exécute les appels d'outils dans le répertoire de travail et renvoie les résultats. Il litANTHROPIC_ENVIRONMENT_KEYetANTHROPIC_ENVIRONMENT_IDdepuis l'environnement.ant beta:worker poll --workdir "/workspace"Le worker se termine proprement sur SIGTERM ou SIGINT : avant de s'arrêter, il annule tout appel d'outil en cours, renvoie son résultat d'erreur et libère l'élément de travail.
Un bac à sable par session
Si vous avez besoin d'une isolation plus forte (un système de fichiers neuf, des limites de ressources ou des contrôles réseau par session), exécutez chaque session dans son propre bac à sable.
Construisez une image dans laquelle
antest installé, avecant beta:worker runcomme point d'entrée. L'image de base doit fournir/bin/bash;curln'est utilisé qu'au moment de la construction. Au démarrage, le bac à sable lit les détails de la session depuis les variables d'environnement, traite cette session, puis s'arrête :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"]Écrivez ensuite un script de lancement qui transmet les détails de la session à un nouveau bac à sable. Le processus d'interrogation fournit au script les éléments suivants :
- les variables
ANTHROPIC_SESSION_ID,ANTHROPIC_WORK_ID,ANTHROPIC_ENVIRONMENT_IDetANTHROPIC_ENVIRONMENT_KEY, injectées dans son environnement ; - l'élément de travail réclamé, écrit au format JSON sur son entrée standard, y compris le
secretpropre à la session lorsqu'Anthropic en a émis un ; - la variable
ANTHROPIC_BASE_URL, facultative, transmise uniquement si elle était définie sur l'hôte du processus d'interrogation ; elle remplace le point de terminaison d'API par défaut.
Dans l'exemple,
/host/outputsest un répertoire de l'hôte que vous choisissez. Il est monté par liaison sur le répertoire de travail du bac à sable (/workspace), ce qui vous permet de récupérer les livrables de la session après l'arrêt du bac à sable. Sur les environnements auto-hébergés, l'agent écrit ses livrables sous le répertoire de travail plutôt que dans/mnt/session/outputs(consultez Système de fichiers du bac à sable) : c'est donc le montage du répertoire de travail qui les capture. Ce montage récupère aussi l'arborescenceskills/téléchargée et tous les fichiers intermédiaires créés par l'agent.#!/bin/bash # spawn.sh : appelé une fois par élément de travail réclamé. 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-imageLe point d'entrée
ant beta:worker runne monte pas les magasins de mémoire. Si les sessions de cet environnement attachent des magasins de mémoire, conservez le processus d'interrogation, mais construisez l'image par session autour du worker du SDK. Étendez aussi le script de lancement pour qu'il transmette lesecretde l'élément de travail au bac à sable, comme indiqué dans Exécuter un bac à sable par session.Démarrez le processus d'interrogation en le faisant pointer vers le script :
ant beta:worker poll --on-work ./spawn.sh- les variables
Exécuter le worker
EnvironmentWorkerréclame les éléments de travail assignés à l'environnement, télécharge les compétences, exécute les appels d'outils dans le répertoire de travail et renvoie les résultats. Authentifiez-vous avec la clé d'environnement que vous avez générée dans Avant de commencer.import asyncio import contextlib import os import signal from anthropic import AsyncAnthropic from anthropic.lib.environments import EnvironmentWorker 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: worker = EnvironmentWorker( client, environment_id=environment_id, environment_key=environment_key, workdir="/workspace", ) task = asyncio.create_task(worker.run()) # Cancelling the task, rather than killing the process, lets the worker stop its # in-flight work item and upload changed memory files 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())
Exporter la clé de signature du webhook
En plus de l'ID et de la clé d'environnement obtenus dans Avant de commencer, exportez la clé de signature du webhook sur l'hôte de votre gestionnaire, afin que celui-ci puisse vérifier les charges utiles entrantes. Dans le gestionnaire Python, la vérification des signatures nécessite l'extra webhooks :
pip install "anthropic[webhooks]".export ANTHROPIC_WEBHOOK_SIGNING_KEY="whsec_..."Implémenter le gestionnaire de webhook
EnvironmentWorkerréclame l'élément de travail, télécharge les compétences, exécute les appels d'outils dans le répertoire de travail, renvoie les résultats, puis s'arrête. Invoquez-le lorsquesession.status_run_startedse déclenche.Lorsque vous transmettez vous-même un élément de travail réclamé à
handle_item(), comme le fait ce gestionnaire, passez lesecretde l'élément de travail en tant quework_secret. La session peut ainsi monter les magasins de mémoire qui lui sont attachés.Un gestionnaire comme celui-ci exécute tous les éléments réclamés dans un seul processus, sur un seul hôte. Deux sessions qui attachent le même magasin de mémoire ne peuvent donc pas s'y exécuter en même temps (consultez Préparer l'hôte). Si vos sessions partagent des magasins, lancez plutôt un bac à sable par session.
import asyncio import os import anthropic import standardwebhooks # installed by the anthropic[webhooks] extra environment_key = os.environ["ANTHROPIC_ENVIRONMENT_KEY"] environment_id = os.environ["ANTHROPIC_ENVIRONMENT_ID"] client = anthropic.AsyncAnthropic( auth_token=environment_key, ) # Cancelled by shutdown() so an in-flight work item can upload changed memory files and # remove its store directories before the process exits. inflight: set[asyncio.Task[None]] = set() # Await this from the host's shutdown hook, such as an ASGI lifespan shutdown (the code after # `yield` in a FastAPI lifespan), which uvicorn runs on SIGTERM. uvicorn lets open requests # finish before that hook runs, so set --timeout-graceful-shutdown to bound the wait. async def shutdown() -> None: for task in inflight: task.cancel() await asyncio.gather(*inflight, return_exceptions=True) async def handle(raw: bytes, headers: dict[str, str]) -> tuple[dict[str, str], int]: try: event = client.beta.webhooks.unwrap(raw.decode(), headers=headers) except standardwebhooks.WebhookVerificationError: return {"error": "signature verification failed"}, 401 if event.data.type != "session.status_run_started": return {"status": "ignored"}, 200 task = asyncio.create_task(run_queued_work()) inflight.add(task) task.add_done_callback(inflight.discard) try: # Shielded: a dropped or timed-out delivery must not cancel the item; shutdown() does. await asyncio.shield(task) except asyncio.CancelledError: return {"status": "shutting down"}, 503 return {"status": "ok"}, 200 async def run_queued_work() -> None: async for work in client.beta.environments.work.poller( environment_id=environment_id, environment_key=environment_key, block_ms=None, reclaim_older_than_ms=2000, drain=True, auto_stop=False, ): await client.beta.environments.work.worker(workdir="/workspace").handle_item( work_id=work.id, environment_id=environment_id, session_id=work.data.id, environment_key=environment_key, # The per-session secret is what lets the worker mount the session's memory stores. work_secret=work.secret, )
Utilitaires du SDK
Le SDK fournit trois utilitaires, chacun offrant un niveau de contrôle différent. EnvironmentWorker couvre la plupart des cas d'usage. Passez aux utilitaires de plus bas niveau lorsque vous devez lancer votre propre processus par session ou exécuter des outils pour une session déjà réclamée.
EnvironmentWorker: le worker prêt à l'emploi. Il gère l'interrogation, la configuration et l'exécution de bout en bout..run(): s'exécute indéfiniment et prend en charge les sessions à mesure qu'elles arrivent..handle_item(): traite un seul élément de travail réclamé, puis s'arrête.- Passez explicitement les identifiants du travail, de la session et de l'environnement, ou laissez la méthode lire les variables
ANTHROPIC_*queant beta:worker poll --on-workdéfinit pour le processus qu'il lance. - Pour permettre à la session de monter ses magasins de mémoire, passez aussi le
secretde l'élément de travail en tant quework_secret, ou définissezANTHROPIC_WORK_SECRET. ant beta:worker poll --on-workne définit pas cette variable. Lisez donc le secret depuis le JSON de l'élément de travail qu'il écrit sur l'entrée standard de votre script, comme indiqué dans Exécuter un bac à sable par session.
- Passez explicitement les identifiants du travail, de la session et de l'environnement, ou laissez la méthode lire les variables
memory_sync_intervaletmemory_sync_deletions: la fréquence à laquelle les magasins de mémoire attachés se réconcilient avec le serveur pendant l'exécution de la session, et si les fichiers que l'agent supprime localement sont également supprimés du magasin. Consultez Configurer la synchronisation pour les unités, les valeurs par défaut et la manière de désactiver la prise en charge de la mémoire.
work.poller(): interroge la file de travail pour vous et vous fournit chaque session réclamée. Utilisez-le lorsque vous voulez décider de ce qui se passe pour chaque session, par exemple lancer un bac à sable plutôt qu'exécuter les outils dans le processus.drain: indique s'il faut arrêter l'interrogation une fois la file vide, plutôt que d'attendre de nouveaux travaux.block_ms: la durée d'attente de l'arrivée d'un travail avant de rendre la main, en millisecondes.- La valeur doit être comprise entre 1 et 999 (attente par interrogation ; l'utilitaire relance automatiquement l'interrogation).
- Passez
Nonepour une vérification non bloquante. - Si vous omettez le paramètre, l'interrogation longue par défaut de 999 ms s'applique.
reclaim_older_than_ms: réclame à nouveau les éléments de travail qui ont été réclamés mais jamais acquittés dans ce délai, exprimé en millisecondes.auto_stop: indique s'il faut envoyer un signal d'arrêt pour chaque élément de travail une fois que le corps de votre boucle en a terminé avec lui. Désactivez cette option chaque fois que le composant qui exécute l'élément de travail envoie lui-même le signal d'arrêt :handle_item()l'envoie. Définissez donc l'option sur false lorsque vous transmettez des éléments réclamés àhandle_item(), comme le font les gestionnaires de webhook de cette page.- Un bac à sable que vous lancez et qui se charge de l'appel d'arrêt l'envoie également.
client.beta.sessions.events.tool_runner(): exécute les appels d'outils d'une seule session, à partir de l'ID de session et d'une liste d'outils. Utilisez-le lorsque vous avez déjà réclamé le travail et n'avez besoin que de la couche d'exécution.
Utilisez directement work.poller() lorsque vous voulez lancer votre propre processus par session, par exemple pour démarrer un bac à sable pour chaque session réclamée :
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}")
# Remplacez `docker run` par votre propre lanceur de sandbox. Transmettez la clé
# d'environnement (jamais votre clé API) et le secret par session de l'élément de travail : le worker
# qui s'y exécute a besoin de ce secret pour monter les magasins de mémoire de la session.
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())Le composant qui lance le bac à sable doit y transmettre le secret de l'élément de travail réclamé (par exemple sous la forme ANTHROPIC_WORK_SECRET), en plus des identifiants de la session, du travail et de l'environnement. Le worker à l'intérieur peut ainsi monter les magasins de mémoire de la session ; consultez Exécuter un bac à sable par session.
AgentToolContext est le contexte d'exécution des appels d'outils. Il définit le répertoire de travail et la politique de chemins, et peut télécharger les compétences de la session.
- Les outils de fichiers (
read,write,edit,glob,grep) sont confinés au répertoire de travail et aux répertoires listés dansallowed_roots. writeeteditrefusent en outre les chemins situés sousread_only_roots.EnvironmentWorkerajoute lui-même les répertoires des magasins de mémoire de la session à ces listes.
Ce confinement est un garde-fou pour les outils de fichiers uniquement, et non un bac à sable : il ne restreint pas bash.
beta_agent_toolset_20260401(env) prend un AgentToolContext et renvoie les implémentations d'outils standard (bash, read, write, edit, glob, grep).
Avec EnvironmentWorker : ces deux éléments sont gérés automatiquement. Passez une fabrique tools pour personnaliser la liste d'outils :
EnvironmentWorker(client, ..., tools=lambda env: [beta_bash_tool(env), my_custom_tool])Avec work.poller() et tool_runner() : passez une liste d'outils en tant que tools à client.beta.sessions.events.tool_runner(). Pour construire cette liste, configurez vous-même AgentToolContext et appelez 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)Vérifier que le worker est connecté
Depuis un autre shell, avec ANTHROPIC_API_KEY défini sur votre clé API Claude (et non sur la clé d'environnement), vérifiez que workers_polling vaut au moins 1 :
ant beta:environments:work stats --environment-id "$ANTHROPIC_ENVIRONMENT_ID"Si workers_polling reste à 0, le worker n'atteint pas la file. Vérifiez que ANTHROPIC_ENVIRONMENT_KEY et ANTHROPIC_ENVIRONMENT_ID sont définis sur l'hôte du worker. Consultez Lire la profondeur de la file pour la réponse complète des statistiques et des exemples dans d'autres langages.
Démarrer une session
Une fois votre worker en cours d'exécution, créez une session qui cible l'environnement. Définissez AGENT_ID sur l'ID d'agent noté dans Avant de commencer. La session entre dans la file de travail de l'environnement et y attend qu'un worker la réclame. Si aucun worker n'est connecté, la session reste en file d'attente au lieu d'échouer.
Anthropic ne monte ni fichiers ni dépôts GitHub dans les bacs à sable auto-hébergés. Pour rendre disponibles des fichiers propres à une session, passez des références de fichiers (comme un chemin S3 ou un SHA de commit) dans le champ metadata de la session. L'élément de travail réclamé ne contient pas les métadonnées de la session, mais il contient l'ID de session. Votre script de lancement ou votre gestionnaire --on-work récupère donc la session (GET /v1/sessions/{session_id}) pour lire le champ metadata. Il place ensuite les fichiers dans le répertoire de travail avant le début de l'exécution des outils.
session = client.beta.sessions.create(
agent=agent.id,
environment_id=environment.id,
metadata={"input_file": "s3://my-bucket/data.csv"},
)Consultez Worker auto-hébergé dans la référence pour la liste complète des options de la CLI, et Utilitaires du SDK pour les options des utilitaires du SDK.
Utiliser les magasins de mémoire
Les sessions d'un environnement auto-hébergé attachent des magasins de mémoire exactement comme les sessions des environnements cloud : listez-les dans resources lors de la création de la session, comme indiqué dans Attacher un magasin de mémoire à une session. Une session accepte jusqu'à 8 magasins de mémoire.
Sur un environnement auto-hébergé, c'est le worker du SDK, et non l'infrastructure d'Anthropic, qui matérialise chaque magasin pour l'agent. Les magasins de mémoire y nécessitent donc EnvironmentWorker (ou sa méthode handle_item()) du SDK Python, TypeScript ou Go.
Le worker de la CLI ant (ant beta:worker poll et ant beta:worker run) ne monte pas les magasins de mémoire. Pour combiner le processus d'interrogation de la CLI avec des magasins de mémoire, exécutez le worker du SDK dans un bac à sable par session, comme décrit dans Exécuter un bac à sable par session.
Les magasins de mémoire ne peuvent pas être attachés aux sessions des environnements auto-hébergés sur Claude Platform on AWS.
Comment le worker gère la mémoire
Lorsque le worker réclame un élément de travail dont la session a des magasins de mémoire attachés, il effectue les opérations suivantes :
- Il télécharge chaque magasin attaché vers son
mount_pathsur l'hôte du worker, en s'authentifiant avec lesecretpropre à la session de l'élément de travail. Lemount_pathest le même répertoire sous/mnt/memory/que celui utilisé par les sessions cloud (par exemple,/mnt/memory/user-preferences/pour un magasin nommé « User Preferences »). L'invite système de la session le décrit à l'agent. - Il ajoute ces répertoires aux racines autorisées des outils de fichiers. Les répertoires des magasins attachés avec
access: "read_only"sont en outre ajoutés aux racines en lecture seule. L'agent manipule ainsi les mémoires avec les mêmes outilsread,write,edit,globetgrepque dans le répertoire de travail. - Il réconcilie les modifications locales et distantes après les appels d'outils, au plus une fois par intervalle de synchronisation (15 secondes par défaut). Les mémoires modifiées dans le magasin sont écrites sur le disque, et les fichiers modifiés par l'agent sont téléversés vers le magasin.
- Il effectue une synchronisation finale à la fin de la session, termine les téléversements encore en attente pendant 30 secondes au maximum, puis supprime les répertoires qu'il a créés. Un worker annulé pendant l'exécution d'une session ignore la synchronisation finale. Il téléverse néanmoins les fichiers modifiés et supprime les répertoires avant de s'arrêter.
Le magasin de mémoire côté Anthropic reste la source de vérité. Les versions de mémoire, la rédaction (caviardage), ainsi que la consultation et la modification des mémoires dans la Console fonctionnent comme pour les sessions cloud. Les lectures et écritures de mémoire de l'agent apparaissent dans le flux d'événements sous forme d'événements d'outils ordinaires.
Comme chaque worker se synchronise à intervalles réguliers, une modification écrite dans une session ne devient visible pour une autre session en cours qu'une fois que les deux se sont synchronisées. Avec l'intervalle par défaut, ce délai est généralement bien inférieur à une minute. Les sessions exécutées dans des bacs à sable cloud voient les modifications des autres presque immédiatement.
Chaque répertoire de magasin contient un fichier marqueur nommé .anthropic-memory-store qui lie le répertoire à son magasin. Laissez-le en place : le worker ne synchronise pas un répertoire dont le marqueur est absent ou altéré.
Préparer l'hôte
Les magasins de mémoire sur les bacs à sable auto-hébergés nécessitent un système de fichiers POSIX sur l'hôte du worker (l'hôte Linux mentionné dans Avant de commencer). Les hôtes Windows ne sont pas pris en charge, car le worker a besoin de O_NOFOLLOW pour ouvrir les fichiers de mémoire. Un système de fichiers sensible à la casse est recommandé, afin que des chemins de mémoire qui ne diffèrent que par la casse n'entrent pas en collision.
Avant de démarrer le worker, créez le répertoire parent et rendez-le accessible en écriture à l'utilisateur sous lequel le worker s'exécute :
sudo mkdir -p /mnt/memory && sudo chown "$USER" /mnt/memoryNe créez pas vous-même les répertoires propres à chaque magasin. Au démarrage d'une session, le worker crée le répertoire mount_path de chaque magasin (par exemple, /mnt/memory/user-preferences). Il refuse de démarrer le travail de la session si quelque chose existe déjà à ce chemin, et il supprime le répertoire à la fin de la session. Il en découle deux règles de fonctionnement :
- Exécutez une seule session par système de fichiers lorsque des sessions attachent le même magasin. Deux sessions ne peuvent pas monter le même magasin sur un même hôte en même temps, car elles ont toutes deux besoin du même chemin. Donner à chaque session son propre bac à sable, comme décrit dans Exécuter un bac à sable par session, permet de respecter cette règle.
- Arrêtez les workers proprement.
- Annulation plutôt que terminaison forcée : lorsque vous arrêtez un worker pendant l'exécution d'une session,
EnvironmentWorkerne téléverse les fichiers de mémoire modifiés et ne supprime les répertoires de magasin que s'il est annulé. S'il est tué, le processus n'exécute aucun nettoyage, et le worker n'installe pas lui-même de gestionnaires de signaux. - Relier les signaux à l'annulation : dans le processus qui exécute le worker, reliez SIGTERM et SIGINT à l'annulation. En TypeScript, interrompez le
signalque vous passez au worker. En Go, annulez le contexte. En Python, annulez la tâche qui exécuterun()ouhandle_item(). - Où placer ce code : faites-le depuis un gestionnaire de signaux lorsque votre worker est le processus lui-même, comme le font les workers autonomes de cette page. Lorsque le worker s'exécute dans un gestionnaire de webhook, faites-le depuis le hook d'arrêt de votre serveur, car le gestionnaire ne doit pas s'approprier les signaux du serveur.
- Délai d'arrêt : arrêtez ensuite les workers avec SIGTERM et laissez-leur au moins 30 secondes pour s'arrêter avant toute terminaison forcée, car le téléversement final peut prendre ce temps.
- Après une terminaison forcée : si un worker est tué avant l'exécution de son nettoyage, supprimez le répertoire de magasin restant sous
/mnt/memory/avant la prochaine session qui attache ce magasin. Toutes les modifications qu'il contenait et qui n'avaient pas été synchronisées sont perdues.
- Annulation plutôt que terminaison forcée : lorsque vous arrêtez un worker pendant l'exécution d'une session,
Exécuter un bac à sable par session
Le modèle « un bac à sable par session » décrit dans Exécuter un worker donne à chaque session un système de fichiers neuf. C'est ce qu'exige Préparer l'hôte lorsque des sessions attachent le même magasin. Conservez ant beta:worker poll --on-work (ou work.poller() du SDK) comme processus d'interrogation sur l'hôte.
Le point d'entrée ant beta:worker run qui y est présenté ne monte pas les magasins de mémoire. Construisez donc plutôt l'image par session autour du worker du SDK. Son point d'entrée construit EnvironmentWorker et appelle handle_item(). Cette méthode lit les identifiants de session, de travail et d'environnement depuis les variables ANTHROPIC_*, et le secret par session de l'élément de travail depuis ANTHROPIC_WORK_SECRET. Vous pouvez également passer le secret explicitement via 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 ne définit pas ANTHROPIC_WORK_SECRET pour le script qu'il lance. Le script de lancement lit donc le secret depuis le JSON de l'élément de travail sur son entrée standard et le transmet au bac à sable :
#!/bin/bash
# spawn.sh : appelé une fois par élément de travail réclamé.
# L'élément de travail réclamé arrive au format JSON sur stdin. Son secret est
# l'identifiant propre à la session, requis par les points de terminaison du magasin de mémoire.
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-imageSi vous réclamez plutôt le travail avec work.poller() du SDK, transmettez de la même manière le secret de chaque élément réclamé au bac à sable que vous lancez. Transmettez-le uniquement au bac à sable qui sert cette session, et ne le journalisez jamais.
L'image du bac à sable a également besoin d'un répertoire /mnt/memory accessible en écriture (consultez Préparer l'hôte). Chaque bac à sable sert une seule session et est supprimé ensuite. Aucun répertoire restant n'a donc besoin d'être nettoyé, et les répertoires de mémoire n'ont pas besoin d'être montés en bind sur l'hôte : le worker téléverse leur contenu vers le magasin avant l'arrêt du bac à sable.
Si vous arrêtez un conteneur avant la fin de sa session, envoyez un signal que le point d'entrée convertit en annulation (consultez Préparer l'hôte) plutôt que de le tuer, afin que ce téléversement s'exécute quand même. Laissez aussi au conteneur le temps de terminer le téléversement. Par défaut, Docker fait suivre le signal d'arrêt d'un SIGKILL au bout de 10 secondes. Portez donc cette limite à au moins les 30 secondes qu'exige la section Préparer l'hôte, avec --stop-timeout sur docker run ou avec le délai de grâce de terminaison de votre orchestrateur.
Configurer la synchronisation
Deux options d'EnvironmentWorker contrôlent le comportement de la mémoire :
memory_sync_interval: la fréquence à laquelle les magasins attachés se réconcilient avec le serveur pendant l'exécution de la session. La valeur s'exprime en secondes en Python, en millisecondes en TypeScript et sous forme de durée en Go.- La valeur par défaut est de 15 secondes, et le minimum est de 5 secondes. Un intervalle plus court réduit la fenêtre pendant laquelle une autre session voit des mémoires obsolètes, au prix d'un plus grand nombre de requêtes vers le magasin de mémoire.
Noneen Python,nullen TypeScript ou une durée négative en Go désactive entièrement la prise en charge de la mémoire. Le worker ne télécharge ni ne synchronise alors aucun magasin. Une session à laquelle des magasins de mémoire sont attachés s'exécute sans eux, même si son invite système les décrit toujours. Ne désactivez donc la prise en charge de la mémoire que sur les workers dont les sessions n'attachent aucun magasin de mémoire.- Tant que la prise en charge de la mémoire est activée, un élément de travail qui arrive sans
secretpar session pour une session avec des magasins attachés échoue au lieu de s'exécuter sans mémoire (consultez Dépanner les montages de mémoire).
memory_sync_deletions: indique si un fichier que l'agent supprime localement est également supprimé du magasin.- En Python et en TypeScript, la valeur est
"enabled"(la valeur par défaut),"log_only"ou"disabled". En Go, c'est l'une des constantesenvironments.MemorySyncDeletionsEnabled(la valeur zéro),environments.MemorySyncDeletionsLogOnlyouenvironments.MemorySyncDeletionsDisabled. - En mode activé, le worker supprime la mémoire du magasin dès qu'une synchronisation ultérieure confirme que le fichier a toujours disparu.
- En mode journalisation seule, il effectue les mêmes vérifications mais se contente de journaliser ce qu'il aurait supprimé. Vous pouvez ainsi observer ce que vos workers supprimeraient avant de faire confiance au mode activé.
- En mode désactivé, il ne supprime jamais rien du magasin.
- Les téléversements et les téléchargements ne sont pas affectés par ce paramètre.
- En Python et en TypeScript, la valeur est
Définissez ces options à l'endroit où vous construisez le worker : soit via le constructeur EnvironmentWorker, soit, en Python et en TypeScript, via la fabrique client.beta.environments.work.worker() qu'utilise le gestionnaire de webhook.
Par exemple, pour synchroniser toutes les 10 secondes et seulement journaliser les suppressions que le worker aurait effectuées :
worker = EnvironmentWorker(
client,
environment_id=environment_id,
environment_key=environment_key,
workdir="/workspace",
memory_sync_interval=10, # seconds
memory_sync_deletions="log_only",
)Magasins en lecture seule et conflits
Pour un magasin attaché avec access: "read_only", les outils write et edit refusent de modifier les fichiers de son répertoire, et le worker n'en téléverse jamais rien. Les modifications effectuées via bash, ou via un outil personnalisé ou un serveur MCP que vous servez depuis la sandbox, ne sont pas bloquées localement : elles ne sont jamais synchronisées vers le magasin, et la prochaine modification distante de cette mémoire les écrase. Si vous avez besoin que la copie locale elle-même reste inchangée pendant la session, désactivez l'outil bash pour cet agent et ne lui donnez aucun outil personnalisé qui écrit dans le système de fichiers de la sandbox ; ne montez pas le chemin du magasin en lecture seule, car le worker lui-même doit créer le répertoire et y écrire les mémoires téléchargées.
Les conflits sont résolus en faveur du magasin. Lorsque l'agent modifie un fichier de mémoire qui a également changé dans le magasin depuis la dernière synchronisation de la session, le worker conserve la version du magasin lors de la synchronisation suivante, écrase le fichier local avec celle-ci et journalise un avertissement ; les outils write et edit eux-mêmes réussissent et aucune erreur ne parvient à l'agent. Si la modification de l'agent reste pertinente, celui-ci peut relire le fichier après la synchronisation et effectuer à nouveau la modification.
Dépanner les montages de mémoire
Le worker journalise les échecs de montage et de synchronisation en arrière-plan au lieu de les signaler à la session ; seuls les refus liés à la lecture seule parviennent à l'agent, sous forme d'erreurs d'outil (consultez Magasins en lecture seule et conflits). Si un magasin de mémoire ne peut pas être monté lorsque le worker réclame une session, le worker fait échouer l'élément de travail : la session n'émet aucun événement d'erreur et reste inactive.
| Symptôme | Cause | Correctif |
|---|---|---|
Le journal du worker contient the work item carried no sessions token (en Go, l'erreur ErrSessionMemoryNoToken) et l'élément de travail échoue. | Le secret par session de l'élément de travail n'a pas atteint le worker : les magasins de mémoire sur les sandboxes auto-hébergées ne sont pas activés pour votre organisation, ou votre script de lancement n'a pas transmis le secret au bac à sable. | Dans le modèle « un bac à sable par session », transmettez ANTHROPIC_WORK_SECRET au bac à sable comme indiqué dans Exécuter un bac à sable par session. Si le worker interroge et exécute les sessions dans un seul processus et journalise toujours ce message, contactez le support. |
Le journal du worker contient something already exists at the memory store's path. | Un répertoire résiduel d'une session précédente, généralement une session dont le worker a été tué avant l'exécution de son nettoyage. | Supprimez le répertoire résiduel indiqué par la ligne de journal. Les modifications qu'il contenait et qui n'avaient pas été synchronisées sont perdues. |
Le journal du worker contient cannot create the memory store's folder et the worker host must make this mount path writable. | L'utilisateur sous lequel s'exécute le worker ne peut pas créer de répertoires sous /mnt/memory. | Créez /mnt/memory et attribuez-le à cet utilisateur avec chown ; consultez Préparer l'hôte. |
La session reste idle avec une raison d'arrêt requires_action et aucun événement d'erreur peu après qu'un worker l'a réclamée. | Le worker a fait échouer l'élément de travail parce qu'il n'a pas pu monter un magasin de mémoire, pour l'une des raisons précédentes. | Corrigez la cause sur l'hôte, puis envoyez un événement user.interrupt : le travail de la session est remis en file d'attente et le prochain worker qui le réclame retente le montage. |
Servir des outils personnalisés depuis votre sandbox
Les outils personnalisés sont des outils que votre propre code exécute. L'agent émet un événement agent.custom_tool_use et attend un user.custom_tool_result correspondant. Le worker peut être ce code. Comme il s'exécute dans votre sandbox, l'outil accède aux services internes, aux identifiants et au trafic réseau sortant que vous avez configurés pour la sandbox, et à rien de plus. La clé d'environnement autorise la publication des résultats des outils personnalisés, de sorte que votre clé API Claude reste en dehors de l'hôte du worker.
Déclarer l'outil sur l'agent
Ajoutez aux
toolsde l'agent une entréecustomdont lenamecorrespond à l'outil que votre worker enregistre. Consultez Outils personnalisés pour la structure complète de la déclaration.{ "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"] } }Enregistrer l'implémentation auprès du worker
Passez l'outil via la fabrique
toolsdu worker (consultez Utilitaires du SDK), aux côtés de l'ensemble d'outils intégré :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())
Le worker ne répond qu'aux outils enregistrés auprès de lui. Un outil personnalisé déclaré sur l'agent mais enregistré auprès d'aucun worker ni client laisse la session en pause, avec une raison d'arrêt requires_action, jusqu'à ce que quelque chose publie son résultat. Consultez Gérer les appels d'outils personnalisés pour le flux d'événements.
Encapsuler un serveur MCP en tant qu'outils personnalisés
Le connecteur MCP se connecte aux serveurs MCP depuis l'infrastructure d'Anthropic. Un serveur doit donc exposer un point de terminaison HTTP qu'Anthropic peut atteindre, directement ou via un tunnel MCP. Pour utiliser un serveur que seul votre réseau peut atteindre, faites plutôt du worker le client MCP et déclarez les outils du serveur en tant qu'outils personnalisés.
Le serveur MCP n'a besoin d'aucune connectivité entrante depuis l'extérieur de votre réseau. Anthropic reçoit les définitions d'outils que vous déclarez sur l'agent, l'entrée de chaque appel et le résultat que votre worker renvoie. À l'exécution, le modèle appelle un outil encapsulé comme n'importe quel autre outil personnalisé :
- L'agent émet un événement
agent.custom_tool_use. - Le worker, dans votre sandbox, transmet l'appel via sa session MCP ouverte au serveur de votre réseau.
- Le worker publie la réponse du serveur en tant que
user.custom_tool_result.
Les assistants MCP côté client des SDK convertissent les outils du serveur en outils exécutables que le worker accepte. Installez un SDK MCP aux côtés du SDK Anthropic :
- Python :
pip install "anthropic[mcp]" "mcp>=1.24" - TypeScript :
npm install @modelcontextprotocol/sdk - Go :
go get github.com/modelcontextprotocol/go-sdk
Les exemples se connectent sans authentification. Pour envoyer des identifiants, configurez le client HTTP ou les options de requête que vous fournissez au transport MCP (http_client).
Déclarer les outils du serveur sur l'agent
Listez les outils du serveur MCP et déclarez chacun d'eux en tant qu'outil
custom. Les champs MCPname,descriptionetinputSchemacorrespondent un à un aux champs de l'outil personnalisé. Si le serveur pagine sa liste d'outils, déclarez toutes les pages, et le worker doit lister les mêmes pages.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())Servir les outils depuis le worker
Au démarrage, connectez-vous au même serveur MCP et convertissez ses outils avec
async_mcp_tool. Enregistrez-les ensuite aux côtés debeta_agent_toolset_20260401. Gardez une seule session MCP ouverte pendant toute la durée de vie du worker.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())
Gardez les points suivants à l'esprit lorsque vous encapsulez un serveur MCP :
-
Les outils sont déclarés, et non découverts à l'exécution. Le worker liste les outils du serveur MCP une seule fois au démarrage et ne peut pas ajouter d'outils à une session en cours. Lorsque les outils du serveur changent, déclarez-les à nouveau, puis redémarrez le worker. Vous pouvez les redéclarer sur l'agent, ou sur une session inactive via Mise à jour de la configuration de l'agent.
-
Les noms et les descriptions doivent respecter les contraintes de l'API Managed Agents.
- Les noms d'outils personnalisés sont uniques par agent et utilisent des lettres, des chiffres, des traits de soulignement et des traits d'union (1 à 128 caractères).
- Une description non vide est requise.
- Le tableau
toolsd'un agent accepte au maximum 128 entrées. Chaque outil encapsulé compte pour une entrée, et l'ensemble d'outils intégré en compte une de plus. - L'API rejette une déclaration qui réutilise un nom d'outil, qui donne à un outil personnalisé le nom d'un outil d'agent intégré tel que
bashouread, ou qui utilise le préfixe réservémcp__.
Les assistants MCP conservent les noms et les descriptions du serveur : renommez-les ou raccourcissez-les si nécessaire. Lorsque deux serveurs exposent le même nom d'outil, définissez vous-même l'encapsuleur sous un nom préfixé et faites-lui appeler le nom d'outil d'origine du serveur.
-
La plupart des schémas passent sans modification. L'API accepte les mots-clés JSON Schema que les serveurs MCP émettent couramment, tels que
additionalPropertiesettitle. Elle rejette en revanche :- les mots-clés de référence tels que
$ref, où qu'ils se trouvent dans l'input_schemad'un outil personnalisé. Intégrez donc directement les schémas que des générateurs comme pydantic factorisent dans$defs; oneOf,anyOfetallOfau niveau supérieur ;- les noms de propriétés contenant d'autres caractères que des lettres, des chiffres, des traits de soulignement, des points et des traits d'union, ou dont la longueur sort de la plage de 1 à 64 caractères.
- les mots-clés de référence tels que
-
Les échecs d'outils apparaissent sous forme de résultats d'outil en erreur. Lorsque le serveur MCP signale une erreur d'outil, le worker publie un résultat d'outil en erreur auquel le modèle peut réagir. Le contenu MCP sans équivalent en résultat d'outil, comme les blocs audio et les liens de ressources, apparaît également sous forme d'erreur.
Définissez un délai d'expiration sur le client MCP pour obtenir un échec plus rapide et plus clair, comme le fait l'exemple de worker Python avec
read_timeout_seconds. Sans délai d'expiration, un appel bloqué ne devient un résultat en erreur que lorsque l'un des mécanismes suivants se déclenche :- le délai d'expiration de requête par défaut du SDK MCP TypeScript (environ une minute) ;
- le filet de sécurité propre au worker : environ deux minutes et demie en Python, et deux minutes en Go, où le worker annule un appel d'outil qui dépasse son délai par défaut de 120 secondes et publie un résultat en erreur.
-
N'encapsulez que des serveurs que vous exploitez ou auxquels vous faites confiance. Le nom, la description et les résultats d'un outil encapsulé entrent dans le contexte du modèle comme ceux de n'importe quel autre outil. Il s'agit d'une entrée non fiable qui peut influencer ce que l'agent fait avec ses autres outils, y compris
bashsur l'hôte du worker. Ne déclarez que les outils que vous souhaitez voir utilisés par l'agent. -
Les politiques d'autorisation ne s'appliquent pas aux outils personnalisés. Les politiques d'autorisation régissent les ensembles d'outils intégrés et MCP. Le worker exécute chaque appel d'outil encapsulé que fait le modèle : placez donc toute étape d'approbation dans votre propre code d'outil.
Surveillance et opérations
Ces appels s'exécutent depuis vos outils de surveillance ou d'exploitation, authentifiés avec votre clé API Claude, afin d'observer et de gérer la flotte de workers. La boucle de réclamation et de maintien en vie est gérée à l'intérieur des assistants du worker ; vous n'appelez donc pas ces points de terminaison directement.
Lire la profondeur de la file d'attente
work.stats renvoie l'état de la file d'attente d'un environnement :
depthest le nombre d'éléments en attente de réclamation. Dimensionnez votre flotte de workers ou déclenchez des alertes sur l'arriéré en fonction de cette valeur.pendingest le nombre d'éléments réclamés par un worker mais pas encore acquittés. Les assistants du worker acquittent chaque élément avant de le traiter ; cette valeur reste donc proche de zéro en fonctionnement normal. Une valeur non nulle persistante signifie qu'un worker s'est bloqué entre la réclamation et l'acquittement.oldest_queued_atest l'horodatage de l'élément le plus ancien encore dans la file d'attente, en attente de réclamation ou réclamé mais pas encore acquitté, ounulls'il n'y en a aucun.workers_pollingest le nombre de workers ayant interrogé la file au cours des 30 dernières secondes. Utilisez cette valeur pour les alertes de disponibilité.
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
}Arrêter une session proprement
Utilisez work.stop pour demander au worker qui gère une session spécifique de l'arrêter. Par défaut, l'élément de travail passe à l'état stopping : le worker le remarque lors de sa prochaine pulsation de bail, annule l'appel d'outil en cours de la session et confirme l'arrêt, moment auquel l'élément de travail passe à l'état stopped. Transmettez force: true dans le corps de la requête (avec la CLI, transmettez --force) pour marquer immédiatement l'élément de travail comme stopped au lieu d'attendre la confirmation du worker.
Comme ces appels s'exécutent depuis vos outils d'exploitation plutôt que depuis l'hôte du worker, ANTHROPIC_WORK_ID n'est pas défini automatiquement. Définissez-le sur l'ID de l'élément de travail cible avant d'exécuter les exemples suivants. Pour trouver l'ID d'un élément de travail, listez les éléments de travail de l'environnement via les points de terminaison 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)Étapes suivantes
Modèle de responsabilité partagée pour les environnements de sandbox auto-hébergés.
Créez une session pour exécuter votre agent et commencer à exécuter des tâches.
Connectez Claude en toute sécurité à des serveurs MCP s'exécutant dans votre réseau privé, sans ouvrir de ports entrants ni exposer de services à l'internet public.
Was this page helpful?