Vous pouvez associer des outils à une session Realtime pour que le modèle puisse rechercher des données, exécuter des actions ou appeler des services au cours d’une conversation en direct. Les événements de configuration des outils sont les mêmes, que votre client utilise un canal de données WebRTC ou une connexion WebSocket.
Utilisez un outil de type fonction si votre application doit exécuter l’outil et renvoyer le résultat. Utilisez un outil MCP si vous souhaitez que la Realtime API se connecte pour vous à un serveur d’outils distant.
| Type d’outil |
Quand l’utiliser |
Qui l’exécute |
function |
Votre application gère la logique métier, les vérifications d’approbation ou l’accès à des systèmes privés. |
Votre client ou votre serveur reçoit un appel de fonction et renvoie un élément function_call_output. |
mcp avec server_url |
Vous souhaitez que le modèle appelle les outils mis à disposition par un serveur MCP distant. |
La Realtime API appelle le serveur MCP distant. |
mcp avec connector_id |
Vous utilisez un ancien connecteur intégré avec un modèle existant. |
La Realtime API appelle le connecteur avec l’autorisation que vous fournissez. |
Ajoutez les outils à l’un des deux niveaux suivants :
- Au niveau de la session, avec
session.tools dans session.update, si vous souhaitez que l’outil soit disponible pendant toute la session.
- Au niveau de la réponse, avec
response.tools dans response.create, si vous avez besoin de l’outil pour un seul tour de conversation.
Privilégiez les outils de type fonction lorsque l’exécution doit se faire dans votre application. Le modèle fournit les arguments de l’appel de fonction, puis votre code exécute l’action et renvoie le résultat dans un élément 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
Lorsque le modèle appelle la fonction, écoutez les événements pour détecter l’élément correspondant à l’appel de fonction, exécutez la logique de votre application, puis renvoyez le résultat :
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)
Pour suivre le déroulement complet d’un appel de fonction, événement par événement, consultez le guide Gestion des conversations.
Les outils MCP sont utiles lorsqu’un serveur MCP distant propose déjà l’outil ou qu’un modèle existant utilise un ancien connecteur intégré. Contrairement aux outils de type fonction, les outils MCP sont exécutés directement par la Realtime API.
Dans Realtime, la définition d’un outil MCP comporte les champs suivants :
type: "mcp"
server_label
- Soit
server_url, soit connector_id
authorization et headers (facultatifs)
allowed_tools (facultatif)
require_approval (facultatif)
server_description (facultatif)
Cet exemple rend un serveur MCP de documentation accessible pendant toute la session :
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 est obsolète pour les modèles publiés après le 1er septembre 2026. Utilisez server_url pour vous connecter à un serveur MCP distant ou
tunnel_id pour vous connecter à un serveur MCP local via un
tunnel MCP sécurisé. Les modèles existants continuent de prendre en charge les connecteurs. L’exemple ci-dessous utilise gpt-realtime-1.5, publié avant cette date.
Les connecteurs intégrés se configurent comme les autres outils MCP, mais utilisent connector_id à la place de server_url. Par exemple, Google Agenda utilise connector_googlecalendar. Dans Realtime, utilisez ces connecteurs intégrés pour les opérations de lecture, comme la recherche ou la consultation d’événements ou d’e-mails. Transmettez le jeton d’accès OAuth de l’utilisateur dans authorization et, si possible, limitez les outils accessibles avec 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
}
]
)
Les serveurs MCP distants
ne reçoivent pas automatiquement tout le contexte de la conversation, mais ils peuvent voir toutes les données que le modèle transmet lors d’un appel d’outil.
Limitez les outils accessibles avec allowed_tools et exigez une approbation pour toute action que vous ne lanceriez pas automatiquement.
Contrairement aux outils Realtime de type function, les outils MCP distants sont exécutés directement par la Realtime API. Votre client n’exécute pas l’outil distant et ne renvoie donc pas d’élément function_call_output. Il configure l’accès, écoute les événements du cycle de vie MCP et peut envoyer une réponse à une demande d’approbation du serveur.
Voici le déroulement habituel :
- Envoyez
session.update ou response.create avec une entrée dans tools dont le champ type vaut mcp.
- Le serveur commence à importer les outils et émet
mcp_list_tools.in_progress.
- Tant que le serveur établit la liste des outils, le modèle ne peut appeler que les outils déjà chargés. Si vous souhaitez attendre avant de lancer un tour de conversation qui dépend de ces outils, écoutez l’événement
mcp_list_tools.completed. L’événement conversation.item.done dont le champ item.type vaut mcp_list_tools indique les noms des outils effectivement importés. Si l’importation échoue, vous recevez mcp_list_tools.failed.
- L’utilisateur parle ou envoie du texte. Votre client crée alors une réponse, ou la session la crée automatiquement selon sa configuration.
- Si le modèle choisit un outil MCP, vous recevez
response.mcp_call_arguments.delta et response.mcp_call_arguments.done.
- Si une approbation est requise, le serveur ajoute à la conversation un élément dont le champ
item.type vaut mcp_approval_request. Votre client doit y répondre avec un élément mcp_approval_response.
- Lorsque l’outil commence à s’exécuter, vous recevez
response.mcp_call.in_progress. Si l’appel aboutit, vous recevez ensuite un événement response.output_item.done dont le champ item.type vaut mcp_call. En cas d’échec, vous recevez response.mcp_call.failed.
- L’événement
response.done d’une réponse peut arriver avant la fin des appels MCP associés. Une fois la réponse et tous ses appels MCP terminés, envoyez un nouvel événement response.create pour que le modèle puisse utiliser les résultats et poursuivre la conversation. Répétez cette étape si le modèle effectue d’autres appels MCP. La Realtime API ne crée pas automatiquement ces réponses de suivi.
Ce gestionnaire consigne les principaux événements du cycle de vie MCP. Il ne gère pas les réponses de suivi.
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 : la Realtime API n’a pas pu importer les outils du serveur distant ou du connecteur. Vérifiez la valeur de server_url ou de connector_id, l’authentification, la connexion au serveur et les noms d’outils que vous avez indiqués dans allowed_tools.
response.mcp_call.failed : le modèle a sélectionné un outil, mais l’appel n’a pas abouti. Examinez le contenu de l’événement, puis celui de l’élément mcp_call reçu ultérieurement, pour repérer les erreurs de protocole MCP, d’exécution ou de transport.
- Vous recevez un élément
mcp_approval_request sans élément mcp_approval_response correspondant : l’appel d’outil reste en attente tant que votre client ne l’a pas explicitement approuvé ou rejeté.
- Un tour de conversation commence alors que
mcp_list_tools.in_progress est toujours actif : seuls les outils déjà chargés peuvent être appelés pendant ce tour.
- Une réponse utilise
tool_choice: "required" alors qu’aucun outil n’est disponible : le modèle ne peut appeler aucun outil. Attendez mcp_list_tools.completed, vérifiez qu’au moins un outil a été importé ou choisissez une autre valeur de tool_choice pour les tours qui ne nécessitent pas d’outil.
- La validation de la définition de l’outil MCP échoue avant le début de l’importation : les causes fréquentes sont un
server_label en double dans le même tableau tools, la présence simultanée de server_url et de connector_id, l’absence de ces deux champs dans la requête initiale de création de session, une valeur de connector_id non valide ou l’envoi simultané de authorization et de headers.Authorization. Pour les connecteurs, n’envoyez jamais headers.Authorization.
Si un outil nécessite une approbation, la Realtime API insère un élément mcp_approval_request dans la conversation. Pour poursuivre, envoyez un nouvel événement conversation.item.create dont le champ item.type vaut 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
)
Pour rejeter la demande, définissez approve sur false et, si vous le souhaitez, indiquez le motif dans reason.
Si MCP doit être disponible pour un seul tour de conversation, associez le même objet de configuration de l’outil MCP à response.tools au lieu de 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
}
]
)
Cette configuration est utile lorsqu’une seule réponse nécessite des informations externes ou que vous souhaitez utiliser des serveurs MCP différents selon le tour de conversation.
server_label est l’identifiant stable d’une définition d’outil dans la session Realtime en cours. Une fois le serveur ou le connecteur défini avec server_label et l’un des deux champs server_url ou connector_id, vous pouvez, dans les événements session.update ou response.create suivants, indiquer uniquement ce même server_label. La Realtime API réutilise la définition associée sans que vous ayez à renvoyer l’objet de configuration complet.
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"
}
]
)
Cette définition reste valable uniquement dans la session en cours. Si vous démarrez une nouvelle session Realtime, renvoyez la définition MCP complète pour que le serveur puisse importer sa liste d’outils.