OpenAI Agents SDK #28:HostedMCPTool 完全拆解——HostedTool 家族第三成员,2 个外层参数 + 6 个 tool_config 字段
本期深拆 HostedMCPTool——HostedTool 家族第三成员(前两期已覆盖 WebSearchTool 和 FileSearchTool)。详解 2 个外层参数(tool_config、on_approval_request)和 6 个 tool_config 字段(type/server_label/server_url/headers/require_approval/allowed_tools),重点展开 require_approval 的人在回路审批机制、MCPToolApprovalRequest 完整回调结构、allowed_tools 最小权限设计,并横向对比 HostedMCPTool / MCPServerSse / MCPServerStdio 三套机制的 8 维度差异矩阵。附 3 条生产建议:最小权限分层设计、Responses API 架构约束确认、静态 token 刷新策略。
리서치 브리프
HostedTool 家族有三个成员:WebSearchTool(#26 深拆)、FileSearchTool(#27 深拆),今天是第三个——
HostedMCPTool。它做的事情只有一句话:让 Agent 无需管理本地进程或网络连接,直接通过 OpenAI 服务器调用任意远程 MCP 服务器的工具。
但这个「无需管理」背后藏着几个值得细看的设计决策。
MCP 在 SDK 里的两条路
在进入
HostedMCPTool 之前,先厘清一个经常被混淆的问题:SDK 里的 MCP 支持其实有两套独立机制,走的是完全不同的路。第一套:
mcp_servers 参数 + MCPServerSse / MCPServerStdioSDK 在每次 Agent 运行时调用
list_tools() 和 call_tool(),由本地 SDK 进程直接与 MCP 服务器通信。需要用 async context manager 管理连接生命周期1:async with MCPServerSse(
name="My SSE Server",
params={"url": "https://example.com/sse"}
) as server:
agent = Agent(
name="Assistant",
instructions="Use the tools to achieve the task",
mcp_servers=[server]
)第二套:
tools 参数 + HostedMCPTool工具调用请求发到 OpenAI 服务器,由 OpenAI 平台代理完成对远程 MCP 服务器的实际调用。Agent 代码侧不管理任何连接——和使用
WebSearchTool、FileSearchTool 的体验完全一致:from agents import Agent, Runner, HostedMCPTool
tool = HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "my_service",
"server_url": "https://example.com/mcp",
"require_approval": "never"
}
)
agent = Agent(
name="Assistant",
tools=[tool]
)两套机制的核心差异:第一套 SDK 自己跑 MCP 客户端,第二套 OpenAI 平台跑 MCP 客户端。
HostedMCPTool 的完整参数
HostedMCPTool 是一个 dataclass,只有两个外层参数:| 参数 | 类型 | 说明 |
|---|---|---|
tool_config | dict | 必填。包含连接配置的字典(见下表) |
on_approval_request | Callable | 可选。仅当 require_approval 触发时生效的自定义审批函数 |
tool_config 的 6 个字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | str | ✅ | 固定为 "mcp" |
server_label | str | ✅ | 服务器唯一标识符,用于日志和 tracing |
server_url | str | ✅ | 远程 MCP 服务器的 HTTP/HTTPS 地址 |
headers | dict | ⬜ | 认证请求头,如 Authorization、自定义 API Key |
require_approval | str | ⬜ | "never" 或 "always",默认 "never" |
allowed_tools | list[str] | ⬜ | 工具白名单,不在列表内的工具对 Agent 不可见 |
require_approval:人在回路的开关
这是
HostedMCPTool 里最值得单独拆的参数。require_approval: "always" 意味着 Agent 每次想调用 MCP 工具之前,SDK 会先触发 on_approval_request 回调——由你的代码决定放行还是拦截。2from agents import (
Agent, Runner, HostedMCPTool,
MCPToolApprovalRequest, MCPToolApprovalFunctionResult
)
import os
# 安全操作:直接放行;危险操作:拦截
SAFE_TOOLS = {"list_tasks", "get_project", "read_file"}
RESTRICTED_TOOLS = {"delete_task", "delete_project", "write_file"}
def approve_tool_call(
request: MCPToolApprovalRequest
) -> MCPToolApprovalFunctionResult:
tool_name = request.data.name
if tool_name in SAFE_TOOLS:
return {"approve": True}
elif tool_name in RESTRICTED_TOOLS:
return {"approve": False, "reason": "写入/删除操作需要人工确认"}
else:
# 未知工具:默认拒绝
return {"approve": False, "reason": f"未知工具 {tool_name},需人工审核"}
tool = HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "file_service",
"server_url": "https://example.com/mcp",
"headers": {"Authorization": f"Bearer {os.getenv('API_TOKEN')}"},
"require_approval": "always"
},
on_approval_request=approve_tool_call
)request.data.name 包含当前待调用的工具名,据此做细粒度决策。对比之前在 #14「Human in the Loop」中介绍的
interrupt 机制:那套方案是在 Agent 运行流中暂停整个 run、等待外部输入;而 require_approval 是在单次工具调用层面加一个同步的审批函数。粒度更细,但要求审批函数必须是同步可执行的本地逻辑——无法做「等用户点按钮」式的异步审批。allowed_tools:比 require_approval 更彻底的限制
如果说
require_approval 是「调用前问一遍」,那 allowed_tools 是「干脆看不到」。tool = HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "task_service",
"server_url": "https://example.com/mcp",
"headers": {"X-API-KEY": os.getenv("TASK_API_KEY")},
"allowed_tools": [
"list_tasks",
"get_task",
"create_task",
# 故意排除 delete_task 和 update_task
],
"require_approval": "never"
}
)allowed_tools 里没有的工具,LLM 在 tool schema 里根本看不到它们——既不会尝试调用,也不会受 require_approval 逻辑影响。适合以下场景:- 多租户系统:不同 Agent 对同一 MCP 服务器有不同权限边界
- 只读 Agent:只暴露
list_/get_前缀的工具,屏蔽所有写操作 - Token 节约:MCP 服务器工具数量大时,只暴露当前任务需要的工具子集,减少 schema 的 token 占用
headers:认证字段的设计约束
headers 是 tool_config 里的认证入口,但有一个在 GitHub issues 里反复被提及的限制:目前 OpenAI 平台主要可靠支持 Authorization: Bearer <token> 格式的认证头3,自定义请求头(如 X-API-Key)的行为取决于目标 MCP 服务器是否接受。另一个已知限制是认证 token 刷新问题:
headers 目前只接受静态 dict,无法传入 Callable 来实现动态 token 刷新4。如果 token 有过期时间,只能在构造 HostedMCPTool 实例前手动刷新。HostedMCPTool vs 两种本地 MCP 方式的横向对比
| 维度 | HostedMCPTool | MCPServerSse | MCPServerStdio |
|---|---|---|---|
| 调用位置 | OpenAI 服务器 | 本地 SDK 进程 | 本地 SDK 进程 |
| 连接管理 | 无需(平台代理) | async context manager | async context manager |
| 支持的 API 后端 | 仅 Responses API | 任意 | 任意 |
| 认证方式 | headers 字段 | 参数/env | 参数/env |
require_approval 支持 | ✅ | ❌(需手动在工具调用层拦截) | ❌ |
| 工具白名单 | allowed_tools | 无内置 | 无内置 |
| 工具缓存 | 无内置 | cache_tools_list=True | cache_tools_list=True |
| 超时控制 | 无参数 | 支持 timeout | 支持 timeout |
| 适用场景 | 云端 MCP 服务、需要审批 | 远程 SSE 服务器、需缓存 | 本地进程工具、调试 |
一个完整示例:带认证和工具白名单的任务管理 Agent
import os
from agents import Agent, Runner, HostedMCPTool
taskmaster_tool = HostedMCPTool(
tool_config={
"type": "mcp",
"server_label": "taskmaster",
"server_url": "https://example-mcp-service.com/mcp",
"headers": {
"Authorization": f"Bearer {os.getenv('MCP_API_TOKEN')}"
},
"allowed_tools": [
"list_projects",
"get_project",
"list_tasks",
"create_task",
"update_task"
],
"require_approval": "never"
}
)
agent = Agent(
name="Task Manager",
instructions=(
"你是一个任务管理助手。帮助用户查看和管理项目与任务。"
"不要主动删除任何内容,除非用户明确要求。"
),
tools=[taskmaster_tool]
)
result = Runner.run_sync(agent, "帮我看一下当前有哪些进行中的项目")
print(result.final_output)三条生产级实践建议
1. 优先用
allowed_tools 做最小权限设计,再按需叠加 require_approval不要把所有工具都暴露给 Agent 再靠 instructions 约束。
allowed_tools 是结构性限制,LLM 看不到的工具就不会去调用;require_approval 是动态防线,用于对确实需要暴露但存在风险的工具加审批。两者分层使用比单独依赖任何一个都更可靠。2.
HostedMCPTool 只能用于 Responses API,提前在架构层面确认如果你的 Agent 使用了自定义模型(如通过
set_default_openai_api("chat_completions") 切换到 ChatCompletions 路径),HostedMCPTool 会抛 UserError: Hosted tools are not supported with the ChatCompletions API5。这个限制同样适用于 WebSearchTool 和 FileSearchTool——整个 HostedTool 家族都只走 Responses API。3. 静态 token + 程序启动时刷新,是目前最稳的认证方案
headers 不支持 Callable,意味着无法在运行时动态刷新 token。如果目标 MCP 服务的 token 有过期时间,建议把 token 刷新逻辑放在应用初始化阶段,或者在每次构造新的 Agent/HostedMCPTool 实例前先刷新——不要期望在 tool_config 里做惰性求值。下期预告:SDK 里的
ComputerTool 与 CodeInterpreterTool——两个不走 LLM 工具调用、直接操作环境的「执行器」类工具,原理和用法与 HostedTool 家族有根本性的不同。참고 출처
- 1Model context protocol (MCP) - OpenAI Agents SDK
- 2How to connect OpenAI Agents SDK to MCP - Speakeasy
- 3Support Custom Headers for MCP Servers in OpenAI - GitHub Issue #1609
- 4Agent tools and mcp_servers should allow for Callable - GitHub Issue #2167
- 5Hosted tools are not supported with the ChatCompletions API - GitHub Issue #1008
이 콘텐츠를 둘러싼 관점이나 맥락을 계속 보강해 보세요.