🤖

ゼロから作る『ローカル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 v4funcコマンド)
    • 最低バージョンの目安: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]

コードの詳細解説

1) app = func.FunctionApp()

Python(Functions v2モデル)で関数アプリのエントリを作ります。以降のデコレータ(@app.*)でトリガーやバインディングを宣言します。

2) tool_properties_reverse_json(= ツール引数スキーマ)

役割:MCPクライアント(例:Copilot)がツールの引数UIを自動生成できるように、呼び出し可能なプロパティの一覧をサーバー側で公開します。

形式:toolPropertyの配列をJSON文字列として渡します(Pythonではデコレータ引数が定数評価されるため、list[dict]ではなくJSON文字列で持たせるのが確実です)。

各フィールド

  • propertyName: 引数名(例:text)
  • propertyType: string/number/boolean/object/arrayなど
  • description: 入力フィールドの説明文
  • (必要に応じて)required, enum等の制約も使えます

3) @app.generic_trigger(..., type="mcpToolTrigger", ...)

この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としてサーバーに渡します。

4) context の中身(受信ペイロード)

典型的には次のような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で待受しているログを確認

MCPイメージ図
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(ローカル)

MCPイメージ図
Azurite実行後の画面

https://github.com/Azure/azure-functions-python-worker/issues/1714

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イメージ図
mcp.json実行画面

作成したMCPを実際に使ってみる!

  1. Copilot ChatをAgentモードで開く → ツールアイコンからreverse_textを選択

MCPイメージ図
図4: reverse_textがあることを確認

  1. 例)「reverse_textいろはにほへとちりぬるをを渡して」→ 返答:をるぬりちどへほにはろいが返れば成功
    (ツール引数UIが表示されます)

MCPイメージ図
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.jsonfunction_app.pyrequirements.txtlocal.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として運用します。


連載ナビ


参考文献

細部の仕様や推奨トランスポート(SSE→HTTP)の最新情報は、本文の公式ドキュメントへのリンクから随時キャッチアップしていただければと思います。

  • MCP公式仕様(2025-03-26版)/ GitHubリポジトリ:MCPの定義と用語、実装指針。
  • Azure Functions Core Tools(ローカル実行 / func startの基礎)
  • Azurite(Azure Storageのローカルエミュレーター)とUseDevelopmentStorage=trueの関係。
  • Azure Functions MCP拡張(Preview)host.jsonextensions.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