Agentes autohospedados como herramientas MCP

Note

La compatibilidad con herramientas MCP autohospedadas en .NET estará disponible próximamente.

Go expone un agente como una herramienta MCP mediante la combinación de tool/agenttool, tool/mcptool y el SDK oficial de MCP Go. Encapsula el agente con agenttool.New, regístralo con mcptool.AddTool y sírvelo a través de un transporte MCP.

El ejemplo de Foundry mantenido usa stdio. Requiere un punto de conexión de proyecto de Foundry, un despliegue del modelo y una credencial de Azure:

// Copyright (c) Microsoft. All rights reserved.

// This sample shows how to expose an Agent as an MCP tool.

package main

import (
    "context"

    "github.com/microsoft/agent-framework-go/agent"
    "github.com/microsoft/agent-framework-go/examples/internal/demo"
    "github.com/microsoft/agent-framework-go/provider/foundryprovider"
    "github.com/microsoft/agent-framework-go/tool/agenttool"
    "github.com/microsoft/agent-framework-go/tool/mcptool"
    "github.com/modelcontextprotocol/go-sdk/mcp"
)

// Run "npx @modelcontextprotocol/inspector go run ." to connect to this MCP server.

func main() {
    token := demo.FoundryTokenCredential()

    // Create Microsoft Foundry agent with the same configuration as the C# example.
    agent := foundryprovider.NewAgent(
        demo.FoundryProjectEndpoint,
        token,
        foundryprovider.ModelDeployment(demo.FoundryModel),
        foundryprovider.AgentConfig{
            Instructions: "You are good at telling jokes, and you always start each joke with 'Aye aye, captain!'.",
            Config: agent.Config{
                Name:        "Joker",
                Description: "An agent that tells jokes.",
            },
        },
    )

    // Create an MCP server with the agent exposed as a tool.
    srv := mcp.NewServer(&mcp.Implementation{
        Name:    "agent-mcp-server",
        Version: "1.0.0",
    }, nil)

    // Register the agent as an MCP tool.
    mcptool.AddTool(srv, agenttool.New(agent, agenttool.Config{}))

    // Run the MCP server with StdIO transport.
    if err := srv.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
        demo.Panic(err)
    }
}

Consulte Integración de herramientas de MCP para patrones de cliente y servidor y el ejemplo completo del servidor MCP para obtener instrucciones de ejecución.

Use agent-framework-hosting-mcp para exponer un agente de Agent Framework o un flujo de trabajo como herramienta en el SDK nativo de Model Context Protocol. El paquete no elige un marco web ni encapsula el ciclo de vida del servidor del SDK de MCP. Su aplicación sigue siendo propietaria del servidor MCP, el registro de controladores, el transporte, la directiva de clave de sesión, la autenticación, la autorización y la implementación.

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

Convertir en el límite del protocolo

mcp_to_run(...) convierte los argumentos de la herramienta MCP validados en mensajes de Agent Framework y opciones de chat seleccionadas y mcp_from_run(...) convierte una respuesta completa en valores de MCP ContentBlock nativos. Use estas dos funciones directamente cuando el contrato de herramientas de una aplicación necesite un esquema y un controlador nativos totalmente personalizados:

                    "properties": {
                        TASK_ARGUMENT: {
                            "type": "string",
                            "description": "The request for the hosted agent.",
                        },
                        **CHAT_OPTION_ARGUMENTS,
                    },
                    "required": [TASK_ARGUMENT],
                    "additionalProperties": False,
                },
            )
        ]
    )


async def call_tool(
    _ctx: ServerRequestContext[dict[str, Any]], params: types.CallToolRequestParams
) -> types.CallToolResult:
    """Convert, run, and render without the agent-backed adapter."""
    name = params.name
    arguments = params.arguments or {}

    if name != "run_agent_manually":
        raise MCPError(types.INVALID_PARAMS, 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 types.CallToolResult(content=mcp_from_run(result))


server = Server("agent-framework-hosting-mcp-manual-sample", on_list_tools=list_tools, on_call_tool=call_tool)
credential = DefaultAzureCredential()

Solo se copian en chat_option_arguments los nombres de argumentos enumerados en run["options"]; los demás argumentos de MCP siguen estando disponibles en la representación sin procesar del mensaje, pero no se reenvían al cliente del modelo.

Hospedar con MCPServer

En el caso de las aplicaciones de MCP SDK 2.x, use MCPServer para derivar un esquema nativo de la herramienta MCP de una función decorada. Devuelve CallToolResult explícitamente después de convertir la respuesta de Agent Framework completada:

from contextlib import asynccontextmanager
from typing import Literal

from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework_hosting_mcp import mcp_from_run, mcp_to_run
from azure.identity.aio import DefaultAzureCredential
from mcp import types
from mcp.server.mcpserver import MCPServer

credential = DefaultAzureCredential()
agent = Agent(
    client=FoundryChatClient(
        project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
        model=os.environ["FOUNDRY_MODEL"],
        credential=credential,
    ),
    name="MCPHostedAgent",
    description="Answer a request with the hosted Agent Framework agent.",
    instructions="Answer the user's request clearly and concisely.",
)


@asynccontextmanager
async def lifespan(_server: MCPServer[None]) -> AsyncGenerator[None]:
    """Close the model credential when the MCP server stops."""
    async with credential:
        yield


server = MCPServer(
    name="agent-framework-hosting-mcpserver-sample",
    instructions="Expose an Agent Framework agent as an MCP tool.",
    lifespan=lifespan,
)


@server.tool(
    name="run_agent",
    description="Run the hosted Agent Framework agent.",
    structured_output=False,
)
async def run_agent(
    task: str,
    reasoning_effort: Literal["low", "medium", "high"] | None = None,
) -> types.CallToolResult:
    """Run the agent with MCPServer-validated arguments."""

El ejemplo usa HTTP streamable sin estado. Agregue la autenticación, autorización y persistencia de sesión propiedad de la aplicación antes de exponer el servidor más allá de un entorno local de confianza.

Alojar un agente como una herramienta generada

AgentMCPTool obtiene de un agente el nombre, la descripción y el esquema de la herramienta nativa, y mantiene alineados el listado, el análisis sintáctico, la ejecución y la conversión de resultados para evitar que ambos se desincronicen:

) -> types.CallToolResult:
    """Run the app-owned tool with native MCP and Agent Framework values."""
    return await agent_tool.call_tool(params.name, params.arguments)


