跳转到内容

MCP 集成

Model Context Protocol (MCP) 是一种开放协议,用于标准化应用向 LLM 提供工具和上下文的方式。MCP 文档中的定义如下:

MCP 是一种开放协议,用于标准化应用向 LLM 提供上下文的方式。您可以把 MCP 想象成 AI 应用的 USB-C 端口。正如 USB-C 提供了一种将设备连接到各种外设和配件的标准方式,MCP 也提供了一种将 AI 模型连接到不同数据源和工具的标准方式。

此 SDK 支持三种类型的 MCP 服务器:

  1. 远程 MCP 服务器工具——由 OpenAI Responses API 作为工具使用的远程 MCP 服务器
  2. Streamable HTTP MCP 服务器——实现了 Streamable HTTP 传输机制的本地或远程服务器
  3. Stdio MCP 服务器——通过标准输入/输出访问的服务器(最简单的选项)

注意:SDK 还包括用于旧版服务器发送事件传输的 MCPServerSSE,但 MCP 项目已弃用 SSE。对于新的集成,请优先使用 Streamable HTTP 或 stdio。

请根据您的使用场景选择服务器类型:

您的需求推荐选项
使用默认 OpenAI Responses 模型调用可公开访问的远程服务器1. 托管 MCP 工具
使用可公开访问的远程服务器,但在本地触发工具调用2. Streamable HTTP
使用本地运行的 Streamable HTTP 服务器2. Streamable HTTP
将任意 Streamable HTTP 服务器与非 OpenAI Responses 模型配合使用2. Streamable HTTP
使用仅支持标准 I/O 协议的本地 MCP 服务器3. Stdio

Agents SDK 在内部使用 MCP TypeScript SDK v2 客户端。这并不要求您的 MCP 服务器迁移到 v2 服务器包。对于 stdio 和 Streamable HTTP 连接,Agents SDK 会自动协商协议;只要使用 MCP TypeScript SDK v1 实现的现有服务器支持兼容的协议版本,Agents SDK 就仍与其兼容。

如果您的应用仅使用 Agents SDK 的 MCPServerStdioMCPServerStreamableHttpMCPServerSSE 类,则无需为实现这种兼容性而安装 MCP v1 和 v2 包或在两者之间建立桥接。如果希望采用 v2 MCP SDK API,请单独迁移应用自行管理的 MCP SDK 对象;请参阅 MCP TypeScript SDK 的 v1 到 v2 迁移指南

托管工具会将整个往返流程交给模型处理。OpenAI Responses API 会调用远程工具端点,并将结果以流式方式返回给模型,而不再由您的代码调用 MCP 服务器。

下面是使用托管 MCP 工具的最简单示例。您可以将远程 MCP 服务器的标签和 URL 传递给 hostedMcpTool 实用函数,以便创建远程 MCP 服务器工具。

hostedAgent.ts
import { Agent, hostedMcpTool } from '@openai/agents';
export const agent = new Agent({
name: 'MCP Assistant',
instructions: 'You must always use the MCP tools to answer questions.',
tools: [
hostedMcpTool({
serverLabel: 'deepwiki',
serverUrl: 'https://mcp.deepwiki.com/mcp',
}),
],
});

然后,您可以使用 run 函数(或自定义 Runner 实例的 run 方法)运行智能体:

使用托管 MCP 工具运行
import { run } from '@openai/agents';
import { agent } from './hostedAgent';
async function main() {
const result = await run(
agent,
'Which language is the repo I pointed in the MCP tool settings written in?',
);
console.log(result.finalOutput);
}
main().catch(console.error);

若要以流式方式传输增量 MCP 结果,请在运行 Agent 时传入 stream: true

