Agenti ospitati autonomamente come strumenti MCP

Annotazioni

Il supporto per lo strumento MCP per l’hosting autonomo in .NET sarà presto disponibile.

Go espone un agente come strumento MCP combinando tool/agenttool, tool/mcptoole MCP Go SDK ufficiale. Eseguire il wrapping dell'agente con agenttool.New, registrarlo con mcptool.AddTool e pubblicarlo con un trasporto MCP.

L'esempio di Foundry mantenuto usa stdio. Richiede un endpoint del progetto Foundry, una distribuzione del modello e una credenziale 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)
    }
}

Per istruzioni sull'esecuzione, vedere Integrazione degli strumenti MCP per i modelli client e server e l'esempio completo del server MCP .

Usare agent-framework-hosting-mcp per esporre un agente o un flusso di lavoro di Agent Framework come strumento nell'SDK del protocollo di contesto del modello nativo. Il pacchetto non sceglie un framework Web o esegue il wrapping del ciclo di vita del server MCP SDK. L'applicazione possiede ancora il server MCP, la registrazione del gestore, il trasporto, i criteri della chiave di sessione, l'autenticazione, l'autorizzazione e la distribuzione.

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

Convertire al limite del protocollo

mcp_to_run(...) converte gli argomenti dello strumento MCP convalidati in messaggi di Agent Framework e le opzioni di chat selezionate e mcp_from_run(...) converte una risposta completata in valori MCP ContentBlock nativi. Usare queste due funzioni direttamente quando il contratto degli strumenti di un'applicazione richiede uno schema e un gestore nativi completamente personalizzati:

                    "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 i nomi degli argomenti elencati in vengono copiati in chat_option_argumentsrun["options"]. Gli altri argomenti MCP rimangono disponibili nella rappresentazione non elaborata del messaggio, ma non vengono inoltrati al client del modello.

Host con MCPServer

Per le applicazioni MCP SDK 2.x, usare MCPServer per derivare uno schema dello strumento MCP nativo da una funzione decorata. Restituisci CallToolResult in modo esplicito dopo la conversione della risposta completata di Agent Framework:

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."""

L'esempio usa HTTP streamabile senza stato. Aggiungere l'autenticazione, l'autorizzazione e la persistenza della sessione gestite dall'applicazione prima di esporre il server oltre un ambiente locale attendibile.

Ospitare un agente come singolo strumento generato

AgentMCPTool ricava da un agente il nome, la descrizione e lo schema dello strumento nativo e mantiene allineati la visualizzazione nell'elenco, il parsing, l'esecuzione e la conversione dei risultati, impedendo che i due si disallineino:

) -> 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 usa il nome e la descrizione dell'agente a meno che non venga eseguito l'override. parameters aggiunge proprietà dello schema JSON di proprietà dell'app che rimangono disponibili negli argomenti MCP non elaborati e chat_option_parameters aggiunge proprietà i cui valori vengono copiati in modo esplicito nelle opzioni di chat di Agent Framework.

Mantenere una sessione per ogni chiamata

Passa un AgentState esistente e un session_id_parameter per consentire a chiamate ripetute con lo stesso session_id opaco definito dall'app di continuare una conversazione:

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 esegue solo la sequenza get/run/set della sessione AgentState; l'applicazione deve autenticare o autorizzare l'identificatore di sessione e serializzare le chiamate concorrenti per la stessa sessione, come fa l'esempio con un asyncio.Lock per sessione. Non si tratta di una ramificazione in stile previous_response_id: un'applicazione che deve diramare una conversazione dovrebbe accettare ID di origine e di destinazione separati, copiare la sessione di origine e memorizzare il risultato con la chiave di destinazione.

Ospitare un flusso di lavoro come strumento

WorkflowMCPTool ricava uno strumento MCP nativo dal tipo di input dell'esecutore di avvio di un flusso di lavoro e converte gli output dei flussi di lavoro completati. Dataclass, Pydantic e altri input a forma di oggetto diventano argomenti MCP di primo livello; gli input primitivi vengono inclusi in un nome di argomento configurabile:



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

Le istanze del flusso di lavoro mantengono lo stato di esecuzione, quindi le applicazioni che necessitano di chiamate indipendenti devono fornire una WorkflowState factory con cache_target=False, come illustrato in precedenza. Il ripristino dei checkpoint, le risposte human-in-the-loop e gli identificatori di continuazione rimangono gestiti dall'applicazione; se un workflow richiede input esterno, l'adattatore genera un'eccezione anziché restituire un risultato vuoto dello strumento con esito positivo.

Per il set completo di server eseguibili, inclusa la MCPServer variante, vedere gli esempi di hosting MCP.

Importante

Considera l'identificatore di sessione MCP e qualsiasi argomento session_id specificato dall'app come input non attendibile. Autenticare e autorizzare il chiamante prima di utilizzare uno dei due per caricare o salvare lo stato della sessione e derivare il partizionamento durevole dal tenant, dall'utente o dall'area di lavoro autenticati anziché dal valore grezzo.

Passaggi successivi

Approfondimento: