Voice LiveでMCPサーバのツールを使う - function callingとの違いと並列ツール呼び出し #AI - Qiita
@miyaura(Miyaura)

Using MCP Server Tools in Voice Live - Differences from Function Calling and Parallel Tool Calls

  • Azure
  • MicrosoftFoundry
  • VoiceLiveAPI
  • MCP
  • AI
0
Last updated at 2026-11-10Posted at 2026-11-10

Trying MCP Server Tools in Voice Live

I wanted a voice-conversation agent to be able to look things up using external tools, so I tried it with the Voice Live API.

The Voice Live API is an API for building agents that converse using voice. There are two ways to make the model use tools:

  • function calling: The client defines the tools and executes them in response to calls from the model
  • MCP server: An MCP (Model Context Protocol) server is registered with the session, and the Voice Live service calls the tools

This article describes how MCP server tools differ from function calling, the setup, and what happened when I actually ran it and how I addressed it. It also describes parallel tool calls, where multiple tools are used to answer a single question. This is the third installment in the Voice Live feature explanation series.

List of articles in the series (SDK 1.2.0 / API 2026-07-15)

  1. Changes in Voice Live SDK 1.2.0 and API 2026-07-15 GA
    Classifying Features That Lack Types in the SDK
  2. Handling Conversation “Pauses” with the Voice Live API
    Speak first, wait for hesitation, interrupt, and speak during wait times
  3. Improving Voice Live Audio Input Quality
    Choosing Noise Suppression, Echo Cancellation, and Live-Reference AEC
  4. Using MCP Server Tools in Voice Live (This Article)
    Differences from Function Calling and Parallel Tool Calls
  5. Calling a Foundry Agent as a Tool in Voice Live
    How to use the chat-supervisor pattern and choose the model for the preceding stage
  6. Making a Microsoft Foundry Agent Speak with a “Voice of Your Own” Created from 30 Seconds of Audio
    From Applying for Personal Voice to Implementation
  7. Choosing Among Voice Live API Avatars
    Configuring and implementing standard, custom photo, and custom video
  8. Receiving Voice Live avatar video over WebSocket
    Differences from WebRTC and video formats

Bonus episode: Connecting to Voice Live's WebRTC audio connection with SIPSorcery for .NET
A record of comparing the SDP line by line

The code used for validation is available here.

This article reflects information as of October 2026. It applies to Voice Live API 2026-07-15 (GA) and the .NET SDK Azure.AI.VoiceLive 1.2.0.

Preparation

You can get set up by following the official quickstart from resource creation through running your first voice conversation in C#. See the Voice Live overview page for supported regions.

When trying the content of this article in the quickstart environment, note the following points.

    When using MCP, set the API version to 2026-04-10 or later
  • The parallel tool calls setting (parallel_tool_calls) is available with API version 2026-06-01-preview or later
  • Use the Azure.AI.VoiceLive 1.2.0 .NET SDK

How tools are invoked in Voice Live

In function calling and MCP, tools are executed in different places.

function calling MCP server
Tool definition Passed by the client in session.update The client registers the MCP server, and the service retrieves the list of tools
Tool execution Client Voice Live service
What is sent to the client function_call (tool name and arguments) Execution notification (such as response.mcp_call.completed)
Tool result Sent by the client via function_call_output Used as-is by the service
Request for a response using the resultThe client sends response.create The client sends response.create

The last line is the same for either approach. To respond using the tool result, the client sends response.create. Voice Live rejects overlapping responses, so this request must be sent only once. MCP Issue 1, introduced later, and parallel tool calls both relate to this rule.

The pattern in which the client declares tools (function calling, MCP) pertains to the Model session. In an Agent session, tools are managed and executed by the agent (on the cloud side).

MCP server registration

session.update
{ "type": "session.update", "session": { "tools": [ { "type": "mcp", "server_label": "deepwiki", "server_url": "https://mcp.deepwiki.com/mcp", "require_approval": "never" } ]} }

With SDK 1.2.0, it can be written using types.

SdkFeatureCatalog.cs (excerpt)
options.Tools.Add(new VoiceLiveMcpServerDefinition("deepwiki", new Uri("https://mcp.deepwiki.com/mcp")) { RequireApproval = new RequireApprovalOption(McpApprovalKind.Never) });

To use MCP, API version 2026-04-10 or later is required.

The default value for require_approval is always. If omitted, an mcp_approval_request is sent to the client every time a tool is called, and the tool is not executed until the client returns mcp_approval_response (approve=true). If approval handling is not implemented, the conversation stops there. To have it run automatically, explicitly specify never. You can also separate them by tool, such as {"never": [...], "always": [...]}.

Issues encountered when running MCP

The sample uses deepwiki, a public MCP server (which answers questions about GitHub repository wikis), asks about the configuration of microsoft/vscode, and confirms that the tool is executed on the server side. During this process, the following occurred.

Issue 1: No response is generated after the tool is executed

