Realtime セッションにツールを追加すると、モデルは会話中にデータを検索したり、操作を実行したり、サービスを呼び出したりできます。クライアントが WebRTC データチャネルと WebSocket のどちらを使う場合も、ツールの設定には同じイベントを使います。
アプリケーションがツールを実行して結果を返す場合は、関数ツールを使います。Realtime API にリモートのツールサーバーへの接続を任せる場合は、MCP ツールを使います。
| ツールの種類 |
用途 |
実行主体と動作 |
function |
アプリケーション側でビジネスロジック、承認の確認、非公開システムへのアクセスのいずれかを管理する場合 |
クライアントまたはサーバーが関数呼び出しを受け取り、function_call_output を返します。 |
mcp と server_url |
モデルからリモート MCP サーバーが公開するツールを呼び出す場合 |
Realtime API がリモート MCP サーバーを呼び出します。 |
mcp と connector_id |
既存のモデルで従来の組み込みコネクタを使う場合 |
Realtime API が指定された認可情報を使ってコネクタを呼び出します。 |
ツールは次の 2 か所のいずれかに追加します。
- セッション単位で追加するには、
session.tools を session.update に含めます。ツールをセッション全体で利用できます。
- レスポンス単位で追加するには、
response.tools を response.create に含めます。ツールが 1 ターンだけ必要な場合に使います。
アプリケーション側でツールを実行する場合は、基本的に関数ツールを使います。モデルが関数呼び出しの引数を出力したら、アプリケーションのコードで操作を実行し、function_call_output アイテムで結果を返します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27const event = {
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2.1",
tools: [
{
type: "function",
name: "lookup_order",
description: "Look up an order by its order number.",
parameters: {
type: "object",
properties: {
order_number: {
type: "string",
description: "The customer-facing order number.",
},
},
required: ["order_number"],
},
},
],
tool_choice: "auto",
},
};
ws.send(JSON.stringify(event));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27event = {
"type": "session.update",
"session": {
"type": "realtime",
"model": "gpt-realtime-2.1",
"tools": [
{
"type": "function",
"name": "lookup_order",
"description": "Look up an order by its order number.",
"parameters": {
"type": "object",
"properties": {
"order_number": {
"type": "string",
"description": "The customer-facing order number.",
}
},
"required": ["order_number"],
},
}
],
"tool_choice": "auto",
},
}
ws.send(json.dumps(event))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27if err := conn.Send(ctx, realtime.RealtimeClientEventParamOfSessionUpdate(realtime.RealtimeSessionCreateRequestParam{
Model: "gpt-realtime-2.1",
ToolChoice: realtime.RealtimeToolChoiceConfigUnionParam{
OfToolChoiceMode: param.NewOpt[responses.ToolChoiceOptions]("auto"),
},
Tools: realtime.RealtimeToolsConfigParam{
realtime.RealtimeToolsConfigUnionParam{
OfFunction: &realtime.RealtimeFunctionToolParam{
Description: openai.String("Look up an order by its order number."),
Name: openai.String("lookup_order"),
Parameters: map[string]any{
"properties": map[string]any{
"order_number": map[string]any{
"description": "The customer-facing order number.",
"type": "string",
},
},
"required": []any{"order_number"},
"type": "object",
},
Type: "function",
},
},
},
})); err != nil {
return err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.core.JsonValue;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
import java.util.List;
import java.util.Map;
connection.send(
RealtimeClientEvent.ofSessionUpdate(
SessionUpdateEvent.builder()
.session(
RealtimeSessionCreateRequest.builder()
.model("gpt-realtime-2.1")
.addTool(
RealtimeFunctionTool.builder()
.type(RealtimeFunctionTool.Type.FUNCTION)
.name("lookup_order")
.description("Look up an order by its order number.")
.parameters(
JsonValue.from(
Map.of(
"type",
"object",
"properties",
Map.of(
"order_number",
Map.of(
"type",
"string",
"description",
"The customer-facing order number.")),
"required",
List.of("order_number"))))
.build())
.toolChoice(com.openai.models.responses.ToolChoiceOptions.AUTO)
.build())
.build()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17using OpenAI.Realtime;
#pragma warning disable OPENAI002
await session.SendCommandAsync(new RealtimeClientCommandSessionUpdate(new RealtimeConversationSessionOptions
{
Model = "gpt-realtime-2.1",
Tools =
{
new RealtimeFunctionTool("lookup_order")
{
FunctionDescription = "Look up an order by its order number.",
FunctionParameters = BinaryData.FromObjectAsJson(new { type = "object", properties = new { order_number = new { type = "string", description = "The customer-facing order number." } }, required = (string[])["order_number"] })
}
},
ToolChoice = RealtimeDefaultToolChoice.Auto
}), timeout.Token);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29connection.session.update(
type: :realtime,
model: "gpt-realtime-2.1",
tools: [
{
type: :function,
name: "lookup_order",
description: "Look up an order by its order number.",
parameters: {
type: "object",
properties: {
order_number: {
type: "string",
description: "The customer-facing order number."
}
},
required: ["order_number"]
}
}
],
tool_choice: :auto
)
updated = connection.find do |event|
raise event.error.message if event.is_a?(OpenAI::Realtime::RealtimeErrorEvent)
event.is_a?(OpenAI::Realtime::SessionUpdatedEvent)
end
raise "Connection closed before the session update was acknowledged" unless updated
モデルが関数を呼び出したら、関数呼び出しのアイテムを受け取り、アプリケーションのロジックを実行してから結果を返します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const event = {
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: functionCall.call_id,
output: JSON.stringify({
status: "shipped",
delivery_date: "2026-05-09",
}),
},
};
ws.send(JSON.stringify(event));
ws.send(
JSON.stringify({
type: "response.create",
response: { output_modalities: ["text"], tool_choice: "none" },
})
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23event = {
"type": "conversation.item.create",
"item": {
"type": "function_call_output",
"call_id": function_call["call_id"],
"output": json.dumps(
{
"status": "shipped",
"delivery_date": "2026-05-09",
}
),
},
}
ws.send(json.dumps(event))
ws.send(
json.dumps(
{
"type": "response.create",
"response": {"output_modalities": ["text"], "tool_choice": "none"},
}
)
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// callID comes from the model's function_call item.
if err := conn.Send(ctx, realtime.RealtimeClientEventParamOfConversationItemCreate(realtime.RealtimeConversationItemFunctionCallOutputParam{
CallID: callID,
Output: "{\"status\":\"shipped\",\"delivery_date\":\"2026-05-09\"}",
})); err != nil {
return err
}
if err := conn.Send(ctx, realtime.RealtimeClientEventUnionParam{
OfResponseCreate: &realtime.ResponseCreateEventParam{
Response: realtime.RealtimeResponseCreateParams{
OutputModalities: []string{"text"},
ToolChoice: realtime.RealtimeResponseCreateParamsToolChoiceUnion{
OfToolChoiceMode: param.NewOpt[responses.ToolChoiceOptions]("none"),
},
},
},
}); err != nil {
return err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.core.JsonMissing;
import com.openai.core.JsonValue;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
import com.openai.models.responses.ToolChoiceFunction;
import com.openai.models.responses.ToolChoiceOptions;
import java.util.Map;
static void sendFunctionCallOutput(RealtimeConnection connection, String callId)
throws Exception {
send(
connection,
RealtimeClientEvent.ofConversationItemCreate(
ConversationItemCreateEvent.builder()
.item(
RealtimeConversationItemFunctionCallOutput.builder()
.object_(JsonMissing.of())
.callId(callId)
.output("{\"status\":\"shipped\",\"delivery_date\":\"2026-05-09\"}")
.build())
.build()));
send(
connection,
RealtimeClientEvent.ofResponseCreate(
ResponseCreateEvent.builder()
.response(
RealtimeResponseCreateParams.builder()
.metadata(
RealtimeResponseCreateParams.Metadata.builder()
.putAdditionalProperty(
"topic", JsonValue.from("lookup_order_followup"))
.build())
.addOutputModality(RealtimeResponseCreateParams.OutputModality.TEXT)
.toolChoice(ToolChoiceOptions.NONE)
.build())
.build()));
}
// Retry only explicit admission rejections; these guarantee nothing was sent.
private static void send(RealtimeConnection connection, RealtimeClientEvent event)
throws Exception {
long deadline = System.nanoTime() + java.util.concurrent.TimeUnit.SECONDS.toNanos(5);
while (true) {
try {
connection.send(event);
return;
} catch (com.openai.core.http.WebSocketWriteNotAttempted.Busy busy) {
if (System.nanoTime() >= deadline) throw busy;
Thread.sleep(10);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18using OpenAI.Realtime;
#pragma warning disable OPENAI002
internal static async Task SendFunctionCallOutputAsync(RealtimeSessionClient session, string callId, CancellationToken cancellationToken)
{
string output = System.Text.Json.JsonSerializer.Serialize(new { status = "shipped", delivery_date = "2026-05-09" });
await session.SendCommandAsync(new RealtimeClientCommandConversationItemCreate(new RealtimeFunctionCallOutputItem(callId, output)), cancellationToken);
await session.SendCommandAsync(new RealtimeClientCommandResponseCreate
{
ResponseOptions = new()
{
OutputModalities = { RealtimeOutputModality.Text },
Metadata = new Dictionary<string, BinaryData> { ["topic"] = BinaryData.FromObjectAsJson("lookup_order_followup") },
ToolChoice = RealtimeDefaultToolChoice.None
}
}, cancellationToken);
}
1
2
3
4
5
6connection.conversation.items.create(
type: :function_call_output,
call_id: call_id,
output: JSON.generate(status: "shipped", delivery_date: "2026-05-09")
)
connection.response.create(output_modalities: [:text], tool_choice: :none)
Function Calling の手順をイベントごとに確認するには、「会話の管理」を参照してください。
MCP ツールは、リモート MCP サーバーがすでに提供しているツールを使う場合や、既存のモデルで従来の組み込みコネクタを使う場合に適しています。関数ツールと異なり、MCP ツールは Realtime API 自体が実行します。
Realtime では、MCP ツールを次の項目で定義します。
type: "mcp"
server_label
server_url または connector_id のいずれか
- 任意項目の
authorization と headers
- 任意項目の
allowed_tools
- 任意項目の
require_approval
- 任意項目の
server_description
次の例では、ドキュメント用の MCP サーバーをセッション全体で利用できるようにします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19const event = {
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2.1",
output_modalities: ["text"],
tools: [
{
type: "mcp",
server_label: "openai_docs",
server_url: "https://developers.openai.com/mcp",
allowed_tools: ["search_openai_docs", "fetch_openai_doc"],
require_approval: "never",
},
],
},
};
ws.send(JSON.stringify(event));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19event = {
"type": "session.update",
"session": {
"type": "realtime",
"model": "gpt-realtime-2.1",
"output_modalities": ["text"],
"tools": [
{
"type": "mcp",
"server_label": "openai_docs",
"server_url": "https://developers.openai.com/mcp",
"allowed_tools": ["search_openai_docs", "fetch_openai_doc"],
"require_approval": "never",
}
],
},
}
ws.send(json.dumps(event))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20if err := conn.Send(ctx, realtime.RealtimeClientEventParamOfSessionUpdate(realtime.RealtimeSessionCreateRequestParam{
Model: "gpt-realtime-2.1",
OutputModalities: []string{"text"},
Tools: realtime.RealtimeToolsConfigParam{
{
OfMcp: &realtime.RealtimeToolsConfigUnionMcpParam{
ServerLabel: "openai_docs",
ServerURL: openai.String("https://developers.openai.com/mcp"),
AllowedTools: realtime.RealtimeToolsConfigUnionMcpAllowedToolsParam{
OfMcpAllowedTools: []string{"search_openai_docs", "fetch_openai_doc"},
},
RequireApproval: realtime.RealtimeToolsConfigUnionMcpRequireApprovalParam{
OfMcpToolApprovalSetting: openai.String("never"),
},
},
},
},
})); err != nil {
return err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
import java.util.List;
connection.send(
RealtimeClientEvent.ofSessionUpdate(
SessionUpdateEvent.builder()
.session(
RealtimeSessionCreateRequest.builder()
.model("gpt-realtime-2.1")
.addOutputModality(RealtimeSessionCreateRequest.OutputModality.TEXT)
.addTool(
RealtimeToolsConfigUnion.Mcp.builder()
.serverLabel("openai_docs")
.serverUrl("https://developers.openai.com/mcp")
.allowedToolsOfMcp(
List.of("search_openai_docs", "fetch_openai_doc"))
.requireApproval(
RealtimeToolsConfigUnion.Mcp.RequireApproval
.McpToolApprovalSetting.NEVER)
.build())
.build())
.build()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27using OpenAI.Realtime;
#pragma warning disable OPENAI002
await session.SendCommandAsync(new RealtimeClientCommandSessionUpdate(new RealtimeConversationSessionOptions
{
Model = "gpt-realtime-2.1",
OutputModalities =
{
RealtimeOutputModality.Text
},
Tools =
{
new RealtimeMcpTool("openai_docs", new Uri("https://developers.openai.com/mcp"))
{
AllowedTools = new()
{
ToolNames =
{
"search_openai_docs",
"fetch_openai_doc"
}
},
ToolCallApprovalPolicy = RealtimeDefaultMcpToolCallApprovalPolicy.NeverRequireApproval
}
}
}), timeout.Token);
1
2
3
4
5
6
7
8
9
10
11
12
13
14connection.session.update(
type: :realtime,
model: "gpt-realtime-2.1",
output_modalities: [:text],
tools: [
{
type: :mcp,
server_label: "openai_docs",
server_url: "https://developers.openai.com/mcp",
allowed_tools: ["search_openai_docs", "fetch_openai_doc"],
require_approval: :never
}
]
)
connector_id は、2026 年 9 月 1 日より後にリリースされたモデルでは非推奨です。リモート MCP サーバーに接続するには server_url を使います。ローカル MCP サーバーに接続するには
tunnel_id を指定し、
セキュア MCP トンネルを経由します。既存のモデルは引き続きコネクタをサポートします。次の例で使用する gpt-realtime-1.5 は、2026 年 9 月 1 日より前にリリースされたモデルです。
組み込みコネクタも MCP ツールと同じ形式で定義しますが、指定するのは connector_id で、server_url は使いません。たとえば、Google カレンダーでは connector_googlecalendar を使います。Realtime では、予定やメールの検索、内容の取得などの読み取り操作に組み込みコネクタを使います。ユーザーの OAuth アクセストークンを authorization に指定し、可能な限り allowed_tools で利用できるツールを絞り込んでください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const event = {
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-1.5",
output_modalities: ["text"],
tools: [
{
type: "mcp",
server_label: "google_calendar",
connector_id: "connector_googlecalendar",
authorization: "<google-oauth-access-token>",
allowed_tools: ["search_events", "read_event"],
require_approval: "never",
},
],
},
};
ws.send(JSON.stringify(event));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import os
connector_authorization = os.environ["OPENAI_CONNECTOR_AUTHORIZATION"]
event = {
"type": "session.update",
"session": {
"type": "realtime",
"model": "gpt-realtime-1.5",
"output_modalities": ["text"],
"tools": [
{
"type": "mcp",
"server_label": "google_calendar",
"connector_id": "connector_googlecalendar",
"authorization": connector_authorization,
"allowed_tools": ["search_events", "read_event"],
"require_approval": "never",
}
],
},
}
ws.send(json.dumps(event))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26accessToken := os.Getenv("OPENAI_MCP_ACCESS_TOKEN")
if accessToken == "" {
return fmt.Errorf("OPENAI_MCP_ACCESS_TOKEN is required")
}
if err := conn.Send(ctx, realtime.RealtimeClientEventParamOfSessionUpdate(realtime.RealtimeSessionCreateRequestParam{
Model: "gpt-realtime-1.5",
OutputModalities: []string{"text"},
Tools: realtime.RealtimeToolsConfigParam{
{
OfMcp: &realtime.RealtimeToolsConfigUnionMcpParam{
ServerLabel: "google_calendar",
ConnectorID: "connector_googlecalendar",
Authorization: param.NewOpt(accessToken),
AllowedTools: realtime.RealtimeToolsConfigUnionMcpAllowedToolsParam{
OfMcpAllowedTools: []string{"search_events", "read_event"},
},
RequireApproval: realtime.RealtimeToolsConfigUnionMcpRequireApprovalParam{
OfMcpToolApprovalSetting: openai.String("never"),
},
},
},
},
})); err != nil {
return err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
import java.util.List;
connection.send(
RealtimeClientEvent.ofSessionUpdate(
SessionUpdateEvent.builder()
.session(
RealtimeSessionCreateRequest.builder()
.model("gpt-realtime-1.5")
.addOutputModality(RealtimeSessionCreateRequest.OutputModality.TEXT)
.addTool(
RealtimeToolsConfigUnion.Mcp.builder()
.serverLabel("google_calendar")
.connectorId(
RealtimeToolsConfigUnion.Mcp.ConnectorId
.CONNECTOR_GOOGLECALENDAR)
.authorization(System.getenv("OPENAI_CONNECTOR_AUTHORIZATION"))
.allowedToolsOfMcp(List.of("search_events", "read_event"))
.requireApproval(
RealtimeToolsConfigUnion.Mcp.RequireApproval
.McpToolApprovalSetting.NEVER)
.build())
.build())
.build()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28using OpenAI.Realtime;
#pragma warning disable OPENAI002
await session.SendCommandAsync(new RealtimeClientCommandSessionUpdate(new RealtimeConversationSessionOptions
{
Model = "gpt-realtime-1.5",
OutputModalities =
{
RealtimeOutputModality.Text
},
Tools =
{
new RealtimeMcpTool("google_calendar", RealtimeMcpToolConnectorId.GoogleCalendar)
{
AuthorizationToken = Environment.GetEnvironmentVariable("OPENAI_CONNECTOR_AUTHORIZATION")!,
AllowedTools = new()
{
ToolNames =
{
"search_events",
"read_event"
}
},
ToolCallApprovalPolicy = RealtimeDefaultMcpToolCallApprovalPolicy.NeverRequireApproval
}
}
}), timeout.Token);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17access_token = ENV.fetch("OPENAI_MCP_ACCESS_TOKEN")
connection.session.update(
type: :realtime,
model: "gpt-realtime-1.5",
output_modalities: [:text],
tools: [
{
type: :mcp,
server_label: "google_calendar",
connector_id: "connector_googlecalendar",
authorization: access_token,
allowed_tools: ["search_events", "read_event"],
require_approval: :never
}
]
)
リモート MCP サーバーが
会話のコンテキスト全体を自動的に受け取ることはありません。ただし、モデルがツール呼び出しで送信したデータはすべてサーバーから参照できます。
利用できるツールを必要最小限に絞るには allowed_tools を使います。自動実行を許可しない操作には必ず承認を求めてください。
Realtime の function ツールと異なり、リモート MCP ツールは Realtime API 自体が実行します。クライアントはリモートツールを実行せず、function_call_output も返しません。クライアントはツールへのアクセスを設定し、MCP のライフサイクルイベントを受け取ります。サーバーが承認を求めた場合は、必要に応じて承認リクエストへの応答を送信します。
一般的な流れは次のとおりです。
session.update または response.create を送信します。tools には、type が mcp のエントリを含めます。
- サーバーがツールのインポートを開始し、
mcp_list_tools.in_progress を送信します。
- ツール一覧の取得中、モデルはまだ読み込まれていないツールを呼び出せません。そのツールが必要なターンを開始する前に読み込みを待つ場合は、
mcp_list_tools.completed を待ち受けます。conversation.item.done イベントのうち、item.type が mcp_list_tools のイベントで、実際にインポートされたツールの名前を確認できます。インポートに失敗すると、mcp_list_tools.failed が届きます。
- ユーザーが発話するかテキストを送信すると、クライアントがレスポンスを作成するか、セッションの設定に従ってレスポンスが自動的に作成されます。
- モデルが MCP ツールを選ぶと、
response.mcp_call_arguments.delta と response.mcp_call_arguments.done が届きます。
- 承認が必要な場合、サーバーは
item.type が mcp_approval_request のアイテムを会話に追加します。クライアントは mcp_approval_response アイテムで応答する必要があります。
- ツールの実行が始まると、
response.mcp_call.in_progress が届きます。成功すると、その後に response.output_item.done イベントが届きます。このイベントの item.type は mcp_call です。失敗すると、response.mcp_call.failed が届きます。
response.done は、そのレスポンスに含まれる MCP 呼び出しが完了する前に届くことがあります。レスポンスとその MCP 呼び出しがすべて完了したら、もう一度 response.create イベントを送信し、モデルが結果を使って会話を続けられるようにします。モデルがさらに MCP 呼び出しを行った場合は、この手順を繰り返します。Realtime API は、こうした後続のレスポンスを自動では作成しません。
次のイベントハンドラーは、MCP の主要なライフサイクルイベントをログに記録します。後続のレスポンスは管理しません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82function parseRealtimeEvent(rawMessage) {
if (typeof rawMessage === "string") {
return JSON.parse(rawMessage);
}
if (typeof rawMessage?.data === "string") {
return JSON.parse(rawMessage.data);
}
return JSON.parse(rawMessage.toString());
}
function getOutputText(item) {
if (item.type !== "message") return "";
return (item.content ?? [])
.filter((part) => part.type === "output_text")
.map((part) => part.text)
.join("");
}
ws.on("message", (rawMessage) => {
const event = parseRealtimeEvent(rawMessage);
switch (event.type) {
case "mcp_list_tools.in_progress":
console.log("Listing MCP tools for item:", event.item_id);
break;
case "mcp_list_tools.completed":
console.log("MCP tool listing complete for item:", event.item_id);
break;
case "mcp_list_tools.failed":
console.error("MCP tool listing failed for item:", event.item_id);
break;
case "conversation.item.done":
if (event.item.type === "mcp_list_tools") {
const names = event.item.tools.map((tool) => tool.name).join(", ");
console.log(`MCP tools ready on ${event.item.server_label}: ${names}`);
}
if (event.item.type === "mcp_approval_request") {
console.log(
"Approval required for:",
event.item.name,
event.item.arguments
);
}
break;
case "response.mcp_call_arguments.done":
console.log("Final MCP call arguments:", event.arguments);
break;
case "response.mcp_call.in_progress":
console.log("Running MCP tool for item:", event.item_id);
break;
case "response.mcp_call.failed":
console.error("MCP tool call failed for item:", event.item_id);
break;
case "response.output_item.done":
if (event.item.type === "mcp_call") {
console.log(
`MCP output from ${event.item.server_label}.${event.item.name}:`,
event.item.output
);
}
if (event.item.type === "message") {
console.log("Assistant:", getOutputText(event.item));
}
break;
case "response.done":
console.log("Realtime turn complete.");
break;
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61def on_message(ws, message):
event = json.loads(message)
event_type = event["type"]
if event_type == "mcp_list_tools.in_progress":
print("Listing MCP tools for item:", event["item_id"])
return
if event_type == "mcp_list_tools.completed":
print("MCP tool listing complete for item:", event["item_id"])
return
if event_type == "mcp_list_tools.failed":
print("MCP tool listing failed for item:", event["item_id"])
return
if event_type == "conversation.item.done":
item = event["item"]
if item["type"] == "mcp_list_tools":
names = ", ".join(tool["name"] for tool in item["tools"])
print(f"MCP tools ready on {item['server_label']}: {names}")
return
if item["type"] == "mcp_approval_request":
print("Approval required for:", item["name"], item["arguments"])
return
if event_type == "response.mcp_call_arguments.done":
print("Final MCP call arguments:", event["arguments"])
return
if event_type == "response.mcp_call.in_progress":
print("Running MCP tool for item:", event["item_id"])
return
if event_type == "response.mcp_call.failed":
print("MCP tool call failed for item:", event["item_id"])
return
if event_type == "response.output_item.done":
item = event["item"]
if item["type"] == "mcp_call":
print(
f"MCP output from {item['server_label']}.{item['name']}:",
item.get("output"),
)
return
if item["type"] == "message":
text_parts = [
part["text"]
for part in item.get("content", [])
if part["type"] == "output_text"
]
print("Assistant:", "".join(text_parts))
return
if event_type == "response.done":
print("Realtime turn complete.")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// Call for each received event. Tool results can arrive after response.done.
func logMCPEvent(event realtime.RealtimeServerEventUnion) error {
switch e := event.AsAny().(type) {
case realtime.RealtimeErrorEvent:
return fmt.Errorf("Realtime API error: %s", e.Error.Message)
case realtime.McpListToolsFailed, realtime.ResponseMcpCallFailed:
return fmt.Errorf("MCP failed: %s", event.Type)
case realtime.ResponseOutputItemDoneEvent:
item := e.Item.OfRealtimeMcpToolCall
if item.Type == "mcp_call" {
if item.JSON.Error.Valid() || !item.JSON.Output.Valid() {
return fmt.Errorf("MCP call failed or has no output")
}
fmt.Fprintln(log.Writer(), "Tool output:", item.Output)
}
}
return nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77completed_events = {}
expected_calls = []
completed_calls = {}
complete = connection.find do |event|
case event
when OpenAI::Realtime::McpListToolsInProgress
puts("Listing MCP tools for item: #{event.item_id}")
when OpenAI::Realtime::McpListToolsFailed
raise "MCP tool listing failed for item: #{event.item_id}"
when OpenAI::Realtime::McpListToolsCompleted
puts("MCP tools ready for item: #{event.item_id}")
connection.response.create(
output_modalities: [:text],
input: [
{
type: :message,
role: :user,
content: [
{
type: :input_text,
text: "Which Realtime API transport should browser clients use?"
}
]
}
],
tool_choice: :required,
parallel_tool_calls: false
)
when OpenAI::Realtime::ConversationItemDone
item = event.item
case item
when OpenAI::Realtime::RealtimeMcpListTools
names = item.tools.map(&:name).join(", ")
puts("MCP tools ready on #{item.server_label}: #{names}")
when OpenAI::Realtime::RealtimeMcpApprovalRequest
puts("Approval required for: #{item.name} #{item.arguments}")
end
when OpenAI::Realtime::ResponseMcpCallArgumentsDone
puts("Final MCP call arguments: #{event.arguments}")
when OpenAI::Realtime::ResponseMcpCallInProgress
puts("Running MCP tool for item: #{event.item_id}")
when OpenAI::Realtime::ResponseMcpCallCompleted
puts("MCP tool call completed: #{event.item_id}")
when OpenAI::Realtime::ResponseMcpCallFailed
raise "MCP tool call failed: #{event.item_id}"
when OpenAI::Realtime::ResponseOutputItemDoneEvent
item = event.item
case item
when OpenAI::Realtime::RealtimeMcpToolCall
raise "MCP tool call failed" if item.error || item.output.nil?
completed_calls[item.id] = true
puts("MCP output from #{item.server_label}.#{item.name}: #{item.output}")
when OpenAI::Realtime::RealtimeConversationItemAssistantMessage
text = item.content.filter_map do |content|
content.text if content.type == :output_text
end.join
puts("Assistant: #{text}")
end
when OpenAI::Realtime::RealtimeErrorEvent
raise "Realtime API error: #{event.error.message}"
when OpenAI::Realtime::ResponseDoneEvent
raise "Realtime response did not complete" unless event.response.status == :completed
expected_calls.replace(Array(event.response.output).grep(OpenAI::Realtime::RealtimeMcpToolCall).map(&:id))
raise "The response did not call an MCP tool" if expected_calls.empty?
completed_events[:response] = true
end
if completed_events[:response] && expected_calls.all? { |id| completed_calls[id] }
puts("Realtime turn complete.")
true
else
false
end
end
raise "Connection closed before the MCP tool result arrived" unless complete
mcp_list_tools.failed が届いた場合、Realtime API がリモートサーバーまたはコネクタからツールをインポートできませんでした。server_url または connector_id の値、認証情報、サーバーへの接続、allowed_tools に指定したツール名を確認してください。
response.mcp_call.failed が届いた場合、モデルはツールを選択しましたが、呼び出しが完了しませんでした。イベントのペイロードと後続の mcp_call アイテムを調べ、MCP プロトコル、ツールの実行、通信のいずれかでエラーが発生していないか確認してください。
mcp_approval_request に対応する mcp_approval_response がない場合、クライアントが明示的に承認または拒否するまでツール呼び出しは先に進みません。
mcp_list_tools.in_progress の状態が続いている間にターンが始まった場合、そのターンで利用できるのは、すでに読み込みが完了したツールだけです。
- レスポンスに
tool_choice: "required" を指定していても、その時点で利用できるツールがなければ、モデルが呼び出せるツールはありません。mcp_list_tools.completed を待つか、少なくとも 1 つのツールがインポートされたことを確認してください。ツールが不要なターンでは別の tool_choice を指定することもできます。
- インポート開始前に MCP ツール定義の検証に失敗する場合は、次の点を確認してください。
server_label が同じ tools 配列内で重複していると検証に失敗します。ほかにも、server_url と connector_id の両方を指定している、最初のセッション作成リクエストでどちらも指定していない、無効な connector_id を使っている、authorization と headers.Authorization の両方を送信している、といった原因があります。コネクタを使う場合は、headers.Authorization を送信しないでください。
ツールの実行に承認が必要な場合、Realtime API は mcp_approval_request アイテムを会話に追加します。処理を続けるには、新しい conversation.item.create イベントの item.type に mcp_approval_response を指定して送信します。
1
2
3
4
5
6
7
8
9
10
11
12function approveMcpRequest(approvalRequestId) {
const event = {
type: "conversation.item.create",
item: {
type: "mcp_approval_response",
approval_request_id: approvalRequestId,
approve: true,
},
};
ws.send(JSON.stringify(event));
}
1
2
3
4
5
6
7
8
9
10
11
12# Use the ID from the received MCP approval-request item.
def approve_mcp_request(ws, approval_request_id):
event = {
"type": "conversation.item.create",
"item": {
"type": "mcp_approval_response",
"approval_request_id": approval_request_id,
"approve": True,
},
}
ws.send(json.dumps(event))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// Call with the approval request received in conversation.item.done.
func approveDocumentationSearch(
ctx context.Context,
conn *realtime.RealtimeConnection,
request realtime.RealtimeMcpApprovalRequest,
) error {
// This example permits only a read-only search of the OpenAI documentation.
if request.ID == "" || request.ServerLabel != "openai_docs" || request.Name != "search_openai_docs" {
return fmt.Errorf("unapproved tool requested")
}
// Derive a distinct item ID within the API's 32-character limit.
approvalID := sha256.Sum256([]byte(request.ID))
return conn.Send(ctx, realtime.RealtimeClientEventParamOfConversationItemCreate(realtime.RealtimeMcpApprovalResponseParam{
ID: fmt.Sprintf("%x", approvalID[:16]),
ApprovalRequestID: request.ID,
Approve: true,
}))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.core.JsonMissing;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
import com.openai.models.responses.ToolChoiceOptions;
// Replace https://mcp.example.com/mcp with your company's MCP server URL.
static void approveMcpRequest(RealtimeConnection connection, String approvalRequestId)
throws Exception {
send(
connection,
RealtimeClientEvent.ofConversationItemCreate(
ConversationItemCreateEvent.builder()
.item(
RealtimeMcpApprovalResponse.builder()
.id(java.util.UUID.randomUUID().toString().replace("-", ""))
.approvalRequestId(approvalRequestId)
.approve(true)
.build())
.build()));
}
// Retry only explicit admission rejections; these guarantee nothing was sent.
private static void send(RealtimeConnection connection, RealtimeClientEvent event)
throws Exception {
long deadline = System.nanoTime() + java.util.concurrent.TimeUnit.SECONDS.toNanos(5);
while (true) {
try {
connection.send(event);
return;
} catch (com.openai.core.http.WebSocketWriteNotAttempted.Busy busy) {
if (System.nanoTime() >= deadline) throw busy;
Thread.sleep(10);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13using System.Text.Json;
using OpenAI.Realtime;
#pragma warning disable OPENAI002
// Connect to the official OpenAI documentation MCP server.
internal static async Task ApproveMcpRequestAsync(RealtimeSessionClient session, string approvalRequestId, CancellationToken cancellationToken)
{
await session.SendCommandAsync(new RealtimeClientCommandConversationItemCreate(new RealtimeMcpToolCallApprovalResponseItem(approvalRequestId, true)
{
Id = Guid.NewGuid().ToString("N")
}), cancellationToken);
}
1
2
3
4
5
6
7
8
9# Use the ID from the received MCP approval-request item.
approval_request_id = item.id
connection.conversation.items.create(
type: :mcp_approval_response,
id: "approval_#{SecureRandom.hex(8)}",
approval_request_id: approval_request_id,
approve: true
)
リクエストを拒否するには、approve を false に設定します。必要に応じて reason に理由を指定できます。
MCP を 1 ターンだけ利用できるようにするには、同じ MCP ツールオブジェクトを response.tools に追加します。この場合、session.tools は使いません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31const event = {
type: "response.create",
response: {
output_modalities: ["text"],
tool_choice: "required",
parallel_tool_calls: false,
input: [
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Which transport should I use for browser clients in the Realtime API?",
},
],
},
],
tools: [
{
type: "mcp",
server_label: "openai_docs",
server_url: "https://developers.openai.com/mcp",
allowed_tools: ["search_openai_docs", "fetch_openai_doc"],
require_approval: "never",
},
],
},
};
ws.send(JSON.stringify(event));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31event = {
"type": "response.create",
"response": {
"output_modalities": ["text"],
"tool_choice": "required",
"parallel_tool_calls": False,
"input": [
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "Which transport should I use for browser clients in the Realtime API?",
}
],
}
],
"tools": [
{
"type": "mcp",
"server_label": "openai_docs",
"server_url": "https://developers.openai.com/mcp",
"allowed_tools": ["search_openai_docs", "fetch_openai_doc"],
"require_approval": "never",
}
],
},
}
ws.send(json.dumps(event))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// input contains the conversation items for this response.
if err := conn.Send(ctx, realtime.RealtimeClientEventUnionParam{
OfResponseCreate: &realtime.ResponseCreateEventParam{
Response: realtime.RealtimeResponseCreateParams{
ParallelToolCalls: openai.Bool(false),
Input: input,
OutputModalities: []string{"text"},
ToolChoice: realtime.RealtimeResponseCreateParamsToolChoiceUnion{
OfToolChoiceMode: param.NewOpt[responses.ToolChoiceOptions]("required"),
},
Tools: []realtime.RealtimeResponseCreateParamsToolUnion{
{
OfMcp: &realtime.RealtimeResponseCreateMcpToolParam{
ServerLabel: "openai_docs",
ServerURL: openai.String("https://developers.openai.com/mcp"),
AllowedTools: realtime.RealtimeResponseCreateMcpToolAllowedToolsUnionParam{
OfMcpAllowedTools: []string{"search_openai_docs", "fetch_openai_doc"},
},
RequireApproval: realtime.RealtimeResponseCreateMcpToolRequireApprovalUnionParam{
OfMcpToolApprovalSetting: openai.String("never"),
},
},
},
},
},
},
}); err != nil {
return err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.core.JsonMissing;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
import java.util.List;
connection.send(
RealtimeClientEvent.ofResponseCreate(
ResponseCreateEvent.builder()
.response(
RealtimeResponseCreateParams.builder()
.toolChoice(com.openai.models.responses.ToolChoiceOptions.REQUIRED)
.parallelToolCalls(false)
.metadata(
RealtimeResponseCreateParams.Metadata.builder()
.putAdditionalProperty(
"topic", com.openai.core.JsonValue.from("mcp_initial"))
.build())
.addOutputModality(RealtimeResponseCreateParams.OutputModality.TEXT)
.addInput(
RealtimeConversationItemUserMessage.builder()
.object_(JsonMissing.of())
.addContent(
RealtimeConversationItemUserMessage.Content.builder()
.type(
RealtimeConversationItemUserMessage.Content.Type
.INPUT_TEXT)
.text(
"Which transport should I use for browser clients in the Realtime API?")
.build())
.build())
.addTool(
RealtimeResponseCreateMcpTool.builder()
.serverLabel("openai_docs")
.serverUrl("https://developers.openai.com/mcp")
.allowedToolsOfMcp(
List.of("search_openai_docs", "fetch_openai_doc"))
.requireApproval(
RealtimeResponseCreateMcpTool.RequireApproval
.McpToolApprovalSetting.NEVER)
.build())
.build())
.build()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36using System.Text.Json;
using OpenAI.Realtime;
#pragma warning disable OPENAI002
await session.SendCommandAsync(new RealtimeClientCommandResponseCreate
{
ResponseOptions = new()
{
Metadata = new Dictionary<string, BinaryData> { ["topic"] = BinaryData.FromObjectAsJson("mcp_initial") },
ToolChoice = RealtimeDefaultToolChoice.Required,
OutputModalities =
{
RealtimeOutputModality.Text
},
InputItems =
{
RealtimeItem.CreateUserMessageItem("Which transport should I use for browser clients in the Realtime API?")
},
Tools =
{
new RealtimeMcpTool("openai_docs", new Uri("https://developers.openai.com/mcp"))
{
AllowedTools = new()
{
ToolNames =
{
"search_openai_docs",
"fetch_openai_doc"
}
},
ToolCallApprovalPolicy = RealtimeDefaultMcpToolCallApprovalPolicy.NeverRequireApproval
}
}
}
}, timeout.Token);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26connection.response.create(
output_modalities: [:text],
tool_choice: :required,
parallel_tool_calls: false,
input: [
{
type: :message,
role: :user,
content: [
{
type: :input_text,
text: "Which Realtime API transport should browser clients use?"
}
]
}
],
tools: [
{
type: :mcp,
server_label: "openai_docs",
server_url: "https://developers.openai.com/mcp",
allowed_tools: ["search_openai_docs", "fetch_openai_doc"],
require_approval: :never
}
]
)
この方法は、外部のコンテキストが必要なレスポンスが 1 つだけの場合や、ターンごとに異なる MCP サーバーを使いたい場合に便利です。
server_label は、現在の Realtime セッション内でツール定義を識別するための固定のラベルです。サーバーまたはコネクタを初めて定義するときに、server_label とともに server_url または connector_id を指定します。以降の session.update または response.create イベントでは、同じ server_label を指定するだけで Realtime API が以前の定義を再利用します。ツールオブジェクト全体を再送する必要はありません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27const event = {
type: "response.create",
response: {
output_modalities: ["text"],
input: [
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Check my schedule for this afternoon.",
},
],
},
],
// Reuses the google_calendar connector defined earlier in this session.
tools: [
{
type: "mcp",
server_label: "google_calendar",
},
],
},
};
ws.send(JSON.stringify(event));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27event = {
"type": "response.create",
"response": {
"output_modalities": ["text"],
"input": [
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "Check my schedule for this afternoon.",
}
],
}
],
# Reuses the google_calendar connector defined earlier in this session.
"tools": [
{
"type": "mcp",
"server_label": "google_calendar",
}
],
},
}
ws.send(json.dumps(event))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// input contains the conversation items for this response.
if err := conn.Send(ctx, realtime.RealtimeClientEventUnionParam{
OfResponseCreate: &realtime.ResponseCreateEventParam{
Response: realtime.RealtimeResponseCreateParams{
OutputModalities: []string{"text"},
Input: input,
Tools: []realtime.RealtimeResponseCreateParamsToolUnion{
{
OfMcp: &realtime.RealtimeResponseCreateMcpToolParam{
ServerLabel: "google_calendar",
},
},
},
ToolChoice: realtime.RealtimeResponseCreateParamsToolChoiceUnion{
OfToolChoiceMode: param.NewOpt(responses.ToolChoiceOptions("required")),
},
},
},
}); err != nil {
return err
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50import com.openai.client.okhttp.OkHttpClient;
import com.openai.core.ClientOptions;
import com.openai.helpers.RealtimeConnection;
import com.openai.helpers.RealtimeWebSocketOptions;
import com.openai.models.realtime.*;
import java.util.List;
send(
connection,
RealtimeClientEvent.ofResponseCreate(
ResponseCreateEvent.builder()
.response(
RealtimeResponseCreateParams.builder()
.metadata(
RealtimeResponseCreateParams.Metadata.builder()
.putAdditionalProperty(
"topic", com.openai.core.JsonValue.from("mcp_initial"))
.build())
.addOutputModality(RealtimeResponseCreateParams.OutputModality.TEXT)
.addInput(
RealtimeConversationItemUserMessage.builder()
.addContent(
RealtimeConversationItemUserMessage.Content.builder()
.type(
RealtimeConversationItemUserMessage.Content.Type
.INPUT_TEXT)
.text("Check my schedule for this afternoon.")
.build())
.build())
.addTool(
RealtimeResponseCreateMcpTool.builder()
.serverLabel("google_calendar")
.build())
.build())
.build()));
// Retry only explicit admission rejections; these guarantee nothing was sent.
private static void send(RealtimeConnection connection, RealtimeClientEvent event)
throws Exception {
long deadline = System.nanoTime() + java.util.concurrent.TimeUnit.SECONDS.toNanos(5);
while (true) {
try {
connection.send(event);
return;
} catch (com.openai.core.http.WebSocketWriteNotAttempted.Busy busy) {
if (System.nanoTime() >= deadline) throw busy;
Thread.sleep(10);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26using OpenAI.Realtime;
#pragma warning disable OPENAI002
await session.SendCommandAsync(new RealtimeClientCommandResponseCreate
{
ResponseOptions = new()
{
Metadata = new Dictionary<string, BinaryData> { ["topic"] = BinaryData.FromObjectAsJson("mcp_initial") },
OutputModalities =
{
RealtimeOutputModality.Text
},
InputItems =
{
RealtimeItem.CreateUserMessageItem("Check my schedule for this afternoon.")
},
Tools =
{
new RealtimeMcpTool
{
ServerLabel = "google_calendar"
}
}
}
}, timeout.Token);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21connection.response.create(
output_modalities: [:text],
input: [
{
type: :message,
role: :user,
content: [
{
type: :input_text,
text: "Check my schedule this afternoon."
}
]
}
],
tools: [
{
type: :mcp,
server_label: "google_calendar"
}
]
)
定義を再利用できるのは同じセッション内に限られます。新しい Realtime セッションを開始する場合は、サーバーがツール一覧をインポートできるように、MCP 定義全体を再送してください。