ゼロから作る『ローカルMCP』- Azure FunctionsでローカルMCPサーバーを起動する
本稿は、MCP入門・導入ガイド『エージェンティックWeb』と『MCP入門』- Azure Functionsでハンズオン開発への実践ガイドのハンズオン第1弾です。
📖 MCPハンズオンシリーズ
このシリーズでは、MCPの基本から実践まで段階的に学習できます:
記事 | 内容 | 形態 |
---|---|---|
🧩 入門ガイド | MCPの概要・エージェンティックWebの基本理解 | 導入編 |
📍 本記事 | ローカル MCPサーバーを構築 | ハンズオン① |
🌐 リモート編 | Azure Functionsによるリモート MCPサーバー構築 | ハンズオン② |
🛰 APIM前段化 | APIMによるMCPエンドポイントのAPI管理 | ハンズオン③ |
🛡️ V-Netで閉域化 | V-Net閉域化による完全プライベート化 | ハンズオン④ |
この記事で得られること
本記事では、文字列を反転する reverse_text
ツールを持つ最小構成のMCPサーバーを構築します。「いろはにほへと」を「とへほにはろい」に変換するシンプルなツールですが、MCPの基本概念から実装までハンズオン形式で学べます。
学べること:
- Azure FunctionsでローカルMCPサーバーを起動
- SSEトランスポートでVS Code(Copilot Agent)から接続
- 4ファイル構成で動く最小MCPツールの実装
MCPの標準とVS Code側の設定は公式ドキュメントに準拠しています。MCPの定義は公式サイト/リポジトリをご参照ください。
動作確認環境 & 事前インストール
- Python 3.10–3.12いずれか(本記事ではPython 3.11 で実行します)
- Node.js LTS(Azurite/一部ツールで使用)
-
Azure Functions Core Tools v4(
func
コマンド)- 最低バージョンの目安:4.0以上(MCP拡張の要件)
-
Azurite(ストレージエミュレーター):
UseDevelopmentStorage=true
の接続先。VS Code拡張機能またはdocker/npxで起動可。 - GitHub Copilot(Agentモード)
- 本記事の検証実行環境 Windows 11 (x64)
Windowsのインストール例
# Python(例: 3.11)
winget install -e --id Python.Python.3.11
# Azure Functions Core Tools v4(winget推奨)
winget install -e --id Microsoft.Azure.FunctionsCoreTools
# 代替: npm経由
npm install -g azure-functions-core-tools@4 --unsafe-perm true
フォルダ構成(最小4ファイル)
空ディレクトリを作成し、以下の4つのスクリプトをそれぞれ作成してください。
.
├─ host.json
├─ function_app.py
├─ requirements.txt
└─ local.settings.json # ローカル専用の設定
1) host.json
推奨(Previewバンドル)
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Preview",
"version": "[4.0.0, 5.0.0)"
},
"extensions": {
"mcp": {
"instructions": "Use the 'reverse_text' tool to reverse strings.",
"serverName": "ReverseMcpServer",
"serverVersion": "0.2.0",
"messageOptions": {
"useAbsoluteUriForEndpoint": false
}
}
}
}
代替(Experimentalバンドル:一部サンプルで採用)
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental",
"version": "[4.*, 5.0.0)"
},
"extensions": {
"mcp": {
"instructions": "Use the 'reverse_text' tool to reverse strings.",
"serverName": "ReverseMcpServer",
"serverVersion": "0.2.0"
}
}
}
extensions.mcp.*
の各プロパティ(instructions
/ serverName
/ serverVersion
/ messageOptions
)はLearnに記載の公式キー です。SSEのメッセージエンドポイントは相対URI推奨ではない旨の注意もあります。
2) function_app.py
最小ツール「reverse_text」をPython Isolated(Functions v4)で実装します。Azure Functions MCP拡張のMCP Tool Triggerをgeneric_trigger
で宣言し、ツールの引数スキーマ(UI表示のため)をJSONで渡します。
# function_app.py
import json
import azure.functions as func
app = func.FunctionApp()
# ツール引数スキーマ(Copilot等が入力UIを出すために必要)
tool_properties_reverse_json = json.dumps([
{
"propertyName": "text",
"propertyType": "string",
"description": "Reverse this text."
}
])
@app.generic_trigger(
arg_name="context",
type="mcpToolTrigger",
toolName="reverse_text",
description="Return the reversed string of the provided text.",
toolProperties=tool_properties_reverse_json
)
def reverse_text(context) -> str:
"""
MCPの argumentsから textを受け取り、反転して返す。
"""
try:
payload = json.loads(context) # {"arguments": {...}, ...}
args = payload.get("arguments", {})
text = args.get("text")
except Exception:
text = None
if not text:
return "No text provided."
if not isinstance(text, str):
text = str(text)
return text[::-1]
コードの詳細解説
app = func.FunctionApp()
1) Python(Functions v2モデル)で関数アプリのエントリを作ります。以降のデコレータ(@app.*
)でトリガーやバインディングを宣言します。
tool_properties_reverse_json
(= ツール引数スキーマ)
2) 役割:MCPクライアント(例:Copilot)がツールの引数UIを自動生成できるように、呼び出し可能なプロパティの一覧をサーバー側で公開します。
形式:toolPropertyの配列をJSON文字列として渡します(Pythonではデコレータ引数が定数評価されるため、list[dict]
ではなくJSON文字列で持たせるのが確実です)。
各フィールド
-
propertyName
: 引数名(例:text) -
propertyType
: string/number/boolean/object/arrayなど -
description
: 入力フィールドの説明文 - (必要に応じて)
required
,enum
等の制約も使えます
@app.generic_trigger(..., type="mcpToolTrigger", ...)
3) この1行が"FunctionsをMCPツールに変える"要です。generic_trigger
はカスタムトリガーを指定でき、type="mcpToolTrigger"
とすることでMCP用トリガーとして扱われます。主要引数は次の通りです:
-
arg_name="context"
: バインドされた受信ペイロードの引数名。関数側のパラメータ名と一致させます。JSON文字列で入り、json.loads(context)
で辞書化します。 -
type="mcpToolTrigger"
(必須): MCPツールトリガーを使う指定。 -
toolName="reverse_text"
(必須): クライアントから見えるツール名。発見(discovery) 時にこの名前で表示されます。 -
description="..."
: ツールの説明。クライアント側UIに表示されます。 -
toolProperties=tool_properties_reverse_json
: 前項の引数スキーマ。クライアントはこれを使って入力UIをレンダリングし、argumentsとしてサーバーに渡します。
context
の中身(受信ペイロード)
4) 典型的には次のようなJSONが届きます:
{
"toolName": "reverse_text",
"arguments": { "text": "abcdef" },
"toolCallId": "xxxxxxxx",
"invocationId": "yyyyyyyy"
}
-
arguments
: クライアントがUIで入力した値。ここから取り出して処理します。 - 返り値は文字列/JSON文字列でOK(まずは文字列が簡単)。必要に応じて構造化レスポンスも検討。
仕様・流れはMCP拡張のリファレンス・サンプルに沿っています。
5) もう1つツールを足す時
同じファイルにもう1つのデコレータ+関数を追加すればOK:
tool_properties_upper_json = json.dumps([
{"propertyName": "text", "propertyType": "string", "description": "Uppercase this text."}
])
@app.generic_trigger(
arg_name="context",
type="mcpToolTrigger",
toolName="to_upper",
description="Uppercase a given text.",
toolProperties=tool_properties_upper_json
)
def to_upper(context) -> str:
args = json.loads(context).get("arguments", {})
text = args.get("text", "")
return text.upper()
Copilot(Agent)側でツールが2つ表示されます。
3) requirements.txt
azure-functions>=1.20.0
ローカル実行(.venv)用
import azure.functions as func
を使うためにFunctionsのPython SDK(azure-functions
)をローカルの仮想環境へインストールします。通常はpython -m pip install -r requirements.txt
を実行し、func start
でその環境が読み込まれます。新規作成クイックスタートでもプロジェクト直下にrequirements.txt
が生成され、必要パッケージを列挙する前提です。
デプロイ時(Azure Functions)用
App Service/Functionsのビルド工程は、プロジェクト直下のrequirements.txt
を検出してpip install
を実行し、サーバー側に依存関係を展開します。requirements.txt
がルートに無いと 「Could not find setup.py or requirements.txt; Not running pip install」 というビルドエラーになります。
4) local.settings.json(ローカル専用)
Azuriteを利用するため、接続文字列はUseDevelopmentStorage=true
を指定します。
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python"
}
}
セットアップと起動
仮想環境を作成して依存を隔離するのが安全です。
本文中はWindowsの例を記載しています。Bashでの仮想環境作成は、末尾の「付録:コピペ用スニペット集」にまとめていますので、そちらを参照してください。
Windows(PowerShell / CMD)
py -m venv .venv
.\.venv\Scripts\activate.bat
python -m pip install -r requirements.txt
Azuriteを起動
VS Code拡張から:Azurite: Start
- Ctrl+Shift+P →「Azurite: Start」または「Azurite: Start All」
- 表示 → 出力 → ドロップダウンで「Azurite」を選び、10000/10001/10002で待受しているログを確認
Azuriteの拡張機能
AzuriteはAzure Storage Emulatorの後継で、Blob/Queue/Tableをローカルで提供します。
Functionsホストを起動
func --version # v4系か確認(4.0.7030+推奨)
func start
起動後、SSEエンドポイントは既定で:http://0.0.0.0:7071/runtime/webhooks/mcp/sse
(ローカル)
Azurite実行後の画面
VS Code(Copilot Agent)から接続して試す
.vscode/mcp.json
を作成
{
"servers": {
"local-mcp-function": {
"type": "sse",
"url": "http://0.0.0.0:7071/runtime/webhooks/mcp/sse"
}
}
}
VS Codeのワークスペースに.vscode/mcp.json
を置くと、Startボタンが出ます。HTTP/SSEのリモートサーバーとしてFunctionsを登録可能です。
mcp.json実行画面
作成したMCPを実際に使ってみる!
- Copilot ChatをAgentモードで開く → ツールアイコンから
reverse_text
を選択
図4: reverse_textがあることを確認
- 例)「
reverse_text
にいろはにほへとちりぬるを
を渡して」→ 返答:をるぬりちどへほにはろい
が返れば成功
(ツール引数UIが表示されます)
copilot agentでの実行画面
次回予告:リモートMCP(Functionsへデプロイ)
本稿のローカル最小MCP(SSE)を土台に、次回はAzureへデプロイして "Remote MCP" 化します。作成したソースコードをGitHubからAzure Functionへデプロイ。GitHub Actionでgit pushすれば変更をリモートにすぐに反映されます。システムキー(mcp_extension
)を使ったMCPクライアントとの接続方法などについてハンズオン形式でまとめていきます。
まとめ
本記事では、Azure Functionsを使って最小構成のMCPサーバーをローカル環境で起動し、VS CodeのCopilot Agentから接続する方法を学びました。
わずか4つのファイル(host.json
、function_app.py
、requirements.txt
、local.settings.json
)だけで、文字列を反転するreverse_text
ツールを持つMCPサーバーを構築できました。Azure FunctionsのMCP拡張(Preview)により、通常のPython関数がMCPツールとして自動的に公開され、VS CodeのAgentモードから呼び出すことが可能になりました。
SSE(Server-Sent Events)トランスポートを使用したこの構成は、MCPの基本概念を理解するための最初のステップとして最適です。ローカル環境での動作確認を通じて、MCPクライアント(Copilot)とサーバー(Functions)の通信フローや、ツール発見・実行の一連の流れを体験できたと思います。
次回は、今回開発したローカルMCPサーバーをAzure Functionsにデプロイし、リモートMCPとして運用します。
連載ナビ
- Part 1:『エージェンティックWeb』と『MCP入門』
- Part 2: ゼロから作る『ローカルMCP』
- Part 3: ゼロから作る『リモートMCP』
- Part 4:ゼロから作る『APIM構築』
- part 5: ゼロから作る『V-Net閉域化』
参考文献
細部の仕様や推奨トランスポート(SSE→HTTP)の最新情報は、本文の公式ドキュメントへのリンクから随時キャッチアップしていただければと思います。
- MCP公式仕様(2025-03-26版)/ GitHubリポジトリ:MCPの定義と用語、実装指針。
-
Azure Functions Core Tools(ローカル実行 /
func start
の基礎) -
Azurite(Azure Storageのローカルエミュレーター)と
UseDevelopmentStorage=true
の関係。 -
Azure Functions MCP拡張(Preview):
host.json
のextensions.mcp
、SSE/HTTPのエンドポイント、SSE非推奨の注意等。
付録:コピペ用スニペット集
PowerShell(Windows)環境構築
winget install -e --id Python.Python.3.11
winget install -e --id Microsoft.Azure.FunctionsCoreTools
Bash (Python仮想環境)
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
仮想環境 & 依存インストール
# Windows
py -m venv .venv && .\.venv\Scripts\activate && python -m pip install -r requirements.txt
# macOS/Linux
python3 -m venv .venv && source .venv/bin/activate && python -m pip install -r requirements.txt
Discussion