Remote MCP server を Azure Functions でホストできますが、Streamble HTTP のサポートが始まりましたのでその実装方法を共有です。
ローカル環境のセットアップ
前提として、Azure Functions で Streamable HTTP は、Microsoft.Azure.Functions.Extensions.Mcp: 1.0.0-preview.7
で利用可能となったので、この子が含まれた Extensions Bundles が必要になります。
Microsoft.Azure.Functions.Extensions.Mcp: 1.0.0-preview.7
が含まれた Extensions Bundles は以下のバージョンで初登場しました。
あとは旧称 Azure Functions Core Tools (v4.1.0 より Azure Functions CLI に名称変更) もいくつかのバージョンからしか使えないはずなのでとりあえず最新に更新しておきましょう。
インストールまたは更新方法は以下の公式ドキュメントを参考に。
Function App で MCP の Streamable HTTP の利用
Function App のプロジェクト作成
はじめての方向けに VS Code で Function App の作成方法を...ということで、以下のドキュメントを参考に "関数をローカルで実行する" の中までを実行すると、はじめての Functions App が作れます(雑)。
Streamable HTTP の MCP server を実装
実装というほど大したことしないのですが、ここからが MCP server の実装です。
host.json の編集
host.json を開き、extensionBundle セクションを以下のように書き換えます。2025-09-11 の時点だと、Microsoft.Azure.Functions.Extensions.Mcp: 1.0.0-preview.7
以上が含まれているバージョンは、Extensions Bundles Experimental Release 4.5.0
となります。
余談ですが Release の名称は Extensions Bundles なのに、ここのセクションの名称は ExtensionBundle
って...s の使い方統一してほしいですね...
これは Experimental バージョンなので、extensionBundle の id を以下のように指定します。
"extensionBundle": { "id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental", "version": "[4.0.0, 5.0.0)" }
(今後 ExtensionBundle の新しいバージョンが出たら、Microsoft.Azure.Functions.ExtensionBundle.Preview
や Microsoft.Azure.Functions.ExtensionBundle
になるかもなので細かいことを気にする場合は以下のリリースをチェックするとよいです。
Function App の MCP trigger を追加
あとは、Streamable HTTP を気にすることなく MCP trigger を実装すれば OK です。
私の場合はこのドキュメントのサンプルがきらいなのでとりあえずこんな感じに実装。現在時刻を返すシンプルな MCP です。(注: フォーマットは雑に書いてますのでそこは見ないでください💦)
import json import datetime import azure.functions as func import logging class ToolProperty: def __init__(self, property_name: str, property_type: str, description: str): self.propertyName = property_name self.propertyType = property_type self.description = description def __str__(self) -> str: return json.dumps(vars(self), ensure_ascii=False) @staticmethod def props_to_json(*properties: 'ToolProperty') -> str: return json.dumps([vars(prop) for prop in properties], ensure_ascii=False) app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION) _prop_tz = ToolProperty( property_name="timezone", property_type="string", description='タイムゾーンを指定します。デフォルトはUTCです。指定可能な値はIANA Time Zone Database(例: "Europe/London", "America/New_York", "Asia/Tokyo" など)です' ) _prop_format = ToolProperty( property_name="format", property_type="string", description='日付のフォーマットを指定します。デフォルトは "iso" です。利用可能なフォーマットは "iso", "rfc2822", "simple" です' ) _formats = { "iso": "%Y-%m-%dT%H:%M:%S%z", # 2025-06-10T00:54:01+0900 "rfc2822": "%a, %d %b %Y %H:%M:%S %z", # Mon, 09 Jun 2025 15:00:00 +0000 "simple": "%Y/%m/%d %H:%M:%S", # 2025/06/09 15:00:00 } @app.generic_trigger( arg_name="context", type="mcpToolTrigger", toolName="get_current_datetime", description="タイムゾーンとフォーマットを指定して現在の時刻を取得できます", toolProperties=ToolProperty.props_to_json(_prop_tz, _prop_format), ) def get_current_datetime(context) -> str: logging.info(f">>>>> MCP CALLED: get_current_datetime: {context}") json_context = json.loads(context) arguments = json_context.get("arguments") fmt = arguments.get("format", "iso") tz = arguments.get("timezone", "UTC") fmt_pattern = _formats.get(fmt, _formats["iso"]) try: import zoneinfo tz_info = zoneinfo.ZoneInfo(tz) except Exception: tz_info = datetime.timezone.utc tz = "UTC" now_str = datetime.datetime.now(tz_info).strftime(fmt_pattern) return f"{now_str} ({tz})"
デバッグ実行
これでデバッグ実行をすると、以前は SSE の endpoint しか表示されなかったのが、無事に Streamable HTTP のエンドポイント (以下図赤枠)も表示されるようになりました。
表示されない場合は、Extension Bundle の指定がおかしいとかが最も可能性の高いと思います。
Function App はデバッグ実行したままでコールできるか試します。
MCP client 実装するほどでもないので VS Code の Copilot Chat から call してみましょう。
mcp.json は、Function App のデバッグ実行した URL に合わせてこんな感じにセットアップします。
Streamable HTTP の MCP server の Function App をコールすることができます。
まとめ
まだ Preview 前の Experimental 段階ですが、Function App も Streamable HTTP に対応できたことが確認できたので、SSE の仕様上 Function App と相性がわるかった問題も解消されましたねー。
余談ですが、Gemini の画像生成、HTTP の文字が HTP になってるよ(と指摘はしてないけど指摘したらきっと直してくれるはず