👋

[Python版] Azure FunctionsでMCPサーバーを動かす

に公開

Azure FunctionsがプレビューでMCPサーバーに対応したので、リファレンス実装を動作させてみました。

本記事の概要

  • Azure-Samples/remote-mcp-functions-python をローカルとリモートで動作させる
  • まずはローカルでMCPサーバーを動作させてGithub Copilot Agentから接続して動作確認
  • その後Azureにデプロイしリモート接続確認

基本的には上記リポジトリのREADMEに則っていますが、部分的に補足や環境に合わせて手順変更している箇所があります。

ローカルでの動作確認

起動

公式のリファレンス実装を動かして、MCPサーバーをFunctionsで動作させるための方法を確認します。
Azure-Samples/remote-mcp-functions-python
このリポジトリをCloneします。

まずはローカルで実行していきます。まずはストレージエミュレーターを起動します。

docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite
Azurite Blob service is starting at http://0.0.0.0:10000
Azurite Blob service is successfully listening at http://0.0.0.0:10000
Azurite Queue service is starting at http://0.0.0.0:10001
Azurite Queue service is successfully listening at http://0.0.0.0:10001
Azurite Table service is starting at http://0.0.0.0:10002
Azurite Table service is successfully listening at http://0.0.0.0:10002

AzuriteによってAzure Storageエミュレーターがローカルで動作し始めます。
上記を実行した状態で、

cd src
pip install -r requirements.txt
func start

でFunctionsのローカル実行が開始され、ローカルサーバーでMCPサーバーが立ち上がります

エージェントからの接続確認

これを維持した状態でエージェントからの接続を行います。ここではGithub Copilot Chat(Agent)
を使っていきますが、正しく設定することClaude Desktopや他のエージェントでも利用できるはずです。

Github Copilot Chat(Agent Mode)を立ち上げます。

「Add MCP Server」を選択します。

すると、以下のようなMCP種類の選択が問われるので、ここではHTTP (server-sent events)を選択します。

テキストボックスに以下を入力します。

http://0.0.0.0:7071/runtime/webhooks/mcp/sse

次にこのMCPサーバーの識別名を聞かれるため(何でもよいですが)my-mcp-server-reference-local としておきます。

最後にMCPの利用範囲を聞かれるため、ひとまずUser Settingsとしておきます。
これでMCPサーバーの追加が完了します。

このリファレンス実装のMCPサーバーからは

Tool 内容
hello_mcp "Hello I am MCPTool!" と返信する
save_snippet スニペットに名前を付けて保存する
get_snippet 保存したスニペットを取得する

の3種類がツールとして与えられています。

hello_mcpは"Hello I am MCPTool!"という文字列を返却するだけのツールです。
save_snippetは指定した文字列を指定した名前のスニペットとして保存するツールで、get_snippetは保存したスニペットを読み出すツールです。

エージェントにsnippetを保存したり読み出す指示を与えると指定文字列やファイルをsnippetとして保存できます。

コード構成の分析

ここで実行しているコードの作りを確認してみます。
Azure FunctionsでPython製MCPサーバーを作成するための要点は以下となります。

  1. Pythonプログラミングモデルv2を利用
  2. MCPのトリガーには@app.generic_triggerでtype="mcpToolTrigger"を指定
  3. @app.generic_triggerにはtoolName(String)、description(String)、toolProperties(JSON)を指定
  4. また、引数がある場合にはarg_name="context"を指定し、関数内でcontextからtoolPropertiesで指定した引数を取り出す処理を行う

また、MCPサーバー特有の設定ではありませんが、リファレンスコードでは@app.generic_input_binding / @app.generic_output_bindingを用いてAzure Storageへの読み書きを行っています。

@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="get_snippet",
    description="Retrieve a snippet by name.",
    toolProperties=tool_properties_get_snippets_json,
)
@app.generic_input_binding(arg_name="file", type="blob", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def get_snippet(file: func.InputStream, context) -> str:
    """
    Retrieves a snippet by name from Azure Blob Storage.

    Args:
        file (func.InputStream): The input binding to read the snippet from Azure Blob Storage.
        context: The trigger context containing the input arguments.

    Returns:
        str: The content of the snippet or an error message.
    """
    snippet_content = file.read().decode("utf-8")
    logging.info(f"Retrieved snippet: {snippet_content}")
    return snippet_content

Azureにデプロイして動作を確認する

Azureへのデプロイ

このリファレンスリポジトリはAzure Developer CLIのフォーマットに則って作られています。infra配下にAzureのIaCコードが含まれており、azd upコマンド一つでクラウドの構築からアプリのデプロイまで完了するようになっています。

Azure Developer CLIがインストールされていない場合には以下のページに則ってインストールを行ってください。
https://learn.microsoft.com/ja-jp/azure/developer/azure-developer-cli/overview?tabs=windows

まずはazdでログインを行います。

azd auth login

(tenantの指定が必要な場合には以下のコマンドを使ってください。)

azd auth login --tenant-id=xxxx.xxx.xxx

(また、azdで構築する設定対象のAzureサブスクリプションは ~/.azd/config.json で指定できます)

デプロイに必要な環境変数を設定します。

azd env set VNET_ENABLED true

VNET_ENABLEDをtrueに指定すると、Azure FunctionsがVNet統合されます。

デプロイを実行します。

azd up

? Enter a new environment name: [? for help]

と聞かれるので、環境名を適宜つけます。ここではremote-mcp-functions-pythonと指定します。
この名前にrg-を付けた名前でリソースグループが作成されます。

このIaCによって、以下のような構成が構築されます。

  • (VNET_ENABLED=trueの場合) VNetを作成し、Azure Functions(Flex Consumptionプラン)を同VNet内のサブネットにデプロイする
  • ストレージアカウントが作成され、VNetにプライベートエンドポイント経由でアクセス可能に
  • Azure FunctionsにはUserAssigned ManagedIDがアサインされ、ストレージアカウントにアクセスするためのロールが付与される
  • Azure MonitorとApp Insightsが設定される
  • Azure Functions上にMCPサーバーがデプロイされる

AzureにデプロイされたFunctionsへのMCP Inspectorによるアクセス

まずは動作確認のためにMCP Inspectorで接続を試みます。

npx @modelcontextprotocol/inspector

でmcp inspectorを立ち上げて、ブラウザから

http://localhost:6274/#resources

にアクセスします。

Transport Typeを"SSE"とし、他は以下のように指定します。

  • URL: https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse?code=<your-mcp-extension-system-key>
  • funcappname: 関数アプリの名前
  • your-mcp-extension-system-key: FunctionsのFunctions > App keys > mcp_extensionから取得できます

Connectを押すと、以下のように接続が確立します。

List Toolsを押すと利用できるツールの一覧が返却されます。

これでローカルからAzure上のFunctionsにデプロイされたMCPサーバーにアクセスできることが確認できました。

Github Copilotからの接続

次はGithub Copilot Agentから接続する設定を行います。

リファレンスコードの.vscode/mcp.jsonというファイルに"remote-mcp-function"というMCPサーバーが定義サンプルが掲載されています。

VSCodeとエージェントが同じOSで実行されている場合

このファイルを開き、remote-mcp-functionの上部に表示されるstartを押下すると、関数アプリ名と関数キーがプロンプトで聞かれるので入力します。

VSCodeとエージェントが別OSで実行されている場合

WSL + Windows VSCodeで動いている場合にはGithub Copilot Agentの設定ファイルに直接リモートMCPの定義を追記する必要があるようです。

Github CopilotのMCP設定ファイルを開きます。
例:
C:\Users{user名}\AppData\Roaming\Code\User\settings.json
(VSCode Insider版を使っている場合には)
C:\Users{user名}\AppData\Roaming\Code - Insiders\User\settings.json

以下のようにmcp要素配下に上記mcp.jsonからinputsとserversの定義をコピーします

get_snippetの例

{
    "remote.autoForwardPortsSource": "hybrid",
    "workbench.startupEditor": "none",
    "mcp": {
        "inputs": [
            {
                "type": "promptString",
                "id": "functions-mcp-extension-system-key",
                "description": "Azure Functions MCP Extension System Key",
                "password": true
            },
            {
                "type": "promptString",
                "id": "functionapp-name",
                "description": "Azure Functions App Name"
            }
        ],
        "servers": {
            "my-mcp-server-83a9388b": {
                "type": "sse",
                "url": "https://${input:functionapp-name}.azurewebsites.net/runtime/webhooks/mcp/sse",
                "headers": {
                    "x-functions-key": "${input:functions-mcp-extension-system-key}"
                }
            }
        }
    }
}

実行時にプロンプトでfunctionapp-nameとfunctions-mcp-extension-system-keyを聞かれるため入力します。
上記を実行すると、MCP Server: {指定した名前}でMCPサーバーが追加されます。

これで以下のようにsnippetのリモートへの保存が可能となりました。

このsnippetはBlobストレージのsnippetというコンテナ内に各snippetごとのファイルとして保存されています。

まとめと雑感

  • Azure Functionsを活用しMCPサーバーを簡単に建てられました
  • このリファレンス実装を元に機能を追加していけば簡単に開発・運用ができそう
  • MCPのTools以外のResourceやPromptにはまだ対応してなさそう(情報求む)

参考

https://github.com/Azure-Samples/remote-mcp-functions-python

https://techcommunity.microsoft.com/blog/appsonazureblog/build-ai-agent-tools-using-remote-mcp-with-azure-functions/4401059

https://learn.microsoft.com/en-us/samples/azure-samples/remote-mcp-functions-python/remote-mcp-functions-python/

また、.NETでのMCPは同僚のお二人が先行して記事化しているのでご参考ください。

https://zenn.dev/microsoft/articles/azure-function-mcp
https://zenn.dev/microsoft/articles/mcp-azurefunctions

Discussion