MCP tools are executed on the server, but it is the client's role to send response.create using those results to generate a response.In Voice Live, MCP is treated as one form of tool, and, just like function calling, it does not automatically begin a response even when all tool results are in (the last row of the table mentioned above). In Foundry agents as well, MCP is configured as one of the tools, alongside functions, web search, and so on. The OpenAI Realtime API has the same behavior, and the official guide states that it "does not continue automatically."

After receiving response.mcp_call.completed, confirm that the response that invoked the tool (response 1) has closed, and then the client requests the next response (the sample in the official how-to guide is structured the same way).

An MCP call can complete while the response that invoked it is still open. Because responses cannot overlap, the sample tracks whether a response is in progress and, if it is, waits for response.done before making a request.

Solution: Send the response request only once

In actual testing, response.done → response.mcp_call.completed sometimes arrive in that order, so the order cannot be relied upon. The sample keeps a count of "responses that should be requested," and makes only one request, regardless of which arrives later.

VoiceLiveAssistant.cs (excerpt)
case SessionUpdateResponseCreated: responseActive = true; break; case SessionUpdateResponseDone: responseActive = false; await RequestToolFollowUpAsync(); break; case SessionUpdateResponseMcpCallCompleted: // The service executed the tool. It is the client's responsibility to request a response. Interlocked.Increment(ref pendingToolCalls);await RequestToolFollowUpAsync(); break; // ... private async Task RequestToolFollowUpAsync() { // Overlapping responses are rejected, so leave it to the response.done handler if one is in progress if (session == null || responseActive || Volatile.Read(ref toolsInFlight) > 0) return; // Even if response.done and tool completion arrive simultaneously, make the request only once if (Interlocked.Exchange(ref pendingToolCalls, 0) == 0) return; await session.StartResponseAsync(); }

Issue 2: Answering from its own knowledge without calling a tool

This may be a common occurrence in the world of agents. Even when an MCP server is registered, the model will often answer using its own knowledge without calling a tool. It is difficult to distinguish this at a glance from the MCP configuration being broken. In particular, because we were also checking the operation this time, the MCP server we used may not have been well suited to the task.

Solution: Guide the model to use tools through instructions

The sample explicitly states in the session instructions, “Always use tools for questions about repositories.”

instructions (excerpt)
For any question about a GitHub repository, you must call the deepwiki tools instead of answering from memory, even if you believe you know the answer.

You can verify whether the tools were actually called by checking for MCP call entries in response.output_item.done. If they do not appear there, the model answered on its own.

Challenge 3: The tool result is large

deepwiki has a tool called read_wiki_contents that returns the full text of the wiki. When called, it returned a result of approximately 620KB. Since this becomes the input for the spoken answer as-is, I think it is unsuitable for answering via voice.

Solution: Restrict the tools that are used

The sample uses instructions to guide the model to use ask_question for short answers. To reliably restrict them, limit the tools that may be used with allowed_tools in the server definition (AllowedTools in the SDK).

Challenge 4: The items are empty when they are created

The MCP tool list and tool invocation entries are empty at the point of conversation.item.created (both the tool list and the invocation arguments and results). Their contents are populated only after mcp_list_tools.completed or response.output_item.done.

Solution: Display them after their contents have been populated

Displaying them at conversation.item.created results in the incorrect display of “0 tools” and “No results,” so display and record them at mcp_list_tools.completed or response.output_item.done.

Parallel tool calls

When a single question requires multiple tools (for example, “What is the weather and time in Tokyo?”), the model calls multiple tools within a single turn. This is parallel tool calling. In function calling, multiple function_call entries arrive within a single response.

session.update
{ "type": "session.update","session": { "parallel_tool_calls": true, "tools": [ { "type": "function", "name": "get_weather", "parameters": { "...": "..." } }, { "type": "function", "name": "get_time", "parameters": { "...": "..." } } ] }}

The default value of parallel_tool_calls is true. If set to false, the model can call tools one at a time in sequence. In SDK 1.2.0, this can be configured with VoiceLiveSessionOptions.AllowParallelToolCalls.

The important point is that you send all tool results and then send response.create only once. If you send response.create every time you send a result, the response will be triggered twice, resulting in an error.

How to send it Result
response.create for each result Error because it attempts to respond twice
Once, after sending all results get_weather / get_time are each called once, with no errors

As with MCP Issue 1, if you keep track of the “number of responses that should be requested” and request a response only once at the end, this can be used for both function calling and MCP. Note that parallel calls were confirmed only with function tools in the sample.

Summary

  • With function calling, the client executes the tools; with MCP, the Voice Live service executes the tools.
  • With either method, the client sends the request for a response using the tool results.
  • Because responses cannot overlap, send the response request only once.
  • The default value of require_approval is always, so specify never if you want it to execute automatically.
  • When the model does not call a tool, use instructions to guide it to the tool.
  • Use allowed_tools to prevent tools with large results from being used.
  • For parallel tool calls, send all results and then send response.create once.

Reference links

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
Sign upLogin