使用托管 MCP 工具运行(流式传输)
import { isOpenAIResponsesRawModelStreamEvent, run } from '@openai/agents';
import { agent } from './hostedAgent';
async function main() {
const result = await run(
agent,
'Which language is the repo I pointed in the MCP tool settings written in?',
{ stream: true },
);
for await (const event of result) {
if (
isOpenAIResponsesRawModelStreamEvent(event) &&
event.data.event.type !== 'response.mcp_call_arguments.delta' &&
event.data.event.type !== 'response.output_text.delta'
) {
console.log(`Got event of type ${JSON.stringify(event.data)}`);
}
}
console.log(`Done streaming; final result: ${result.finalOutput}`);
}
main().catch(console.error);

对于敏感操作,您可以要求人工审批各个工具调用。传入 requireApproval: 'always',或传入一个将工具名称映射到 'never'/'always' 的细粒度对象。

如果您可以通过程序判断工具调用是否安全,则可使用 onApproval 回调批准或拒绝工具调用。如果需要人工审批,则可像处理本地函数工具一样,通过 interruptions 使用相同的人机协作(HITL)方法

托管 MCP 工具的人机协作
import { Agent, run, hostedMcpTool, RunToolApprovalItem } from '@openai/agents';
async function main(): Promise<void> {
const agent = new Agent({
name: 'MCP Assistant',
instructions: 'You must always use the MCP tools to answer questions.',
tools: [
hostedMcpTool({
serverLabel: 'deepwiki',
serverUrl: 'https://mcp.deepwiki.com/mcp',
// 'always' | 'never' | { never, always }
requireApproval: {
never: {
toolNames: ['read_wiki_structure', 'read_wiki_contents'],
},
always: {
toolNames: ['ask_question'],
},
},
}),
],
});
let result = await run(
agent,
'For the repository openai/codex, tell me the primary programming language.',
);
while (result.interruptions && result.interruptions.length) {
for (const interruption of result.interruptions) {
// Human in the loop here
const approval = await confirm(interruption);
if (approval) {
result.state.approve(interruption);
} else {
result.state.reject(interruption);
}
}
result = await run(agent, result.state);
}
console.log(result.finalOutput);
}
import { stdin, stdout } from 'node:process';
import * as readline from 'node:readline/promises';
async function confirm(item: RunToolApprovalItem): Promise<boolean> {
const rl = readline.createInterface({ input: stdin, output: stdout });
const name = item.name;
const params = item.arguments;
const answer = await rl.question(
`Approve running tool (mcp: ${name}, params: ${params})? (y/n) `,
);
rl.close();
return answer.toLowerCase().trim() === 'y';
}
main().catch(console.error);

hostedMcpTool(...) 同时支持 MCP 服务器 URL 和基于连接器的服务器:

选项类型说明
serverLabelstring必填标签,用于在事件和追踪中标识托管 MCP 服务器。
serverUrlstring远程 MCP 服务器 URL(用于常规托管 MCP 服务器)。
connectorIdstringOpenAI 连接器 ID(对于基于连接器的托管服务器,使用此选项而不是 serverUrl)。
authorizationstring可选的授权令牌,将发送到托管 MCP 后端。
headersRecord<string, string>可选的额外请求标头。
allowedToolsstring[] | object向模型公开的工具名称允许列表。传入 string[]{ toolNames?: string[] }
allowedCallers('direct' | 'programmatic')[]仅适用于 Responses API 的非空列表,用于控制远程 MCP 服务器工具能否直接调用、通过程序化工具调用,或通过这两种方式调用。
deferLoadingboolean仅适用于 Responses API 的远程 MCP 服务器工具延迟加载。要求同一智能体中包含 toolSearchTool()
requireApproval'never' | 'always' | object远程 MCP 服务器工具调用的审批策略。使用对象形式可按工具覆盖策略。默认为 'never'
onApproval审批回调可选回调,用于在 requireApproval 要求处理审批时通过程序批准或拒绝。

如果希望模型通过工具搜索按需加载托管 MCP 服务器的工具定义,而不是预先公开这些定义,请设置 deferLoading: true。此功能仅适用于 OpenAI Responses API,要求同一请求中包含 toolSearchTool(),并且应与 GPT-5.4 及更新的受支持模型版本配合使用。完整的延迟加载设置请参阅工具指南

如果远程 MCP 服务器工具只能从模型生成的 JavaScript 中调用,请设置 allowedCallers: ['programmatic'];若要允许任一调用路径,请同时包含 'direct''programmatic'。请将 programmaticToolCallingTool() 添加到同一智能体。当程序调用 MCP 工具时,现有的 requireApprovalonApproval 策略仍然适用。完整设置请参阅程序化工具调用

requireApproval 的对象形式接受可选的 alwaysnever 条目。每个条目都可以通过 toolNames 或工具的只读注解选择工具。可选的 onApproval 回调接收当前运行上下文和审批项,然后返回一个包含 approve 和可选 reason 的 Promise。

托管 MCP 也支持 OpenAI 连接器。无需提供 serverUrl,而是传入连接器的 connectorIdauthorization 令牌。随后,Responses API 会处理身份验证,并通过托管 MCP 接口公开该连接器的工具。

基于连接器的托管 MCP 工具
import { Agent, hostedMcpTool } from '@openai/agents';
const authorization = process.env.GOOGLE_CALENDAR_AUTHORIZATION!;
export const connectorAgent = new Agent({
name: 'Calendar Assistant',
instructions:
"You are a helpful assistant that can answer questions about the user's calendar.",
tools: [
hostedMcpTool({
serverLabel: 'google_calendar',
connectorId: 'connector_googlecalendar',
authorization,
requireApproval: 'never',
}),
],
});

在此示例中,GOOGLE_CALENDAR_AUTHORIZATION 环境变量保存从 Google OAuth Playground 获取的 OAuth 令牌,该令牌授权基于连接器的服务器调用 Calendar API。有关同时演示流式传输的可运行代码示例,请参阅 examples/connectors

完整可运行的代码示例(托管工具/Streamable HTTP/stdio + 流式传输、HITL、onApproval)位于 GitHub 仓库的 examples/mcp 中。

除了选择传输机制外,您还可以通过设置 Agent.mcpConfig 来调整本地 MCP 工具的准备方式。

为智能体配置本地 MCP 工具
import { Agent, MCPServerStreamableHttp } from '@openai/agents';
const server = new MCPServerStreamableHttp({
url: 'https://your-own-domain-here/mcp',
name: 'Docs server',
});
const agent = new Agent({
name: 'Assistant',
mcpServers: [server],
mcpConfig: {
// Try to convert MCP tool schemas to strict JSON schema.
convertSchemasToStrict: true,
// Set to null to raise MCP tool failures instead of returning model-visible error text.
errorFunction: null,
// Prefix local MCP tool names with their server name.
includeServerInToolNames: true,
},
});

注意事项:

  • convertSchemasToStrict 会尽力完成转换。如果无法转换某个模式,则使用原始模式。
  • errorFunction 控制如何向模型呈现 MCP 工具调用失败。
  • 未设置 errorFunction 时,SDK 使用默认的工具错误格式化程序。
  • 服务器级 errorFunction 值会覆盖该服务器的 Agent.mcpConfig.errorFunction
  • includeServerInToolNames 需要显式启用。启用后,每个本地 MCP 工具都会使用带有确定性服务器前缀的名称向模型公开,这有助于避免多个 MCP 服务器发布同名工具时发生冲突。
  • SDK 会先从基于 URL 生成的服务器名称和传输错误中移除 URL 凭据、查询参数和片段,再通过日志、追踪和错误消息公开这些信息。如果需要稳定的服务器标签,请设置一个明确、唯一且不包含机密信息的 name
  • 如果不同的基于 URL 生成的服务器名称在移除凭据、查询参数和片段后变得相同,并且这些服务器发布了同名工具,则配置会在模型请求前失败。请为这些服务器设置明确、唯一且不包含机密信息的 name 值。

当您的智能体直接与本地或远程 Streamable HTTP MCP 服务器通信时,请使用服务器的 urlname 和其他可选设置实例化 MCPServerStreamableHttp