server = Server("agent-framework-hosting-mcp-sample", on_list_tools=list_tools, on_call_tool=call_tool)
credential = DefaultAzureCredential()
agent = Agent(
    client=FoundryChatClient(
        project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
        model=os.environ["FOUNDRY_MODEL"],
        credential=credential,
    ),
    name="MCPHostedAgent",
    description="Answer a request with the hosted Agent Framework agent.",
    instructions="Answer the user's request clearly and concisely.",
)
agent_tool = AgentMCPTool(
    agent,
    name="run_agent",
    argument_description="The request for the hosted agent.",
    chat_option_parameters={
        "reasoning_effort": {
            "type": "string",

AgentMCPTool utiliza el nombre y la descripción del agente, a menos que se sobrescriban. parameters agrega propiedades de esquema JSON propiedad de la aplicación que permanecen disponibles en los argumentos MCP sin procesar y chat_option_parameters agrega propiedades cuyos valores se copian explícitamente en las opciones de chat de Agent Framework.

Mantener una sesión por llamada

Pase un AgentState existente y un session_id_parameter para permitir que las llamadas repetidas con el mismo session_id, opaco y definido por la aplicación, mantengan la misma conversación:

state = AgentState(agent)
agent_tool = AgentMCPTool(
    state,
    name="run_agent",
    argument_description="The request for the hosted agent.",
    parameters={
        "session_id": {
            "type": "string",
            "minLength": 1,
            "description": (
                "Opaque, app-defined conversation key. Reuse the same value to continue and update one conversation."
            ),
        }
    },
    required_parameters={"session_id"},
    chat_option_parameters={
        "reasoning_effort": {
            "type": "string",

AgentMCPTool solo realiza la secuencia de sesión AgentState get/run/set; su aplicación debe autenticar o autorizar el identificador de sesión y serializar las llamadas concurrentes para la misma sesión, como hace el ejemplo con un asyncio.Lock por sesión. Esto no es una bifurcación al estilo de previous_response_id: una aplicación que necesite bifurcar una conversación debe aceptar identificadores de origen y destino distintos, copiar la sesión de origen y almacenar el resultado bajo la clave de destino.

Hospedar un flujo de trabajo como herramienta

WorkflowMCPTool deriva una herramienta MCP nativa a partir del tipo de entrada start-executor de un flujo de trabajo y convierte las salidas del flujo de trabajo completado. Dataclass, Pydantic y otras entradas con forma de objeto se convierten en argumentos MCP de nivel superior; Las entradas primitivas se encapsulan en un nombre de argumento configurable:



async def list_tools(
    _ctx: ServerRequestContext[dict[str, Any]], params: types.PaginatedRequestParams | None
) -> types.ListToolsResult:

Las instancias de flujo de trabajo conservan el estado de ejecución, por lo que las aplicaciones que necesitan invocaciones independientes deben proporcionar una factoría WorkflowState con cache_target=False, como se muestra anteriormente. La restauración de puntos de control, las respuestas con intervención humana y los identificadores de continuación siguen siendo propiedad de la aplicación; si un flujo de trabajo solicita una entrada externa, el adaptador genera un error en lugar de devolver un resultado vacío de la herramienta indicando que la operación se ha realizado con éxito.

Para obtener el conjunto completo de servidores ejecutables, incluida la MCPServer variante, consulte los ejemplos de hospedaje de MCP.

Important

Considere el identificador de sesión de MCP y cualquier argumento session_id definido por la aplicación como entradas no confiables. Autentifica y autoriza al solicitante antes de utilizar cualquiera de ellos para cargar o guardar el estado de la sesión, y deriva la partición duradera del inquilino, usuario o espacio de trabajo autenticado, en lugar del valor sin procesar.

Pasos siguientes

Vaya más profundamente: