ゼロから作る『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 Key(
mcp_extension
)とホスト名を控えていること - Azureサブスクリプション・APIMインスタンス作成権限
0. 仕上がりイメージ
デプロイ後の構成は以下の通りです
主要なメリット:
- クライアントはAPIMのURLのみを使って接続(Functionsのシステムキー不要)
-
SSEストリーミングの最適化(
buffer-response="false"
) - API管理機能(サブスクリプション・レート制限・監査)
1. 事前準備
前回作成したAzure Functionsから以下の情報を控えてください
-
ホスト名:
https://<func-host>.azurewebsites.net
-
システムキー:
- Functionsリソース → App keys → System keys →
mcp_extension
の値
- Functionsリソース → App keys → System keys →
2. APIMインスタンスの作成
API Management を新規作成します
- Azure Portal → API Management → + 作成
- APIM設定項目
基本設定:
- リソースグループ:Functionsを作成したリソースグループ
- リージョン:Functionsと同じリージョン推奨(例:Southeast Asia)
-
リソース名:
mcp-apim-<任意>
(グローバル一意) - 組織名:任意の表示名(開発者ポータルのフッターや通知メール差出人名に使用。後から変更可)
- 管理者のメールアドレス:実際に受信できるメールアドレス(サブスクリプション申請・招待通知が届く。後から変更可)
- 価格レベル:Standard v2 (次回のV-Net閉域化でOutbound V-Net Integration機能が必要。ConsumptionはSSE非推奨のため避ける)
- ユニット:1 (後でスケールアウト可能、課金はユニット数比例)
- その他の設定は規定で進め、
確認と作成画面
でリソース作成を行います。APIMインスタンスの作成には 数分かかります。完了まで待機してください。
作成完了後、Gateway URL が https://<apim-name>.azure-api.net
になっていることを確認してください。
3. FunctionsシステムキーのAPIM保存
クライアントにFunctionsのシステムキーを配らないよう、APIMの Named Values に保存します。
Named Valuesの設定
APIMリソース → Named values → + Add
設定項目:
-
名前:
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 API → HTTP を選択
設定項目:
-
Display name:
mcp-sse
-
Name:
mcp-sse
(自動入力) -
Web service URL:
https://<func-host>.azurewebsites.net
- API URL suffix:(省略可)
4-2. Subscriptionの確認
作成後、設定項目でSubscription requiredをオフにします。
APIM → APIs → mcp-sse → Settings を選択
- Subscription required:オフ(検証を簡単にするため)
5. オペレーションの追加
受信のためのパイプ(サーバ→クライアント)である SSE GET と送信のためのパイプ(クライアント→サーバ)である Message POST に対応する 2つのオペレーションを作成します。
5-1. SSE (GET)
+ Add operation をクリックし、以下を設定:
-
Display name:
SSE GET
-
Name:
sse-get
-
URL:GET
/runtime/webhooks/mcp/sse
5-2. Message(POST)
もう一度 + Add operation をクリック:
-
Display name:
SSE POST
-
Name:
sse-post
-
URL:POST
/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のポリシーで以下を実現します:
- x-functions-keyの自動付与(Named Values参照)
- SSEストリーミングの最適化(バッファリング無効)
- SSE 専用ヘッダーの補完
6-1. APIレベルポリシーの編集
作成したAPI→ All operations → Policies → </>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-stream
とCache-Control: no-cache
を補完
backendセクション:
-
buffer-response="false"
によりSSEストリームをリアルタイム配信 - バッファリングを無効化することで遅延を最小化
7. Productに関連付けて公開
新規Product(製品) を作成し、6で作成したAPIを追加。
APIM → Products → + Add
-
表示名:
Published
-
ID:
published
- 説明:MCP-Function-APIM
- 発行済み:どちらでも可。ゲートウェイ経由の呼び出しは公開/非公開に関わらず可能ですが、開発者ポータルで見せたいなら Publishedを ONにします。
- サブスクリプションを要求する:OFF(不要)
- 承認が必要:OFF(不要)
-
API : プルダウンから
mcp-sse
を追加(Product側 / API側どちらから追加しても機能的に同じです)
7-2. APIの関連付けの確認
作成したProductに先ほどのAPIが関連ずれられているかを確認します。
APIM → APIs → mcp-sse → Settings を選択。
Productsの箇所がPublished
になっていることを確認
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
に記載されています。
8-2. 動作確認
Copilot Agentチャット画面
reverse_text
を呼び、text: "じゅげむじゅげむ"
を渡す → "むじゅげむじゅげ"
が返れば成功!
🚀 次回予告
次回は 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とプライベートエンドポイントを活用した閉域化設計により、さらなるセキュリティ強化とエンタープライズ運用への対応を図ります。
連載ナビ
- Part 1:『エージェンティックWeb』と『MCP入門』
- Part 2: ゼロから作る『ローカルMCP』
- Part 3: ゼロから作る『リモートMCP』
- Part 4:ゼロから作る『APIM構築』
- part 5: ゼロから作る『V-Net閉域化』
Discussion