Claude Platform Docs
Managed AgentsSandbox auto-hébergées

Outils personnalisés dans les sandboxes auto-hébergées

Servez des outils personnalisés depuis un worker de sandbox auto-hébergée, et encapsulez un serveur MCP situé dans votre réseau sous forme d'outils personnalisés sans exécuter de tunnel.

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. Votre worker peut être ce code. Comme il s'exécute dans votre sandbox, l'outil accède aux services internes, aux identifiants et à la sortie réseau 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.

Servir un outil personnalisé

  1. Déclarer l'outil sur l'agent

    Ajoutez une entrée custom aux tools de l'agent dont le name correspond à 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"]
      }
    }
  2. Enregistrer l'implémentation auprès du worker

    Transmettez l'outil via la fabrique tools du worker (voir EnvironmentWorker), 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."""
        # S'exécute sur l'hôte du worker : appelez tout ce que la sandbox peut atteindre.
        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. Si un outil est déclaré sur l'agent mais qu'aucun worker ni client ne le sert, la session se met en pause avec une raison d'arrêt requires_action. Elle reste en pause jusqu'à ce que quelque chose publie le résultat. Consultez Gestion des appels d'outils personnalisés pour le flux d'événements.

Encapsuler un serveur MCP sous forme d'outils personnalisés

Le connecteur MCP se connecte aux serveurs MCP depuis le côté 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 comme 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é :

  1. L'agent émet un événement agent.custom_tool_use.
  2. Le worker, dans votre sandbox, transmet l'appel via sa session MCP ouverte au serveur de votre réseau.
  3. Le worker publie la réponse du serveur en tant que user.custom_tool_result.

Installer un SDK MCP

Les assistants MCP côté client du SDK convertissent les outils du serveur en outils exécutables acceptés par le worker. Installez un SDK MCP aux côtés du SDK Anthropic : pip install "anthropic[mcp]" "mcp>=1.24".

Les exemples se connectent sans authentification. Pour envoyer des identifiants, configurez le http_client que vous transmettez au transport MCP.

Déclarer et servir les outils

  1. Déclarer les outils du serveur sur l'agent

    Listez les outils du serveur MCP et déclarez chacun d'eux comme un outil custom. Les champs MCP name, description et inputSchema correspondent un à un aux champs de l'outil personnalisé. Si le serveur pagine sa liste d'outils, déclarez chaque page ; 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
    # Nécessite mcp >= 1.24, qui a renommé streamablehttp_client en 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:
        # Les champs MCP correspondent un à un à une déclaration d'outil personnalisé. Le cast
        # transmet le dictionnaire de schéma au paramètre typé du SDK sans modification.
        return {
            "type": "custom",
            "name": tool.name,
            "description": tool.description or tool.name,
            "input_schema": cast(Any, tool.inputSchema),
        }
    
    
    async def main() -> None:
        # Exécutez ceci là où vous créez les agents, pas sur l'hôte worker : il
        # s'authentifie avec votre clé API Claude (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. Servir les outils depuis le worker

    Connectez-vous au même serveur MCP au démarrage, convertissez ses outils avec async_mcp_tool, et enregistrez-les aux côtés de beta_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
    # Nécessite mcp >= 1.24, qui a renommé streamablehttp_client en 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"]
        # Se connecte au serveur MCP une seule fois au démarrage et garde la session ouverte pendant
        # toute la vie du worker. Le timeout transforme un appel d'outil bloqué en un résultat
        # d'erreur plutôt qu'en un appel figé.
        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())

Limites et comportement

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 :

  1. Déclarez-les à nouveau, sur l'agent ou sur une session inactive via Mise à jour de la configuration de l'agent.
  2. Redémarrez le worker.

Les déclarations doivent respecter l'API Managed Agents

Les assistants MCP conservent les noms et descriptions du serveur, et la plupart des schémas passent sans modification. Renommez, raccourcissez ou intégrez en ligne lorsqu'une déclaration enfreint l'une de ces règles :

ChampRègle
nameUnique par agent. Lettres, chiffres, traits de soulignement et traits d'union, de 1 à 128 caractères. Ne peut pas correspondre à un outil d'agent intégré tel que bash ou read, ni utiliser le préfixe réservé mcp__.
descriptionObligatoire et non vide.
input_schemaAccepte les mots-clés JSON Schema couramment émis par les serveurs MCP, tels que additionalProperties et title. Rejette les mots-clés de référence tels que $ref où qu'ils se trouvent, ainsi que oneOf, anyOf et allOf au niveau supérieur. Les noms de propriétés utilisent des lettres, chiffres, traits de soulignement, points et traits d'union, de 1 à 64 caractères.
Le tableau tools de l'agentAu maximum 128 entrées. Chaque outil encapsulé constitue une entrée, et l'ensemble d'outils intégré en constitue une de plus.

Deux cas nécessitent un travail supplémentaire :

  • Deux serveurs exposent le même nom d'outil : définissez vous-même l'encapsulation sous un nom préfixé et faites-lui appeler le nom d'outil d'origine du serveur.
  • Un générateur tel que pydantic factorise les schémas dans $defs : intégrez ces schémas en ligne avant de déclarer l'outil.

Les échecs d'outils apparaissent sous forme de résultats d'outils 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. Consultez Un appel d'outil MCP encapsulé reste bloqué pour savoir ce qui se passe sans délai d'expiration.

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'entrées non fiables qui peuvent influencer ce que l'agent fait avec ses autres outils, y compris bash sur l'hôte du worker. Ne déclarez que les outils que vous souhaitez que l'agent utilise.

Les politiques d'autorisation ne s'appliquent pas

Les politiques d'autorisation régissent les ensembles d'outils intégrés et MCP. Le worker exécute chaque appel d'outil encapsulé effectué par le modèle ; placez donc toute étape d'approbation dans votre propre code d'outil.

Was this page helpful?