使用 Streamable HTTP MCP 服务器运行
import { Agent, run, MCPServerStreamableHttp } from '@openai/agents';
async function main() {
const mcpServer = new MCPServerStreamableHttp({
url: 'https://mcp.deepwiki.com/mcp',
name: 'DeepWiki MCP Server',
});
const agent = new Agent({
name: 'DeepWiki Assistant',
instructions: 'Use the tools to respond to user requests.',
mcpServers: [mcpServer],
});
try {
await mcpServer.connect();
const result = await run(
agent,
'For the repository openai/codex, tell me the primary programming language.',
);
console.log(result.finalOutput);
} finally {
await mcpServer.close();
}
}
main().catch(console.error);

构造函数选项:

选项类型说明
urlstringStreamable HTTP 服务器 URL。
namestring可选的服务器标签。
cacheToolsListboolean缓存工具列表以减少延迟。
clientSessionTimeoutSecondsnumberMCP 客户端会话的超时时间。
toolFilterMCPToolFilterCallable | MCPToolFilterStatic筛选可用工具。
toolMetaResolverMCPToolMetaResolver注入每次调用的 MCP _meta 请求字段。
toolInputGuardrailsToolInputGuardrailDefinition[]在每次调用从此服务器转换而来的工具之前运行这些护栏。
toolOutputGuardrailsToolOutputGuardrailDefinition[]在每次调用从此服务器转换而来的工具之后运行这些护栏。
useStructuredContentboolean当 MCP structuredContent 可用且结果不是错误时,将其 JSON 序列化值用作模型可见的输出。默认为 false
customDataExtractorMCPToolCustomDataExtractor将仅供 SDK 使用的 JSON 元数据附加到发出的本地 MCP 工具输出项。该回调可以读取 MCP 结果的 _metastructuredContentisError 和模型可见的工具输出。
errorFunctionMCPToolErrorFunction | null将 MCP 调用失败映射为模型可见的文本。
timeoutnumber每个请求的超时时间(毫秒)。
loggerLogger自定义日志记录器。
authProviderOAuthClientProvider来自 MCP TypeScript SDK 的 OAuth 提供程序。
requestInitRequestInit请求的 Fetch 初始化选项。
fetchFetchLike自定义 Fetch 实现。
reconnectionOptionsStreamableHTTPReconnectionOptions重连调优选项。
sessionIdstringMCP 连接的显式会话 ID。

该构造函数还接受其他 MCP TypeScript SDK 选项,例如 authProviderrequestInitfetchreconnectionOptionssessionId。详情请参阅 MCP TypeScript SDK 仓库及其文档。

toolInputGuardrailstoolOutputGuardrails 使用 SDK 的本地函数工具护栏管道。它们适用于流式和非流式运行中从此服务器转换而来的每个工具。它们不适用于由 Responses API 远程执行的远程 MCP 服务器工具。有关拒绝行为和审批顺序,请参阅工具护栏

对于仅公开标准 I/O 的服务器,请使用 fullCommand 实例化 MCPServerStdio

使用 Stdio MCP 服务器运行
import { Agent, run, MCPServerStdio } from '@openai/agents';
import * as path from 'node:path';
async function main() {
const samplesDir = path.join(__dirname, 'sample_files');
const mcpServer = new MCPServerStdio({
name: 'Filesystem MCP Server, via local package',
fullCommand: `pnpm exec mcp-server-filesystem ${samplesDir}`,
});
await mcpServer.connect();
try {
const agent = new Agent({
name: 'FS MCP Assistant',
instructions:
'Use the tools to read the filesystem and answer questions based on those files. If you are unable to find any files, you can say so instead of assuming they exist.',
mcpServers: [mcpServer],
});
const result = await run(agent, 'Read the files and list them.');
console.log(result.finalOutput);
} finally {
await mcpServer.close();
}
}
main().catch(console.error);

构造函数选项:

