概要
Azure FunctionsでカスタムMCPツールを作り、それが実際に外部から呼び出せるかを検証したい。そう思って動き始めたのですが、ローカル実行のための下準備(Azure Functions Core Tools)のインストールで何度も足止めを食らいました。
そこで発想を変えました。
「そもそもローカルで動かす必要、ある? Azureに直接デプロイして、リモートで動いているのを確認すればよくない?」
この記事は、その発想の転換にたどり着くまでの顛末と、実際にAzure上のMCPツールをVS CodeのGitHub Copilotから呼び出せるようになるまでの記録です。
前提
- Azure Portal上でリソースグループ・Azure Functionは作成済み
- リソースグループ:
{作成したリソースグループ名} - Azure Function:
{作成したfunction名}(Flex Consumption・Python・Application Insights同時作成) - ※実はここに至るまでにも一悶着あったので、詳しくは「秘話」で
- リソースグループ:
- Azureサブスクリプションは従量課金制(Pay-As-You-Go)
- 私の場合、無料期間のクレジットが残っている状態ですぐ登録したので、実質無料という認識で進めています(※※※ 間違っていたら教えてください ※※※)
- ローカル環境(Mac)
- OS:macOS Tahoe version 26.3.1
- Python 3.11.15
- Homebrew導入済み
- Azure CLI導入・
az login済み - uv:結局出番なし(後述)
- VS Code拡張機能
- Azure Functions(
ms-azuretools.vscode-azurefunctions) - Azure Developer CLI (azd)(
ms-azuretools.azure-dev) - Azurite(ローカルストレージエミュレーター。※これも結果的に未使用)
- GitHub Copilot Chat(Agentモードでの呼び出し確認に必須)
- Azure Functions(
目標
とりあえず、Azure Function上にあるカスタムMCPサーバーを呼び出したい!!!
課題
まずはデプロイする前に、ローカル構築して確認するぞ!!!
と思いきや・・・
Azure Functions拡張機能でMCPツールのプロジェクトを作成し、いざローカルでF5デバッグしようとしたところ、以下のエラーで止まり・・・
You must have the Azure Functions Core Tools installed to debug your local functions.
「じゃあCore Tools更新するか〜」と試みたのですが、ここから泥沼が始まりました(詳細は「躓いたこと」で)。
最終的にはディスクの空き容量不足という物理的な壁にぶつかり、ローカル実行の下準備を整えること自体を諦めることにしました・・・
そこで!!!
funcコマンドは、そもそもローカルで関数を実行・デバッグするためだけのツール。
そこで・・・
ローカル実行を経由せず、直接Azureにデプロイして、リモートで動作確認すればいい。
というわけで、Azure CLIでのzipデプロイ(az functionapp deployment source config-zip)に切り替えました。
ゴールイメージ
起動方法と成功画面
-
.vscode/mcp.jsonに登録したリモートサーバー(remote-mcp-server)の「起動」を押下 - GitHub Copilot ChatをAgentモードにして、
#remote-mcp-serverでツール呼び出しをチャットで指示 - 以下のように、Azure Functions上で実行された結果が返ってきた
リモートからの呼び出しに成功したぞ〜!!!!!!!
全体図
ローカルとAzureの間で、デプロイ・呼び出し・応答がどう流れているかをまとめると以下の通りです。
試したこと
成功例
ざっくり
- Azure Functions上にMCPサーバーを構築→VS CodeのGitHub Copilot Chatから実際に呼び出せることを検証しました。
- ローカルのデバッグ環境(Azure Functions Core Tools)は用意せず、VS Codeのテンプレート機能で生成したコードを最小限だけ編集し、Azure CLIで直接デプロイする方法を取っています。
- 結果として、Core Toolsなし・ローカル実行なしでも、Azure上のMCPツールが正しく呼び出せることを確認できました。
(できたときは本当に感動しました・・・)
詳細
下準備(テンプレを作成する)
まず、VS Codeのコマンドパレット(Cmd+Shift+P)で以下を実行します。
Azure Functions: Create New Project...
出てくるプロンプトには、以下のように答えました。
| プロンプト | 選択内容 |
|---|---|
| プロジェクトの種類 | Python |
| Pythonインタープリター | 使いたいものを選択 |
| 最初の関数のテンプレート | MCP Tool trigger |
| 関数名 | mcp_trigger |
| 認可レベル |
FUNCTION(リモート接続時にアクセスキーが必要になる設定) |
| プロジェクトの開き方 | 現在のウィンドウで開く |
これだけで、host.json・requirements.txt・function_app.pyなど、必要な材料一式が自動生成されます。ゼロから書く必要はありません。
実際に自分で編集したのは、生成されたコードのうちPythonの中身、しかもMCPツールに関する部分だけです。具体的には以下の状態にしました。
ダミーの挨拶を返すだけの、最小構成のMCPツールです。
import azure.functions as func
import logging
app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)
@app.mcp_tool_trigger(
arg_name="context",
type="mcpToolTrigger",
tool_name="hello_mcp",
description="リモートからの呼び出しに成功したぞ〜!!!!!!!を返したい",
toolProperties="[]",
)
def mcp_trigger(context) -> None:
return "リモートからの呼び出しに成功したぞ〜!!!!!!!"
hello worldだと、Copilotが雰囲気読んで返している可能性があったため、めちゃくちゃわかりやすいものにしています。
あと、呼び出された時に出てくるものって達成感あるものにしたいじゃないですか・・・(気持ちわかりません?)
Azure Portalへデプロイしよう!
これをAzureにデプロイして、動作確認するまでの流れです。
-
zip化する。
zip -r ../deploy.zip . -x "local.settings.json" -x ".venv/*" -x "__pycache__/*" -x ".vscode/*" -x "*.git*" -
Azure CLIでデプロイする。
az functionapp deployment source config-zip \ --resource-group {作成したリソースグループ名} \ --name {作成したfunction名} \ --src ../deploy.zip -
アクセスキー(
functions-key)を取得する。Azure Portalで対象のFunction App({作成したfunction名})を開き、左メニュー「関数」→「アプリ キー」→「システム キー」の中にあるmcp_extensionという名前のキーの値をコピーする。 -
.vscode/mcp.jsonにリモートのMCPエンドポイントを登録する。{ "servers": { "remote-mcp-server": { "type": "http", "url": "https://<Function Appの実際のホスト名>/runtime/webhooks/mcp", "headers": { "x-functions-key": "${input:functions-key}" } } }, "inputs": [ { "type": "promptString", "id": "functions-key", "description": "Functions App Key", "password": true } ] }サーバーを起動すると
functions-keyの入力欄が表示されるので、3で取得したmcp_extensionの値を貼り付ける。 -
GitHub Copilot ChatをAgentモードにして、
remote-mcp-serverのツールを呼び出す。#remote-mcp-server で挨拶して
Core Toolsなし・ローカル実行なしのまま、Azure上のMCPツールが呼び出せることを確認できました。
躓いたこと
Azure Functions Core Toolsインストール地獄
F5デバッグのために言われるがままCore Toolsを入れようとしたら、次々に別の壁が出てきました。
-
brew install azure-functions-core-tools@4を実行してもfunc --versionがcommand not found -
brew doctorで原因判明。タップが信頼されていない という警告が出ていたWarning: The following taps are not trusted: azure/functions→
brew trust azure/functionsで信頼させて解決 -
気を取り直して再度インストールすると、今度は
Error: Your Command Line Tools are too outdated.→ Command Line Toolsの更新が必要と判明
-
ところがPCの空き容量が足りず、更新できない
ここで「そもそもfuncはローカル実行専用では?」と気づき、ローカルデバッグを諦めてAzure CLIでの直接デプロイに切り替えた、というのが今回の一番の転換点でした。
Flexプラン特有のホスト名
デプロイ後、.vscode/mcp.jsonに
https://{作成したfunction名}.azurewebsites.net/runtime/webhooks/mcp
と決め打ちで書いたところ、以下のエラーで接続できませんでした。
Error sending message to https://{作成したfunction名}.azurewebsites.net/runtime/webhooks/mcp: TypeError: fetch failed: getaddrinfo ENOTFOUND {作成したfunction名}.azurewebsites.net
原因は単純で、Flex Consumptionプランのアプリは名前衝突を避けるため、実際のホスト名にユニークな名前が自動で付く仕様でした。
正式なホスト名を確認した手順は以下の通りです。
クエリを絞らずに、リソースの詳細をまるごと取得する。
az functionapp show --name {作成したfunction名} --resource-group {作成したリソースグループ名}
返ってきたJSONの中からhostname(defaultHostName)の項目を探す。すると、実際は
{作成したfunction名}-xxxxxxxxxxxxxxxx.eastus-01.azurewebsites.net
という、決め打ちしていた形とは違うユニークな名前でした。
見つかった正式なホスト名で.vscode/mcp.jsonのURLを書き換える。すでにAzure上にはデプロイ済みだったので、VS Code側でremote-mcp-serverを再起動し、GitHub Copilot Chatから呼び出したところ成功しました。
決め打ちせずに、必ずaz functionapp showで正式なホスト名を確認するのが安全そうです。
まとめ
- Azure FunctionsでMCPサーバーを検証したいだけなら、ローカル実行環境(Core Tools・F5デバッグ)を整える必要は必ずしもない
-
az functionapp deployment source config-zipを使えば、Flex Consumptionプランでもリモートビルド・zipデプロイでコードを反映できる - 動作確認は、VS Codeの
mcp.jsonにリモートのMCPエンドポイントを登録し、GitHub Copilot ChatのAgentモードから呼び出すだけで十分できる - Flex Consumptionのホスト名は
<名前>.azurewebsites.netという単純な形にならないことがあるので、az functionapp showで必ず確認する
「ローカル環境を完璧に整えてから動かす」のではなく「とりあえずクラウドに上げて動くか見る」という進め方の方が早いこともある、という学びでした。(←あまりあてにはならないと思うけど・・・)
秘話
実はここに至るまでにも一悶着ありました。当初、無料試用版のサブスクリプションでリソース作成を進めていたのですが、Function AppをFlex Consumptionプランで作ろうとすると、こう出ました。
無料試用版サブスクリプションは、Flex 従量課金 ではサポートされていません。別のサブスクリプションを選択してください。
「じゃあApp Serviceでいいか」と別プランを試したら、今度はこうです。
無料試用版サブスクリプションは、App Service ではサポートされていません。別のサブスクリプションを選択してください。
無料試用版は一部SKUがそもそも対象外という仕様だったため、結局サブスクリプションを従量課金制(Pay-As-You-Go)へアップグレードしました。なお公式ドキュメントによると、アップグレードしても元のサインアップから30日間は残っているクレジットがそのまま使え、アップグレード後12ヶ月間は無料サービス枠も継続するとのことなので、検証目的であれば実質無料の感覚で進められています。
追記・更新・発見情報
【Flexプラン特有のホスト名】の箇所について
Azure Portal > 概要 > 既定のドメイン にドメイン名が記載されていました。
そっちの方が早い・・・



