0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Voice LiveでMCPサーバのツールを使う - function callingとの違いと並列ツール呼び出し

0
Last updated at Posted at 2026-11-10

Voice LiveでMCPサーバのツールを使ってみる話

音声会話のエージェントに、外部のツールで調べものをさせたいと思い、Voice Live APIで試してみました。

Voice Live APIは音声で会話するエージェントを作るためのAPIで、モデルにツールを使わせる手段として次の2つがあります。

  • function calling:クライアントがツールを定義し、モデルからの呼び出しを受けてクライアントが実行する
  • MCPサーバ:MCP(Model Context Protocol)のサーバをセッションに登録し、Voice Liveのサービスがツールを呼び出す

本記事では、MCPサーバのツールがfunction callingとどう違うのか、設定と実際に動かしたときに起きたことと対処について紹介します。あわせて、1つの質問に複数のツールを使う並列ツール呼び出しについても紹介します。Voice Live機能解説シリーズの第3回です。

シリーズの記事一覧(SDK 1.2.0 / API 2026-07-15)

  1. Voice Live SDK 1.2.0の変更点とAPI 2026-07-15 GA
    SDKに型が無い機能の分類
  2. Voice Live APIで会話の「間」を扱う
    先に話す・言いよどみを待つ・割り込む・待ち時間に話す
  3. Voice Live の音声入力の品質を上げる
    ノイズ抑制・エコー除去・Live-Reference AEC の選び方
  4. Voice LiveでMCPサーバのツールを使う(本記事)
    function callingとの違いと並列ツール呼び出し
  5. Voice LiveでFoundryのエージェントをツールとして呼ぶ
    chat-supervisorパターンの使い方と前段モデルの選び方
  6. 30秒の音声で「自分の声」を作って Microsoft Foundry エージェントを喋らせる
    Personal Voice の申請から実装まで
  7. Voice Live APIのアバターを使い分ける
    標準・カスタム写真・カスタムビデオの設定と実装
  8. Voice Liveのアバター映像をWebSocketで受ける
    WebRTCとの違いと映像の形式

番外編:Voice LiveのWebRTC音声接続に.NETのSIPSorceryでつなぐ
SDPを1行ずつ突き合わせた記録

検証に使ったコードはこちらです。

本記事は2026年10月時点の情報です。対象はVoice Live API 2026-07-15(GA)と.NET SDK Azure.AI.VoiceLive 1.2.0です。

準備

リソースの作成から、C#で最初の音声会話を動かすところまでは、公式のクイックスタートに沿って進めると準備できます。対応リージョンは、Voice Liveの概要ページで確認できます。

クイックスタートの環境で本記事の内容を試すときは、次の点に注意してください。

  • MCPを使う場合は、APIバージョンを2026-04-10以降にします
  • 並列ツール呼び出しの設定(parallel_tool_calls)は、APIバージョン2026-06-01-preview以降で使えます
  • .NET SDKはAzure.AI.VoiceLive 1.2.0を使います

Voice Liveのツールの呼ばれ方

function callingとMCPでは、ツールを実行する場所が違います。

function calling MCPサーバ
ツールの定義 クライアントがsession.updateで渡す クライアントがMCPサーバを登録し、ツールの一覧はサービスが取得する
ツールの実行 クライアント Voice Liveのサービス
クライアントに届くもの function_call(ツール名と引数) 実行の通知(response.mcp_call.completedなど)
ツールの結果 クライアントがfunction_call_outputで送る サービスがそのまま使う
結果を使った応答の要求 クライアントがresponse.createを送る クライアントがresponse.createを送る

最後の行は、どちらの方式でも同じです。ツールの結果を使って応答させるには、クライアントがresponse.createを送ります。Voice Liveは応答が重なることを拒否するので、この要求は1回だけ送る必要があります。後で紹介するMCPの課題1と並列ツール呼び出しは、どちらもこの規則の話です。

クライアントがツールを宣言する形(function calling、MCP)はModelのセッションの話です。Agentのセッションでは、ツールはエージェント(クラウド側)で管理・実行されます。

MCPサーバの登録

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

SDK 1.2.0では型で書けます。

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

MCPを使うにはAPIバージョン2026-04-10以降が必要です。

require_approvalの既定値はalwaysです。 省略すると、ツールを呼ぶたびにクライアントへmcp_approval_requestが届き、クライアントがmcp_approval_response(approve=true)を返すまでツールは実行されません。承認処理を書いていないと、そこで会話が止まります。自動で実行させるならneverを明示します。ツールごとに{"never": [...], "always": [...]}と分けることもできます。

MCPを動かしたときに起きた困ったこと

サンプルでは公開MCPサーバのdeepwiki(GitHubリポジトリのwikiを答えてくれる)を使い、microsoft/vscodeの構成を尋ねて、サーバ側でツールが実行されることを確認しました。その過程で、次のことが起きました。

課題1: ツールの実行後に応答が生成されない

MCPのツールはサーバで実行されますが、その結果を使って応答を作らせるresponse.createを送るのはクライアントの役目です。Voice Liveでは、MCPはツールの1つの形として扱われていて、function callingと同じく、ツールの結果がそろっても自動では応答を始めません(前述の表の最後の行)。Foundryのエージェントでも、MCPはfunctionやWeb検索などと並んで、ツールの1つとして設定します。OpenAIのRealtime APIも同じ仕様で、公式のガイドに「自動では続かない」と書かれています。

response.mcp_call.completedを受けたら、ツールを呼んだ応答(応答1)が閉じたのを確かめてから、クライアントが次の応答を要求します(公式how-toのサンプルも同じ作りになっています)。

MCPの呼び出しは、それを呼んだ応答がまだ開いている間に完了することがあります。応答は重ねられないので、サンプルでは応答が進行中かどうかを自分で持っておき、進行中ならresponse.doneを待ってから要求しています。

解決策: 応答の要求を1回だけ送る

実測ではresponse.done → response.mcp_call.completedの順で届くこともあり、順序は当てにできません。サンプルでは「要求すべき応答がある」ことを数えておき、どちらが後に来ても1回だけ要求するようにしています。

VoiceLiveAssistant.cs(抜粋)
case SessionUpdateResponseCreated:
    responseActive = true;
    break;

case SessionUpdateResponseDone:
    responseActive = false;
    await RequestToolFollowUpAsync();
    break;

case SessionUpdateResponseMcpCallCompleted:
    // ツールはサービスが実行した。応答を要求するのはクライアントの仕事
    Interlocked.Increment(ref pendingToolCalls);
    await RequestToolFollowUpAsync();
    break;

// ...

private async Task RequestToolFollowUpAsync()
{
    // 応答の重なりは拒否されるので、進行中なら response.done 側に任せる
    if (session == null || responseActive || Volatile.Read(ref toolsInFlight) > 0) return;

    // response.done とツール完了が同時に来ても、要求するのは1回だけ
    if (Interlocked.Exchange(ref pendingToolCalls, 0) == 0) return;

    await session.StartResponseAsync();
}

課題2: ツールが呼ばれずに自分の知識で答える

これはエージェントの世界ではよくある話かもしれません。MCPサーバを登録しても、モデルがツールを呼ばずに自分の知識で答えてしまうことがよくありました。見た目はMCPの設定が壊れているのと区別がつきにくいです。特に今回は動作確認も兼ねていたため使用したMCPがお題に対してよくなかったかもしれないです。

解決策: instructionsでツールに誘導する

サンプルでは、セッションのinstructionsで「リポジトリの質問には必ずツールを使う」と明示しています。

instructions(抜粋)
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.

ツールが実際に呼ばれたかは、response.output_item.doneに入るMCP呼び出しの項目で確かめられます。これが来ていなければ、モデルが自分で答えています。

課題3: ツールの結果が大きい

deepwikiにはwikiの全文を返すread_wiki_contentsというツールがあり、これが呼ばれると約620KBの結果が返ってきました。これがそのまま音声で答えるための入力になるので、音声で答える用途には向かないと思います。

解決策: 使うツールを絞る

サンプルではinstructionsで、短く答えるask_questionを使うように誘導しています。確実に絞るなら、サーバ定義のallowed_tools(SDKではAllowedTools)で使ってよいツールを限定します。

課題4: 作成時点の項目が空

MCPのツール一覧や呼び出しの項目は、conversation.item.createdの時点では中身が空です(ツール一覧、呼び出しの引数・結果とも)。中身が入るのはmcp_list_tools.completedやresponse.output_item.doneのあとです。

解決策: 中身が入ったあとのイベントで表示する

conversation.item.createdで表示すると「ツール0個」「結果なし」と誤った表示になるので、表示や記録はmcp_list_tools.completedやresponse.output_item.doneで行います。

並列ツール呼び出し

1つの質問に複数のツールが必要なとき(「東京の天気と時刻は?」)、モデルは1ターンの中で複数のツールを呼びます。これが並列ツール呼び出しです。function callingでは、1つの応答の中に複数のfunction_callが届きます。

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

parallel_tool_callsの既定値はtrueです。falseにすると、モデルに1つずつ順番にツールを呼ばせられます。SDK 1.2.0ではVoiceLiveSessionOptions.AllowParallelToolCallsで設定できます。

注意点は、すべてのツールの結果を送ってからresponse.createを1回だけ送ることです。結果を1つ送るたびにresponse.createを送ると、応答が二重に発火してエラーになります。

送り方 結果
結果ごとにresponse.create 二重に応答しようとしてエラー
全部の結果を送ってから1回 get_weather / get_timeが1回ずつ呼ばれ、エラーなし

MCPの課題1と同じく、「要求すべき応答の数」を数えておき、最後に1回だけ要求する形にすると、function callingとMCPの両方に使えます。なお、サンプルで並列に呼ばれることを確かめたのはfunctionツールの場合です。

まとめ

  • function callingはクライアントが、MCPはVoice Liveのサービスがツールを実行します
  • どちらの方式でも、ツールの結果を使った応答の要求はクライアントが送ります
  • 応答は重ねられないので、応答の要求は1回だけ送ります
  • require_approvalの既定値はalwaysなので、自動で実行させるならneverを指定します
  • モデルがツールを呼ばないときは、instructionsでツールに誘導します
  • 結果が大きいツールは、allowed_toolsで使わせないようにします
  • 並列ツール呼び出しでは、全部の結果を送ってからresponse.createを1回送ります

参考リンク

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?