选项类型说明
command / argsstring / string[]stdio 服务器的命令和参数。
fullCommandstring完整命令字符串,可替代 command + args
envRecord<string, string>服务器进程的环境变量。
cwdstring服务器进程的工作目录。
cacheToolsListboolean缓存工具列表以减少延迟。
clientSessionTimeoutSecondsnumberMCP 客户端会话的超时时间。
namestring可选的服务器标签。
encodingstringstdio 流的编码。
encodingErrorHandler'strict' | 'ignore' | 'replace'编码错误处理策略。
toolFilterMCPToolFilterCallable | MCPToolFilterStatic筛选可用工具。
toolMetaResolverMCPToolMetaResolver注入每次调用的 MCP _meta 请求字段。
toolInputGuardrailsToolInputGuardrailDefinition[]在每次调用从此服务器转换而来的工具之前运行这些护栏。
toolOutputGuardrailsToolOutputGuardrailDefinition[]在每次调用从此服务器转换而来的工具之后运行这些护栏。
useStructuredContentboolean当 MCP structuredContent 可用且结果不是错误时,将其 JSON 序列化值用作模型可见的输出。默认为 false
customDataExtractorMCPToolCustomDataExtractor将仅供 SDK 使用的 JSON 元数据附加到发出的本地 MCP 工具输出项。该回调可以读取 MCP 结果的 _metastructuredContentisError 和模型可见的工具输出。
errorFunctionMCPToolErrorFunction | null将 MCP 调用失败映射为模型可见的文本。
timeoutnumber每个请求的超时时间(毫秒)。
loggerLogger自定义日志记录器。

使用多个 MCP 服务器时,您可以使用 connectMcpServers 同时连接这些服务器、追踪失败情况,并在一个位置将其关闭。该辅助函数返回一个 MCPServers 实例,其中包含 activefailederrors 集合,因此您可以仅将正常运行的服务器传递给智能体。

管理多个 MCP 服务器
import {
Agent,
MCPServerStreamableHttp,
connectMcpServers,
run,
} from '@openai/agents';
async function main() {
const servers = [
new MCPServerStreamableHttp({
url: 'https://mcp.deepwiki.com/mcp',
name: 'DeepWiki MCP Server',
}),
new MCPServerStreamableHttp({
url: 'http://localhost:8001/mcp',
name: 'Local MCP Server',
}),
];
const mcpServers = await connectMcpServers(servers, {
connectInParallel: true,
});
try {
console.log(`Active servers: ${mcpServers.active.length}`);
console.log(`Failed servers: ${mcpServers.failed.length}`);
for (const [server, error] of mcpServers.errors) {
console.warn(`${server.name} failed to connect: ${error.message}`);
}
const agent = new Agent({
name: 'MCP lifecycle agent',
instructions: 'Use MCP tools to answer user questions.',
mcpServers: mcpServers.active,
});
const result = await run(
agent,
'Which language is the openai/codex repository written in?',
);
console.log(result.finalOutput);
} finally {
await mcpServers.close();
}
}
main().catch(console.error);

使用场景:

  • 同时使用多个服务器:并行连接所有服务器,并将 mcpServers.active 用于智能体。
  • 部分失败处理:检查 failed + errors,并决定继续还是重试。
  • 重试失败的服务器:调用 mcpServers.reconnect()(默认仅重试失败的服务器)。

如果需要严格的”全部成功或全部失败”连接模式或不同的超时时间,请使用 connectMcpServers(servers, options),并根据您的环境调整相关选项。

connectMcpServers 选项:

选项类型默认值说明
connectTimeoutMsnumber | null10000每个服务器执行 connect() 的超时时间。使用 null 可禁用。
closeTimeoutMsnumber | null10000每个服务器执行 close() 的超时时间。使用 null 可禁用。
dropFailedbooleantrueactive 中排除失败的服务器。
strictbooleanfalse如果任何服务器连接失败,则抛出异常。
suppressAbortErrorbooleantrue忽略类似中止的错误,同时仍追踪失败的服务器。
connectInParallelbooleanfalse并发连接所有服务器,而不是依次连接。

mcpServers.reconnect(options) 支持:

选项类型默认值说明
failedOnlybooleantrue仅重试失败的服务器(true),或重新连接所有服务器(false)。

重新连接之前,MCPServers 会关闭所选服务器。它只会重新连接成功关闭的服务器;清理失败信息仍可通过 failederrors 获取。

