Microsoft Foundry 工具箱

Microsoft Foundry 工具箱是托管工具配置的命名、版本控制的服务器端捆绑包,例如代码解释器、文件搜索、图像生成、MCP 和 Web 搜索。 工具箱允许你在 Foundry 中管理工具配置一次,并在代理之间重复使用它。

代理框架支持 Toolbox 的使用。 通过 Foundry 门户或 azure-ai-projects SDK 创建和更新工具箱版本。

重要

FoundryToolbox 由 beta agent-framework-foundry-hosting 包提供,可以在稳定发布之前进行更改。

对于由服务管理的 FoundryAgent,请将工具箱附加到 Foundry 中的代理定义。

对于使用 Microsoft Agent Framework 构建的托管代理,请使用来自 Microsoft.Agents.AI.Foundry.Hosting 的 AddFoundryToolboxes,如以下示例所示。

使用一个启用了隐式 using 的 .NET 10 Web 项目,并确保 Microsoft.Agents.AI.Foundry、Microsoft.Agents.AI.Foundry.Hosting 和 DotNetEnv 的版本相互匹配。 将 TOOLBOX_NAME 设置为现有工具箱,将 FOUNDRY_MODEL 设置为你的模型部署。 Foundry 向已部署的主机提供 FOUNDRY_PROJECT_ENDPOINT。 对于本地模型访问,请使用Azure CLI设置FOUNDRY_PROJECT_ENDPOINT和登录。 托管部署回退到 azd 管理的 AZURE_AI_MODEL_DEPLOYMENT_NAME。 当 FOUNDRY_PROJECT_ENDPOINT 可用时,托管集成会加载工具箱工具。

using DotNetEnv;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Foundry.Hosting;

// Load .env file if present (for local development)
Env.TraversePath().Load();

string endpoint = System.Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
    ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
string deploymentName = FirstNonBlank(
    System.Environment.GetEnvironmentVariable("FOUNDRY_MODEL"),
    System.Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME"),
    "gpt-4o")!;
string toolboxName = FirstNonBlank(
    System.Environment.GetEnvironmentVariable("TOOLBOX_NAME"),
    "my-toolset")!;

// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
// Use a chained credential: try a temporary dev token first (for local Docker debugging),
// then fall back to DefaultAzureCredential (for local dev via dotnet run / managed identity in production).
    .AsAIAgent(
        model: deploymentName,
            Use the available tools to answer user questions.
            If a tool is not available for a request, let the user know clearly.
            """,
        name: System.Environment.GetEnvironmentVariable("AGENT_NAME") ?? "hosted-toolbox-agent",
        description: "Hosted agent backed by Foundry Toolbox MCP tools");

// ── Build the host ────────────────────────────────────────────────────────────

var builder = WebApplication.CreateBuilder(args);

// Register the agent and response handler
// Register Foundry Toolbox: connects to the MCP proxy at startup and makes tools available.
// The toolbox name must match a toolbox registered in your Foundry project.
// When FOUNDRY_PROJECT_ENDPOINT is absent (e.g., in local development without Foundry
// infrastructure), startup succeeds without error and no toolbox tools are loaded.
builder.Services.AddFoundryToolboxes(credential, toolboxName);

app.Run();

static string? FirstNonBlank(params string?[] candidates) =>
    Array.Find(candidates, candidate => !string.IsNullOrWhiteSpace(candidate));

// ── DevTemporaryTokenCredential ───────────────────────────────────────────────

同一托管注册支持那些在其工具箱连接上配置为按用户进行 OAuth 同意的工具。 用户仍然需要具备所需的权限并获得同意;不需要单独的主机注册限制。

有关项目文件和部署说明,请参阅 Hosted-Toolbox。 有关按用户进行的同意配置,请参阅 Hosted-Toolbox-AuthPaths。

安装软件包

pip install agent-framework-foundry-hosting agent-framework-foundry --pre

FoundryToolbox从agent_framework.foundry导入,由agent-framework-foundry-hosting提供。

配置工具箱

为 Toolbox MCP 明确设置一个端点:

TOOLBOX_ENDPOINT="https://<account>.services.ai.azure.com/api/projects/<project>/toolboxes/<name>/mcp?api-version=v1"

或者让 FoundryToolbox 构造终结点:

FOUNDRY_PROJECT_ENDPOINT="https://<account>.services.ai.azure.com/api/projects/<project>"
TOOLBOX_NAME="<toolbox-name>"

托管代理示例在本地优先使用 FOUNDRY_MODEL,并在托管时回退到由 azd 管理的 AZURE_AI_MODEL_DEPLOYMENT_NAME。

将 FoundryToolbox 与托管代理配合使用

FoundryToolbox解析其终结点,使用提供的Azure凭据对每个 MCP 请求进行身份验证,转发 Foundry 每请求调用 ID,并参与代理的连接生命周期。 它会解析每个操作边界上的平台标头,并在有效请求标识发生更改时重新连接其 MCP 会话。 因此,长期存在的 Toolbox 不会保留先前调用方的呼叫 ID。

示例在请求范围的智能体工厂中创建工具箱、客户端和凭据,以实现确定性的所有权和清理。 当相关资源由请求拥有时,使用此工厂模式,但它不是仅用于保持工具箱调用 ID 最新的必需项。

import asyncio
import os
from contextlib import AsyncExitStack
from types import TracebackType

from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient, FoundryToolbox
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.ai.agentserver.core import AgentConfig, get_request_context
from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential
from dotenv import load_dotenv


def create_agent() -> Agent:
    """Create request-owned SDK resources and close them with the agent."""
    endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
    model = os.environ.get("FOUNDRY_MODEL") or os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"]
    credential = (
        ManagedIdentityCredential(client_id=os.environ.get("FOUNDRY_AGENT_INSTANCE_CLIENT_ID"))
        if AgentConfig.from_env().is_hosted
        else AzureCliCredential()
    )

    class RequestClient(FoundryChatClient):
        async def __aenter__(self) -> RequestClient:
            return self

        async def __aexit__(
            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
        ) -> None:
            async with AsyncExitStack() as cleanup:
                cleanup.push_async_callback(credential.close)
                cleanup.push_async_callback(self.project_client.close)
                cleanup.push_async_callback(self.client.close)

    toolbox = FoundryToolbox(credential)
    client = RequestClient(
        project_endpoint=endpoint,
        model=model,
        credential=credential,
        default_headers=get_request_context().platform_headers(),
    )
    return Agent(
        client=client,
        instructions="You are a friendly assistant. Keep your answers brief.",
        tools=toolbox,
    )


async def main() -> None:
    load_dotenv()
    server = ResponsesHostServer(agent=create_agent, history_source="agent_server")
    await server.run_async()

当工具箱代码解释器生成一个文件且助手响应中提及该文件名时,托管的响应适配器会发出包含容器和文件 ID 的本机 container_file_citation 批注。 响应客户端可以使用这些 ID 通过容器文件 API 发现并下载生成的文件。

公开工具箱技能

工具箱可以通过 MCP 公开代理技能。 如果只有技能应对模型可见,请设置load_tools=False,然后将工具箱添加为工具,以便其 MCP 会话建立连接,并将as_skills_provider()用作上下文提供程序。

import asyncio
import os
from contextlib import AsyncExitStack
from types import TracebackType

from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient, FoundryToolbox
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.ai.agentserver.core import AgentConfig, get_request_context
from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential
from dotenv import load_dotenv


def create_agent() -> Agent:
    """Keep skill caches, credentials and the MCP lifecycle request-owned."""
    endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
    model = os.environ.get("FOUNDRY_MODEL") or os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"]
    credential = (
        ManagedIdentityCredential(client_id=os.environ.get("FOUNDRY_AGENT_INSTANCE_CLIENT_ID"))
        if AgentConfig.from_env().is_hosted
        else AzureCliCredential()
    )

    class RequestClient(FoundryChatClient):
        async def __aenter__(self) -> RequestClient:
            return self

        async def __aexit__(
            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
        ) -> None:
            async with AsyncExitStack() as cleanup:
                cleanup.push_async_callback(credential.close)
                cleanup.push_async_callback(self.project_client.close)
                cleanup.push_async_callback(self.client.close)

    # tools= connects the MCP session; context_providers= reads skills from that same session.
    toolbox = FoundryToolbox(credential, load_tools=False)
    skills_provider = toolbox.as_skills_provider(disable_load_skill_approval=True)
    client = RequestClient(
        project_endpoint=endpoint,
        model=model,
        credential=credential,
        default_headers=get_request_context().platform_headers(),
    )
    return Agent(
        client=client,
        name=os.environ.get("AGENT_NAME", "hosted-toolbox-mcp-skills"),
        instructions="You are a helpful assistant.",
        tools=toolbox,
        context_providers=[skills_provider],
    )


async def main() -> None:
    load_dotenv()
    server = ResponsesHostServer(agent=create_agent, history_source="agent_server")
    await server.run_async()

技能操作的审批功能默认保持启用。 仅在受信任的无人值守场景中禁用单独审批。 将工具箱及其技能提供程序放在一起,以便两者都使用相同的 MCP 会话。 对于长期存在的工具箱,当有效的平台标头标识更改时,技能发现缓存将被替换。 与技能一起使用的自定义 header_provider 必须从环境状态解析,例如闭包或 ContextVar,因为技能和资源读取不接收函数运行时参数。

使用带有 FoundryAgent 的工具箱

将工具箱附加到 Foundry 中的提示代理或托管代理定义。 FoundryAgent使用已存储的工具配置;在客户端传递工具箱不会将其添加到托管代理。

通过 MCP 与FoundryToolbox连接

使用 FoundryToolbox 配合 ResponsesHostServer,将托管代理连接到 Toolbox MCP 终结点。 该包装器对 MCP 请求进行身份验证,并转发当前托管请求的调用方上下文,以实现按用户的身份透传。 当客户端、凭据和工具箱需要一个清理边界时,请求范围的 agent 工厂仍然是最简单的选项。 还支持长期存在的 Toolbox,并且当平台标头标识发生更改时,会在操作前重新连接。

import asyncio
import os
from contextlib import AsyncExitStack
from types import TracebackType

from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient, FoundryToolbox
from agent_framework_foundry_hosting import ResponsesHostServer
from azure.ai.agentserver.core import AgentConfig, get_request_context
from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential
from dotenv import load_dotenv


def create_agent() -> Agent:
    """Create request-owned SDK resources and close them with the agent."""
    endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
    model = os.environ.get("FOUNDRY_MODEL") or os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"]
    credential = (
        ManagedIdentityCredential(client_id=os.environ.get("FOUNDRY_AGENT_INSTANCE_CLIENT_ID"))
        if AgentConfig.from_env().is_hosted
        else AzureCliCredential()
    )

    class RequestClient(FoundryChatClient):
        async def __aenter__(self) -> RequestClient:
            return self

        async def __aexit__(
            self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None
        ) -> None:
            async with AsyncExitStack() as cleanup:
                cleanup.push_async_callback(credential.close)
                cleanup.push_async_callback(self.project_client.close)
                cleanup.push_async_callback(self.client.close)

    toolbox = FoundryToolbox(credential)
    client = RequestClient(
        project_endpoint=endpoint,
        model=model,
        credential=credential,
        default_headers=get_request_context().platform_headers(),
    )
    return Agent(
        client=client,
        instructions="You are a friendly assistant. Keep your answers brief.",
        tools=toolbox,
    )


async def main() -> None:
    load_dotenv()
    server = ResponsesHostServer(agent=create_agent, history_source="agent_server")
    await server.run_async()

设置 TOOLBOX_ENDPOINT,或同时设置 FOUNDRY_PROJECT_ENDPOINT 和 TOOLBOX_NAME,如 配置工具箱 中所述。 为本地运行设置 FOUNDRY_MODEL;托管部署将回退到由 azd 管理的 AZURE_AI_MODEL_DEPLOYMENT_NAME。

当工具箱或 MCP 操作需要 OAuth 同意时,流式处理和非流式处理 FoundryAgent 以及 FoundryChatClient 调用会返回带有同意链接的 Content(type="oauth_consent_request"),而不是空助理消息。 将此内容作为用户输入请求处理,并将其consent_link呈现给用户。

客户端仅显示具有 HTTPS 协议和有效主机的链接。 缺少或无效的链接将被忽略并记录。

Limitations

  • 工具箱中的 MCP 工具通过 Foundry project_connection_id使用服务器端身份验证;代理框架客户端不保存上游 MCP 持有者令牌。
  • 将 Toolbox 用作 MCP 服务器时,需要对 Toolbox 端点进行客户端 Entra ID 身份验证。
  • 同意流响应(例如 CONSENT_REQUIRED ,在代理运行时处理),而不是在创建工具箱连接时处理。

Samples

Sample 说明
foundry_toolbox/main.py FoundryToolbox 使用托管响应代理
foundry_toolbox_mcp_skills/main.py 由工具箱支持的代理技能
foundry_chat_client_with_toolbox.py 使用 MCPStreamableHTTPTool 的工具箱 MCP 使用量
foundry_chat_client_with_toolbox_skills.py 由工具箱支持的技能配置
invoke_foundry_toolbox_mcp 工作流端 MCP 消耗

Go 目前尚未提供 Foundry 工具箱辅助函数。 通过 Foundry 配置工具箱,并使用用于 Go 代理的受支持的本地或托管工具声明。