MCP 連携
Model Context Protocol (MCP) は、アプリケーションが LLM にツールとコンテキストを提供する方法を標準化するオープンプロトコルです。MCP ドキュメントより:
MCP は、アプリケーションが LLM にコンテキストを提供する方法を標準化するオープンプロトコルです。MCP は AI アプリケーション向けの USB-C ポートのようなものだと考えてください。USB-C がデバイスをさまざまな周辺機器やアクセサリーに接続するための標準化された方法を提供するのと同じように、MCP は AI モデルをさまざまなデータソースやツールに接続するための標準化された方法を提供します。
この SDK がサポートする MCP サーバーには 3 種類あります。
- リモート MCP サーバーツール – OpenAI Responses API によってツールとして使用されるリモート MCP サーバー
- Streamable HTTP MCP サーバー – Streamable HTTP transport を実装したローカルまたはリモートのサーバー
- Stdio MCP サーバー – 標準入出力経由でアクセスされるサーバー(最も簡単なオプション)
注: SDK には従来の Server-Sent Events トランスポート向けの
MCPServerSSEも含まれていますが、SSE は MCP プロジェクトで非推奨になっています。新しい連携では Streamable HTTP または stdio を推奨します。
ユースケースに応じてサーバータイプを選択します。
| 必要なこと | 推奨オプション |
|---|---|
| デフォルトの OpenAI Responses モデルで、公開アクセス可能なリモートサーバーを呼び出す | 1. リモート MCP サーバーツール |
| 公開アクセス可能なリモートサーバーを使用しつつ、ツール呼び出しはローカルでトリガーする | 2. Streamable HTTP |
| ローカルで実行中の Streamable HTTP サーバーを使用する | 2. Streamable HTTP |
| OpenAI Responses 以外のモデルで任意の Streamable HTTP サーバーを使用する | 2. Streamable HTTP |
| 標準 I/O プロトコルのみをサポートするローカル MCP サーバーを利用する | 3. Stdio |
1. リモート MCP サーバーツール
Section titled “1. リモート MCP サーバーツール”Hosted ツールでは、ラウンドトリップ全体をモデル側に移します。コードが MCP サーバーを呼び出す代わりに、OpenAI Responses API がリモートツールエンドポイントを呼び出し、結果をモデルにストリーミングで返します。
Hosted MCP ツールを使用する最もシンプルな例を次に示します。リモート MCP サーバーのラベルと URL を hostedMcpTool ユーティリティ関数に渡すことができ、これはリモート MCP サーバーツールの作成に役立ちます。
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 メソッド)でエージェントを実行できます。
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 を渡します。
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);任意の承認フロー
Section titled “任意の承認フロー”機密性の高い操作では、個々のツール呼び出しに人間の承認を必須にできます。requireApproval: 'always'、またはツール名を 'never' / 'always' にマッピングするきめ細かなオブジェクトのいずれかを渡します。
ツール呼び出しが安全かをプログラムで判断できる場合は、onApproval コールバック を使用してツール呼び出しを承認または拒否できます。人間の承認が必要な場合は、ローカルの関数ツールの場合と同様に、interruptions を使用する同じ 人間の介入(HITL) のアプローチを利用できます。
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);Hosted MCP オプションリファレンス
Section titled “Hosted MCP オプションリファレンス”hostedMcpTool(...) は、MCP サーバー URL とコネクター連携サーバーの両方をサポートします。
| オプション | 型 | メモ |
|---|---|---|
serverLabel | string | イベントとトレースでリモート MCP サーバーを識別する必須ラベル。 |
serverUrl | string | リモート MCP サーバー URL(通常の Hosted MCP サーバーにはこれを使用します)。 |
connectorId | string | OpenAI コネクター ID(コネクター連携の Hosted サーバーでは serverUrl の代わりにこれを使用します)。 |
authorization | string | Hosted MCP バックエンドに送信される任意の認可トークン。 |
headers | Record<string, string> | 任意の追加リクエストヘッダー。 |
allowedTools | string[] | object | モデルに公開するツール名の許可リスト。string[] または { toolNames?: string[] } を渡します。 |
deferLoading | boolean | Hosted MCP ツール向けの Responses 専用の遅延読み込み。同じエージェント内に toolSearchTool() が必要です。 |
requireApproval | 'never' | 'always' | object | Hosted MCP ツール呼び出しの承認ポリシー。ツールごとの上書きにはオブジェクト形式を使用します。デフォルトは 'never' です。 |
onApproval | 承認コールバック | requireApproval が承認処理を必要とする場合の、プログラムによる承認/拒否用の任意のコールバック。 |
Hosted MCP サーバーのツール定義を事前に公開する代わりに、ツール検索経由でオンデマンドにモデルに読み込ませたい場合は、deferLoading: true を設定します。これは OpenAI Responses API でのみ機能し、同じリクエスト内に toolSearchTool() が必要で、GPT-5.4 以降のサポート対象モデルリリースで使用する必要があります。遅延読み込み設定の詳細については、ツール を参照してください。
requireApproval のオブジェクト形式:
{ always?: { toolNames: string[] }; never?: { toolNames: string[] };}onApproval のシグネチャ:
async function onApproval( context, item,): Promise<{ approve: boolean; reason?: string;}> {}コネクター連携の Hosted サーバー
Section titled “コネクター連携の Hosted サーバー”Hosted MCP は OpenAI コネクターもサポートしています。serverUrl を指定する代わりに、コネクターの connectorId と authorization トークンを渡します。その後 Responses API が認証を処理し、コネクターのツールを Hosted 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 を参照してください。
完全に動作するサンプル(Hosted ツール / Streamable HTTP / stdio + ストリーミング、HITL、onApproval)は、GitHub リポジトリの examples/mcp にあります。
エージェントレベルの MCP 設定
Section titled “エージェントレベルの MCP 設定”トランスポートの選択に加えて、Agent.mcpConfig を設定することで、ローカル MCP ツールの準備方法を調整できます。
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 サーバーが同じ名前のツールを公開している場合に衝突を避けるのに役立ちます。
2. Streamable HTTP MCP サーバー
Section titled “2. Streamable HTTP MCP サーバー”エージェントが Streamable HTTP MCP サーバー(ローカルまたはリモート)と直接通信する場合は、サーバーの url、name、任意の設定を指定して MCPServerStreamableHttp をインスタンス化します。
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);コンストラクターオプション:
| オプション | 型 | メモ |
|---|---|---|
url | string | Streamable HTTP サーバー URL。 |
name | string | サーバーの任意のラベル。 |
cacheToolsList | boolean | レイテンシを減らすためにツール一覧をキャッシュします。 |
clientSessionTimeoutSeconds | number | MCP クライアントセッションのタイムアウト。 |
toolFilter | MCPToolFilterCallable | MCPToolFilterStatic | 利用可能なツールをフィルタリングします。 |
toolMetaResolver | MCPToolMetaResolver | 呼び出しごとの MCP _meta リクエストフィールドを注入します。 |
errorFunction | MCPToolErrorFunction | null | MCP 呼び出しの失敗を、モデルに見えるテキストにマッピングします。 |
timeout | number | リクエストごとのタイムアウト(ミリ秒)。 |
logger | Logger | カスタムロガー。 |
authProvider | OAuthClientProvider | MCP TypeScript SDK の OAuth プロバイダー。 |
requestInit | RequestInit | リクエストの fetch 初期化オプション。 |
fetch | FetchLike | カスタム fetch 実装。 |
reconnectionOptions | StreamableHTTPReconnectionOptions | 再接続の調整オプション。 |
sessionId | string | MCP 接続の明示的なセッション ID。 |
コンストラクターは、authProvider、requestInit、fetch、reconnectionOptions、sessionId などの追加の MCP TypeScript SDK オプションも受け付けます。詳細については、MCP TypeScript SDK リポジトリ とそのドキュメントを参照してください。
3. Stdio MCP サーバー
Section titled “3. Stdio MCP サーバー”標準 I/O のみを公開するサーバーでは、fullCommand を指定して MCPServerStdio をインスタンス化します。
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 / args | string / string[] | stdio サーバー用のコマンドと引数。 |
fullCommand | string | command + args の代替となる完全なコマンド文字列。 |
env | Record<string, string> | サーバープロセスの環境変数。 |
cwd | string | サーバープロセスの作業ディレクトリ。 |
cacheToolsList | boolean | レイテンシを減らすためにツール一覧をキャッシュします。 |
clientSessionTimeoutSeconds | number | MCP クライアントセッションのタイムアウト。 |
name | string | サーバーの任意のラベル。 |
encoding | string | stdio ストリームのエンコーディング。 |
encodingErrorHandler | 'strict' | 'ignore' | 'replace' | エンコーディングエラーの処理戦略。 |
toolFilter | MCPToolFilterCallable | MCPToolFilterStatic | 利用可能なツールをフィルタリングします。 |
toolMetaResolver | MCPToolMetaResolver | 呼び出しごとの MCP _meta リクエストフィールドを注入します。 |
errorFunction | MCPToolErrorFunction | null | MCP 呼び出しの失敗を、モデルに見えるテキストにマッピングします。 |
timeout | number | リクエストごとのタイムアウト(ミリ秒)。 |
logger | Logger | カスタムロガー。 |
MCP サーバーライフサイクルの管理
Section titled “MCP サーバーライフサイクルの管理”複数の MCP サーバーを扱う場合は、connectMcpServers を使用してそれらをまとめて接続し、失敗を追跡し、一か所で閉じることができます。このヘルパーは、active、failed、errors コレクションを持つ MCPServers インスタンスを返すため、正常なサーバーのみをエージェントに渡せます。
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 オプション:
| オプション | 型 | デフォルト | メモ |
|---|---|---|---|
connectTimeoutMs | number | null | 10000 | 各サーバーの connect() のタイムアウト。無効にするには null を使用します。 |
closeTimeoutMs | number | null | 10000 | 各サーバーの close() のタイムアウト。無効にするには null を使用します。 |
dropFailed | boolean | true | 失敗したサーバーを active から除外します。 |
strict | boolean | false | いずれかのサーバーの接続に失敗した場合に例外を投げます。 |
suppressAbortError | boolean | true | 失敗したサーバーは追跡しつつ、abort 相当のエラーを無視します。 |
connectInParallel | boolean | false | 逐次ではなく、すべてのサーバーに同時接続します。 |
mcpServers.reconnect(options) は次をサポートします。
| オプション | 型 | デフォルト | メモ |
|---|---|---|---|
failedOnly | boolean | true | 失敗したサーバーのみを再試行する場合は true、すべてのサーバーを再接続する場合は false。 |
非同期破棄(任意)
Section titled “非同期破棄(任意)”ランタイムが Symbol.asyncDispose をサポートしている場合、MCPServers は await using パターンもサポートします。TypeScript では、tsconfig.json で esnext.disposable を有効にします。
{ "compilerOptions": { "lib": ["ES2018", "DOM", "esnext.disposable"] }}その後、次のように記述できます。
await using mcpServers = await connectMcpServers(servers);その他の留意事項
Section titled “その他の留意事項”Streamable HTTP および Stdio サーバーでは、Agent が実行されるたびに、利用可能なツールを検出するため list_tools() を呼び出す場合があります。そのラウンドトリップは、特にリモートサーバーに対してレイテンシを増やす可能性があるため、MCPServerStdio または MCPServerStreamableHttp に cacheToolsList: true を渡すことで、結果をメモリ内にキャッシュできます。
ツール一覧が変わらないと確信できる場合にのみ有効にしてください。後でキャッシュを無効化するには、サーバーインスタンスで invalidateToolsCache() を呼び出します。getAllMcpTools(...) 経由の共有 MCP ツールキャッシュを使用している場合は、invalidateServerToolsCache(serverName) でサーバー名を指定して無効化することもできます。
高度なケースでは、getAllMcpTools({ generateMCPToolCacheKey }) により、キャッシュの分割方法(たとえばサーバー + エージェント + 実行コンテキスト単位)をカスタマイズできます。
サーバー接頭辞付きツール名
Section titled “サーバー接頭辞付きツール名”デフォルトでは、ローカル MCP ツールは MCP サーバーから報告されたツール名を保持します。2 つのローカル MCP サーバーが同じツール名を公開している場合、モデルが安全に選択できないため、SDK は重複したツール名のエラーを発生させます。
決定的なサーバー接頭辞付きの名前を使用するには、エージェントで mcpConfig.includeServerInToolNames: true を設定します。
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 セーフで、関数ツール名の制限内に収まり、同じエージェント上のローカル関数ツール名および有効なハンドオフ名との衝突を回避します。この設定が影響するのは、ローカルの Streamable HTTP および stdio MCP ツールだけです。Hosted MCP ツールは、Hosted サーバーラベルとツールメタデータを保持します。
ツールフィルタリング
Section titled “ツールフィルタリング”各サーバーから公開されるツールを制限するには、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',});- Model Context Protocol – 公式仕様。
- examples/mcp – 上で参照した実行可能なデモ。