如果您的运行时支持 Symbol.asyncDisposeMCPServers 也支持 await using 模式。在 TypeScript 中,请在 tsconfig.json 中启用 esnext.disposable

{
"compilerOptions": {
"lib": ["ES2018", "DOM", "esnext.disposable"]
}
}

然后可以编写:

通过异步释放关闭已连接的 MCP 服务器
import { MCPServerStreamableHttp, connectMcpServers } from '@openai/agents';
async function main() {
const servers = [
new MCPServerStreamableHttp({
url: 'https://your-own-domain-here/mcp',
name: 'Docs server',
}),
];
await using mcpServers = await connectMcpServers(servers);
}
main().catch(console.error);

对于 Streamable HTTPStdio 服务器,每次运行 Agent 时,都可能调用 list_tools() 来发现可用工具。由于此往返过程可能增加延迟,尤其是远程服务器,因此您可以通过向 MCPServerStdioMCPServerStreamableHttp 传入 cacheToolsList: true,将结果缓存在内存中。

仅当您确定工具列表不会更改时才启用此功能。若要稍后使缓存失效,请在服务器实例上调用 invalidateToolsCache()。如果通过 getAllMcpTools(...) 使用共享 MCP 工具缓存,也可以使用 invalidateServerToolsCache(serverName) 按服务器名称使缓存失效。

对于高级场景,getAllMcpTools({ generateMCPToolCacheKey }) 可用于自定义缓存分区方式(例如按服务器、智能体和运行上下文分区)。

默认情况下,本地 MCP 工具会保留 MCP 服务器报告的工具名称。如果两个本地 MCP 服务器公开相同的工具名称,SDK 会引发工具名称重复错误,因为模型无法安全地在两者之间进行选择。

若要启用带有确定性服务器前缀的名称,请在智能体上设置 mcpConfig.includeServerInToolNames: true

为本地 MCP 工具名称添加服务器名称前缀
import { Agent, MCPServerStreamableHttp } from '@openai/agents';
const docsServer = new MCPServerStreamableHttp({
url: 'https://your-own-domain-here/docs/mcp',
name: 'docs',
});
const calendarServer = new MCPServerStreamableHttp({
url: 'https://your-own-domain-here/calendar/mcp',
name: 'calendar',
});
const agent = new Agent({
name: 'Assistant',
mcpServers: [docsServer, calendarServer],
mcpConfig: {
includeServerInToolNames: true,
},
});

启用此设置后,来自 docs 服务器的 search 工具会以 mcp_docs__search 的名称向模型公开,而来自 calendar 服务器的 search 工具则会以 mcp_calendar__search 的名称公开。SDK 仍会在原始服务器上调用原始 MCP 工具名称。

生成的名称仅使用 ASCII 安全字符,保持在 FunctionTool 实例的名称长度限制内,并且不会与同一智能体上的本地 FunctionTool 实例名称或已启用交接的名称发生冲突。此设置会影响所有本地 MCP 传输机制,包括 Streamable HTTP、SSE 和 stdio;远程 MCP 服务器工具则会保留其托管服务器标签和工具元数据。

您可以通过 createMCPToolStaticFilter 传入静态筛选器或传入自定义函数,限制每个服务器公开的工具。以下组合示例展示了这两种方式:

工具筛选
import {
MCPServerStdio,
MCPServerStreamableHttp,
createMCPToolStaticFilter,
MCPToolFilterContext,
} from '@openai/agents';
interface ToolFilterContext {
allowAll: boolean;
}
const server = new MCPServerStdio({
fullCommand: 'my-server',
toolFilter: createMCPToolStaticFilter({
allowed: ['safe_tool'],
blocked: ['danger_tool'],
}),
});
const dynamicServer = new MCPServerStreamableHttp({
url: 'http://localhost:3000',
toolFilter: async ({ runContext }: MCPToolFilterContext, tool) =>
(runContext.context as ToolFilterContext).allowAll || tool.name !== 'admin',
});