Nur can use two kinds of tools in a live conversation: Both go in the session’s tools, and you can mix them.

Function tools

A function tool is the OpenAI Realtime format: a name, a description and a JSON schema for the arguments.
When Nur calls it you get response.function_call_arguments.done with a call_id, name and arguments. Run it, then send the result and ask Nur to go on:
Answer every call, even with an error: Nur waits for the result.

Remote MCP servers

Point the session at an MCP server and Nur can use its tools while it talks. EESI lists the server’s tools, offers them to Nur, runs the calls she makes and puts the results back into the conversation. Your app does not handle the calls.

What happens

  1. Your session.update takes effect at once. The server’s tools are listed in the background, so nothing waits on a slow server.
  2. You get mcp_list_tools.in_progress, then mcp_list_tools.completed (or mcp_list_tools.failed). A conversation.item.done with an mcp_list_tools item lists the tools Nur was given. If your first question needs the tools, wait for completed before you ask it.
  3. Nur sees each tool as a function named <server_label>__<tool>, for example deepwiki__ask_wiki_question.
  4. When Nur calls one, you see an mcp_call item (in response.output_item.added and response.done), then response.mcp_call.in_progress, then response.mcp_call.completed or response.mcp_call.failed. A conversation.item.done carries the mcp_call item with its output, or its error.
  5. Nur reads the result and keeps talking. You don’t send anything back.
If the person interrupts the reply that asked for a tool, the call is stopped and its result is dropped, so Nur doesn’t come back later with an answer to something the person moved past. She can call it again if they still want it.

Asking first

A tool the server marks as destructive (destructiveHint in its MCP annotations) never runs on Nur’s word alone. Neither does a tool you list in require_approval. The first time Nur calls it, she asks the person instead, and you get an mcp_approval_request item:
The person can say yes out loud, or your app can show a button and send their answer:
Either way the tool runs once, with the arguments Nur asked about. A yes is good for one run and for one minute. require_approval: "never" cannot switch this off for a tool the server marks as destructive. When others can be heard, on a call or a speakerphone, a yes said in the room may not be the person’s. Set eesi_spoken_approvals to false and only your app’s mcp_approval_response answers: nothing said or typed in the conversation does, and Nur asks the person to answer on their screen. It can change in any session.update; true, the default, lets their words answer again.
Any other value is refused with an invalid_value error, and until you send true or false only your app’s answer counts.

Per reply

response.create may carry its own tools. An MCP entry there names one of the session’s servers by its server_label and stands for that server’s tools; declare servers on the session. A tool_choice of {"type": "mcp", "server_label": "deepwiki", "name": "ask_wiki_question"} names that tool, the way a function tool_choice names a function.

Limits

Security

  • The server must be on the public internet over https. Addresses that point to private networks or cloud metadata services are refused, and so are redirects.
  • authorization and headers are sent to that server and nowhere else. They are not logged and not echoed back to you.
  • Results are third-party content, and Nur is told so: they reach her as the server’s words, not hers and not yours.

With client secrets

A server can be part of a client secret. Minting checks it, and the response shows authorization and headers as [redacted]. With tools locked (the default), the device cannot add, change or remove servers, in session.update or in a reply’s own tools. The person can still approve or decline a tool, since that is their answer to Nur.

Example