自承载代理作为 MCP 工具

注释

对在 .NET 中自承载 MCP 工具的支持即将推出。

Go 通过组合tool/agenttooltool/mcptool和官方 MCP Go SDK 将代理公开为 MCP 工具。 使用 agenttool.New 包装该代理,使用 mcptool.AddTool 注册它,并通过 MCP 传输提供服务。

维护的 Foundry 示例使用 stdio。 它需要 Foundry 项目终结点、模型部署和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)
    }
}

有关客户端和服务器模式,请参阅 MCP 工具集成 以及 完整的 MCP 服务器示例 ,了解执行说明。

使用 agent-framework-hosting-mcp 在原生 模型上下文协议 SDK 上将 Agent Framework 代理或工作流公开为工具。 该软件包不指定 Web 框架,也不封装 MCP SDK 服务器生命周期。 应用程序仍拥有 MCP 服务器、处理程序注册、传输、会话密钥策略、身份验证、授权和部署。

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

在协议边界处转换

mcp_to_run(...) 将验证的 MCP 工具参数转换为代理框架消息和所选聊天选项,并将 mcp_from_run(...) 已完成的响应转换为本机 MCP ContentBlock 值。 当应用程序的工具协定需要完全自定义的本机架构和处理程序时,请直接使用这两个函数:

                    "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()

仅将列出的 chat_option_arguments 参数名称复制到 run["options"];其他 MCP 参数在消息的原始表示形式上保持可用,但不会转发到模型客户端。

使用 MCPServer 进行托管

对于 MCP SDK 2.x 应用程序,使用 MCPServer 从修饰的函数中派生原生 MCP 工具架构。 转换已完成的 Agent Framework 响应后显式返回 CallToolResult :

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

此示例使用无状态可流式传输 HTTP。 在将服务器暴露到受信任的本地环境之外之前,添加应用程序拥有的身份验证、授权和会话持久性。

将代理作为一个已生成的工具进行托管

AgentMCPTool 从代理派生原生工具名称、描述和模式,并使列表、解析、执行和结果转换始终保持一致,从而避免二者出现偏离:

) -> 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 使用智能体的名称和描述。 parameters 添加应用程序拥有的 JSON 模式属性,这些属性在原始 MCP 参数中保持可用;而 chat_option_parameters 添加的属性,其值会被显式复制到智能体框架的聊天选项中。

按调用保留会话

传递现有的 AgentState 和 session_id_parameter,以便使用相同的不透明、应用程序定义的 session_id 的重复调用继续同一对话:

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 仅执行 AgentState 会话获取/运行/设置序列;应用程序必须对会话标识符进行身份验证或授权,并序列化同一会话的并发调用,就像示例对每个会话 asyncio.Lock执行的操作一样。 这不是 previous_response_id样式分支 — 需要分叉会话的应用程序应接受单独的源 ID 和目标 ID,复制源会话,并将结果存储在目标密钥下。

将工作流作为工具托管

WorkflowMCPTool 根据工作流启动执行器的输入类型生成一个原生 MCP 工具,并转换已完成工作流的输出。 数据类、Pydantic 和其他对象形输入成为顶级 MCP 参数;基元输入包装在可配置的参数名称中:



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

工作流实例会保留执行状态,因此需要独立调用的应用程序应如上所示,提供一个包含 cache_target=False 的 WorkflowState 生成器。 检查点恢复、人工介入响应和延续标识符仍由应用程序管理;如果工作流请求外部输入,适配器会抛出异常,而不是返回一个空的成功工具结果。

有关完整的可运行服务器集(包括 MCPServer 变体),请参阅 MCP 托管示例。

重要

将 MCP 会话标识符和任何应用定义 session_id 参数视为不受信任的输入。 在使用二者之一加载或保存会话状态之前,先对调用方进行身份验证和授权,并且应基于已验证的租户、用户或工作区来确定持久分区,而不是基于原始值。

后续步骤

更深入: