🛰

ゼロから作る『APIM構築』- Remote MCPをAPIMで公開する 実戦ハンズオン

に公開

本稿は、MCPハンズオンシリーズの第3弾です。前回のAzure Functionsでリモート MCPサーバー構築の続編として、Azure Functions製 MCPサーバーの前段にAzure API Management(APIM) を配置し、API管理機能を提供します。

📖 MCPハンズオンシリーズ

このシリーズでは、MCPの基本から実践まで段階的に学習できます:

記事 内容 形態
🧩 入門ガイド MCPの概要・エージェンティックWebの基本理解 導入編
🤖 ローカル編 Azure Functionsでローカル MCPサーバーを構築 ハンズオン①
🌐 リモート編 Azure Functionsによるリモート MCPサーバー構築 ハンズオン②
📍 本記事 APIMによる MCPエンドポイントの API管理 ハンズオン③
🚀 次回 V-Net閉域化 ハンズオン④

この記事で得られること

前回のハンズオン②では、Azure Functionsを使用してリモートMCPサーバーを構築し、VS Code Copilotから直接Functionsエンドポイントに接続する方法を学びました。しかし、本格的な運用を考えると、Functionsのシステムキーをクライアントに配布する必要があり、セキュリティ面やAPI管理の観点で課題がありました。

本記事では、前回構築したAzure Functions MCPサーバーの前段にAzure API Management(APIM) を配置することで、これらの課題を解決します。APIMを介すことで、クライアントはAPIMのURLのみを知っていればよく、FunctionsのシステムキーはAPIMが自動で付与するため、セキュリティが大幅に向上します。また、APIの監査ログ、レート制限、Analyticsといったエンタープライズグレードの機能も利用できるようになります。

具体的には、APIMインスタンスの作成から始まり、Named Valuesを使用したセキュアなキー管理、SSE(Server-Sent Events)とPOSTメッセージに対応した2つのオペレーション作成、そしてリアルタイムストリーミングを実現するポリシー設定まで、段階的に構築していきます。最終的にVS Code Copilot AgentからAPIM経由でMCPサーバーに接続し、前回と同様の機能をより安全で管理しやすい形で実現します。

前提

  • 前回記事のAzure Functions MCPサーバーが稼働済み
  • FunctionsのSystem Keymcp_extension)とホスト名を控えていること
  • Azureサブスクリプション・APIMインスタンス作成権限

0. 仕上がりイメージ

デプロイ後の構成は以下の通りです

主要なメリット:

  • クライアントはAPIMのURLのみを使って接続(Functionsのシステムキー不要)
  • SSEストリーミングの最適化buffer-response="false"
  • API管理機能(サブスクリプション・レート制限・監査)

1. 事前準備

前回作成したAzure Functionsから以下の情報を控えてください

https://portal.azure.com/signin/index

  1. ホスト名https://<func-host>.azurewebsites.net
  2. システムキー
    • Functionsリソース → App keys → System keys → mcp_extensionの値

Functions System Key

2. APIMインスタンスの作成

API Management を新規作成します

  1. Azure Portal → API Management+ 作成

FunctionsSystemKey

  1. APIM設定項目

基本設定:

  • リソースグループ:Functionsを作成したリソースグループ
  • リージョン:Functionsと同じリージョン推奨(例:Southeast Asia)
  • リソース名mcp-apim-<任意>(グローバル一意)
  • 組織名:任意の表示名(開発者ポータルのフッターや通知メール差出人名に使用。後から変更可)
  • 管理者のメールアドレス:実際に受信できるメールアドレス(サブスクリプション申請・招待通知が届く。後から変更可)
  • 価格レベルStandard v2 (次回のV-Net閉域化でOutbound V-Net Integration機能が必要。ConsumptionはSSE非推奨のため避ける)
  • ユニット1 (後でスケールアウト可能、課金はユニット数比例)
  1. その他の設定は規定で進め、確認と作成画面でリソース作成を行います。APIMインスタンスの作成には 数分かかります。完了まで待機してください。

作成完了後、Gateway URLhttps://<apim-name>.azure-api.net になっていることを確認してください。

3. FunctionsシステムキーのAPIM保存

クライアントにFunctionsのシステムキーを配らないよう、APIMの Named Values に保存します。

Named Valuesの設定

APIMリソース → Named values+ Add

Named Values設定

設定項目:

  • 名前mcp-functions-key
  • 表示名MCP Functions Key
  • 種類Secret
  • :Functionsの mcp_extension システムキーの値を記入してください

この設定により、APIMポリシーで {{mcp-functions-key}} として参照できます。

4. APIの追加(HTTPパススルー)

Functionsへのプロキシ用APIを作成します。

4-1.APIの作成

APIM → APIs+ Add APIHTTP を選択

API作成

設定項目:

  • Display name mcp-sse
  • Name mcp-sse(自動入力)
  • Web service URLhttps://<func-host>.azurewebsites.net
  • API URL suffix:(省略可)

4-2. Subscriptionの確認

作成後、設定項目でSubscription requiredをオフにします。
APIM → APIsmcp-sseSettings を選択

API作成

  • Subscription requiredオフ(検証を簡単にするため)

5. オペレーションの追加

受信のためのパイプ(サーバ→クライアント)である SSE GET と送信のためのパイプ(クライアント→サーバ)である Message POST に対応する 2つのオペレーションを作成します。

5-1. SSE (GET)

+ Add operation をクリックし、以下を設定:

API作成

  • Display nameSSE GET
  • Namesse-get
  • URLGET /runtime/webhooks/mcp/sse

5-2. Message(POST)

もう一度 + Add operation をクリック:

API作成

  • Display nameSSE POST
  • Namesse-post
  • URLPOST /runtime/webhooks/mcp/message

5-3. それぞれの役割詳細

1) GET /runtime/webhooks/mcp/sse(SSEストリーム)

  • 役割:SSEストリームを張るための長寿命コネクション。サーバからのイベント(ツールの進捗、最終結果など)をプッシュ受信
  • ヘッダーAccept: text/event-stream(APIM 経由でも必要)
  • 動作:旧来の HTTP+SSE 仕様では、接続直後にサーバが endpoint イベントを送り、以後のクライアント送信に使うPOST宛先を案内する、という流れでした。Azure FunctionsのMCP拡張でも、この “メッセージ用エンドポイント” の扱いを設定で制御します。

2) POST /runtime/webhooks/mcp/message(メッセージ送信)

  • 役割:クライアント→サーバのJSON-RPCメッセージ送信
  • 送信内容initialize / tools/list / tools/call / cancel など
  • レスポンス:実行結果やログは基本的にGET側のSSEストリームから流れる

5-4. 仕様の補足(Streamable HTTPへの移行)

最新のMCP仕様(2025-06-18)では Streamable HTTP が主流となっています

  • 単一エンドポイント(例:/mcp)でPOSTとGETを両方サポート
  • サーバが POSTのレスポンス自体をSSEでストリーミング可能
  • HTTP+SSEは後方互換のために残存、新規実装はStreamable HTTP推奨

Azure Functionsでも /runtime/webhooks/mcp/sse(HTTP+SSE)と /runtime/webhooks/mcp(Streamable HTTP)の両方がサポートされています。

6. ポリシーの設定

APIMのポリシーで以下を実現します:

  1. x-functions-keyの自動付与(Named Values参照)
  2. SSEストリーミングの最適化(バッファリング無効)
  3. SSE 専用ヘッダーの補完

6-1. APIレベルポリシーの編集

作成したAPI→ All operationsPolicies</>Code をクリック

6-2. ポリシーXMLの設定

以下のポリシーを貼り付けてください:

<!-- API level policy -->
<policies>
    <inbound>
        <base />
        <!-- Functions 認証(mcp_extensionの system keyを Named valueから) -->
        <set-header name="x-functions-key" exists-action="override">
            <value>{{mcp-functions-key}}</value>
        </set-header>
        <!-- ★ GETのときだけ SSE 用ヘッダーを付与 -->
        <choose>
            <when condition="@(context.Request.Method == "GET")">
                <set-header name="Accept" exists-action="override">
                    <value>text/event-stream</value>
                </set-header>
                <set-header name="Cache-Control" exists-action="override">
                    <value>no-cache</value>
                </set-header>
            </when>
        </choose>
    </inbound>
    <backend>
        <!-- ★ SSEはバッファ無効が必須 -->
        <forward-request buffer-response="false" timeout="300" />
    </backend>
    <outbound>
        <base />
        <!-- ★ GET 応答だけ event-streamにする -->
        <choose>
            <when condition="@(context.Request.Method == "GET")">
                <set-header name="Content-Type" exists-action="override">
                    <value>text/event-stream</value>
                </set-header>
            </when>
        </choose>
    </outbound>
    <on-error>
        <base />
    </on-error>
</policies>

6-3. ポリシーの解説

inboundセクション:

  • x-functions-key ヘッダーをNamed Valuesから自動付与
  • SSE接続時に必要な Accept: text/event-streamCache-Control: no-cache を補完

backendセクション:

  • buffer-response="false" によりSSEストリームをリアルタイム配信
  • バッファリングを無効化することで遅延を最小化

7. Productに関連付けて公開

新規Product(製品) を作成し、6で作成したAPIを追加。

APIM → Products+ Add

API作成

  • 表示名Published
  • IDpublished
  • 説明:MCP-Function-APIM
  • 発行済み:どちらでも可。ゲートウェイ経由の呼び出しは公開/非公開に関わらず可能ですが、開発者ポータルで見せたいなら Publishedを ONにします。
  • サブスクリプションを要求する:OFF(不要)
  • 承認が必要:OFF(不要)
  • API : プルダウンから mcp-sse を追加(Product側 / API側どちらから追加しても機能的に同じです)

7-2. APIの関連付けの確認

作成したProductに先ほどのAPIが関連ずれられているかを確認します。

APIM → APIsmcp-sseSettings を選択。
Productsの箇所がPublishedになっていることを確認

API作成

8. 動作確認

8-1. MCP 設定ファイルの更新

VS Codeの .vscode/mcp.json を以下のように更新:

{
  "servers": {
    "remote-mcp-via-apim": {
      "type": "sse",
      "url": "https://<apim-name>.azure-api.net/mcp/runtime/webhooks/mcp/sse"
    }
  }
}

設定値

  • <apim-name>:APIMの既定ドメイン。APIMリソース概要のゲートウェイの URLに記載されています。

VS Code接続確認

8-2. 動作確認

Copilot Agentチャット画面

reverse_text を呼び、text: "じゅげむじゅげむ" を渡す → "むじゅげむじゅげ" が返れば成功!

VS Code接続確認

🚀 次回予告

次回は V-Net Integrationプライベートエンドポイント を活用した閉域化設計を扱います:

  • APIM v2ゲートウェイによるプライベート接続
  • Functionsの V-Net 統合
  • エンドツーエンドの閉域化設計

まとめ

本記事では、前回構築したAzure Functions製MCPサーバーの前段に Azure API Management(APIM) を配置し、API管理機能を実現する方法を学びました。

APIMインスタンスを作成し、Functionsのシステムキー(mcp_extension)を Named Valuesに保存することで、クライアントには秘匿しながらAPIMが自動で認証ヘッダーを付与する仕組みを構築しました。HTTP+SSE対応としてGET(/runtime/webhooks/mcp/sse)とPOST(/runtime/webhooks/mcp/message)の2つのオペレーションを作成し、ポリシー設定により buffer-response="false" でのリアルタイムストリーミングと、SSE専用ヘッダーの自動補完を実現しました。

このAPIM前段化により、単純なFunctions直接接続からAPI管理層を持つアーキテクチャへと進化しました。クライアントはAPIMのURLのみ知っていればよく、セキュリティ強化・監査ログ・レート制限・Analyticsによる可観測性といった、本格運用に必要な機能を一元的に管理できるようになりました。MCP固有の要件であるSSEストリーミングを損なうことなく、API管理の標準機能を活用した実用的なソリューションを構築できました。

次回は、V-Net Integrationとプライベートエンドポイントを活用した閉域化設計により、さらなるセキュリティ強化とエンタープライズ運用への対応を図ります。


連載ナビ

参考文献

Discussion