Agents auto-hébergés comme outils MCP

Note

La prise en charge de l’auto-hébergement des outils MCP dans .NET sera bientôt disponible.

Note

La prise en charge de l’outil MCP en auto-hébergement n’est pas actuellement assurée pour Go.

Utilisez agent-framework-hosting-mcp pour exposer un agent du framework Agent Framework ou un flux de travail en tant qu’outil dans le SDK natif Model Context Protocol. Le package ne choisit pas de framework web et n’encapsule pas le cycle de vie du serveur du SDK MCP ; votre application reste responsable de l’enregistrement du Server, du transport, de la stratégie de clé de session, de l’authentification, de l’autorisation et du déploiement.

pip install --pre agent-framework-hosting-mcp

Convertir au niveau de la limite du protocole

mcp_to_run(...) convertit les arguments de l’outil MCP validés en messages Agent Framework et les options de conversation sélectionnées et mcp_from_run(...) convertit une réponse terminée en valeurs MCP ContentBlock natives. Utilisez ces deux fonctions directement quand le contrat d’outil d’une application a besoin d’un schéma et d’un gestionnaire natifs entièrement personnalisés :

@server.list_tools()
async def list_tools() -> list[types.Tool]:
    """Return the app-owned native MCP tool definition."""
    return [
        types.Tool(
            name="run_agent_manually",
            description=agent.description or "",
            inputSchema={
                "type": "object",
                "properties": {
                    TASK_ARGUMENT: {
                        "type": "string",
                        "description": "The request for the hosted agent.",
                    },
                    **CHAT_OPTION_ARGUMENTS,
                },
                "required": [TASK_ARGUMENT],
                "additionalProperties": False,
            },
        )
    ]


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, object] | None) -> list[types.ContentBlock]:
    """Convert, run, and render without the agent-backed adapter."""
    if name != "run_agent_manually":
        raise ValueError(f"Unknown MCP tool: {name}")
    run = mcp_to_run(
        arguments,
        argument_name=TASK_ARGUMENT,
        chat_option_arguments=CHAT_OPTION_ARGUMENTS,
    )
    result = await agent.run(run["messages"], options=run["options"])
    return mcp_from_run(result)

Seuls les noms d’arguments répertoriés dans chat_option_arguments sont copiés run["options"]; les autres arguments MCP restent disponibles sur la représentation brute du message, mais ne sont pas transférés au client de modèle.

Héberger un agent en tant qu’outil généré

AgentMCPTool extrait d’un agent le nom, la description et le schéma de l’outil natif, et maintient alignés l’énumération, l’analyse syntaxique, l’exécution et la conversion des résultats afin d’éviter toute divergence entre les deux :

agent_tool = AgentMCPTool(
    agent,
    name="run_agent",
    argument_description="The request for the hosted agent.",
    chat_option_parameters={
        "reasoning_effort": {
            "type": "string",
            "enum": ["low", "medium", "high"],
            "description": "Optional reasoning effort for models that support it.",
        }
    },
)


@server.list_tools()
async def list_tools() -> list[types.Tool]:
    """Describe the app-owned MCP tool schema."""
    return await agent_tool.list_tools()


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, object] | None) -> list[types.ContentBlock]:
    """Run the app-owned tool with native MCP and Agent Framework values."""
    return await agent_tool.call_tool(name, arguments)

AgentMCPTool utilise le nom et la description de l’agent, sauf remplacement. parameters ajoute des propriétés de schéma JSON appartenant à l’application qui restent disponibles dans les arguments MCP bruts et chat_option_parameters ajoute des propriétés dont les valeurs sont explicitement copiées dans les options de conversation Agent Framework.

Conserver une session par appel

Transmettez un AgentState existant et un session_id_parameter pour permettre à des appels répétés avec le même session_id opaque défini par l’application de poursuivre une même conversation :

session_locks: dict[str, asyncio.Lock] = {}


@server.list_tools()
async def list_tools() -> list[types.Tool]:
    """Return the agent-derived MCP tool definition."""
    return await agent_tool.list_tools()


@server.call_tool()
async def call_tool(name: str, arguments: dict[str, object] | None) -> list[types.ContentBlock]:
    """Serialize calls per app-owned session before using ``AgentState``."""
    session_id = arguments.get("session_id") if arguments else None
    if not isinstance(session_id, str) or not session_id:
        raise ValueError("MCP tool argument 'session_id' must be a non-empty string.")
    lock = session_locks.setdefault(session_id, asyncio.Lock())
    async with lock:
        return await agent_tool.call_tool(name, arguments)

AgentMCPTool effectue uniquement la séquence get/run/set de session AgentState ; votre application doit authentifier ou autoriser l’identificateur de session et sérialiser les appels simultanés pour la même session, comme l’exemple le fait avec une session par session asyncio.Lock. Il ne s’agit pas d’un embranchement de type previous_response_id : une application qui doit dupliquer une conversation devrait accepter des identifiants source et destination distincts, copier la session source et stocker le résultat sous la clé de destination.

Héberger un flux de travail en tant qu’outil

WorkflowMCPTool génère un outil MCP natif à partir du type d’entrée de l’exécuteur de démarrage d’un flux de travail et convertit les sorties d’un flux de travail terminé. Dataclass, Pydantic et d’autres entrées en forme d’objet deviennent des arguments MCP de niveau supérieur ; les entrées primitives sont encapsulées dans un nom d’argument configurable :

server = Server("agent-framework-hosting-mcp-workflow-sample")
workflow_tool = WorkflowMCPTool(
    WorkflowState(create_workflow, cache_target=False),
    name="draft_content",
)

Les instances de workflow conservent l’état d’exécution. Les applications qui requièrent des appels indépendants doivent donc fournir une fabrique WorkflowState avec cache_target=False, comme illustré ci-dessus. La restauration des points de contrôle, les réponses de boucles humaines et les identificateurs de continuation restent appartenant à l’application ; si un flux de travail demande une entrée externe, l’adaptateur déclenche au lieu de retourner un résultat d’outil réussi vide.

Pour obtenir l’ensemble complet de serveurs exécutables, y compris la variante FastMCP qui dérive son schéma d’une fonction décorée, consultez les exemples d’hébergement MCP.

Important

Traitez l’identificateur de session MCP et tout argument défini par session_id l’application comme une entrée non approuvée. Authentifiez et autorisez l’appelant avant de charger ou d’enregistrer l’état de session, et dérivez le partitionnement durable du locataire authentifié, de l’utilisateur ou de l’espace de travail plutôt que de la valeur brute.

Étapes suivantes

Aller plus loin :