次の方法で共有


クイック スタート: Azure Functions を使用してカスタム リモート MCP サーバーを構築する

このクイック スタートでは、Azure Developer CLI (azd) を使用して、テンプレート プロジェクトからカスタム リモート モデル コンテキスト プロトコル (MCP) サーバーを作成します。 MCP サーバーは、Azure Functions MCP サーバー拡張機能を使用して、AI モデル、エージェント、アシスタント用のツールを提供します。 プロジェクトをローカルで実行し、GitHub Copilot を使用してコードを検証した後、セキュリティで保護されたスケーラブルなデプロイに関する現在のベスト プラクティスに従って、Azure Functions の新しいサーバーレス関数アプリにデプロイします。

ヒント

Functions を使用すると、コード プロジェクトに変更を加えることなく、既存の MCP サーバー コード プロジェクトを Flex Consumption プラン アプリにデプロイすることもできます。 詳細については、「 クイック スタート: Azure Functions で既存の MCP サーバーをホストする」を参照してください。

新しいアプリは、 従量課金 制の課金モデルに従う Flex Consumption プランで実行されるため、このクイックスタートを完了すると、Azure アカウントで数 USD セント以下の小さなコストが発生します。

Important

カスタム MCP サーバーの作成はすべての Functions 言語でサポートされていますが、このクイック スタート シナリオには現在、C#、Python、TypeScript の例しかありません。 このクイック スタートを完了するには、この記事の上部にあるサポートされている言語のいずれかを選択します。

この記事は、Azure Functions の Node.js プログラミング モデルのバージョン 4 に対応しています。

この記事は、Azure Functions の Python プログラミング モデルのバージョン 2 に対応しています。

[前提条件]

プロジェクトを初期化する

azd init コマンドを使用して、テンプレートからローカル Azure Functions コード プロジェクトを作成します。

  1. Visual Studio Code で、プロジェクトを作成するフォルダーまたはワークスペースを開きます。
  1. ターミナルで、次の azd init コマンドを実行します。

    azd init --template remote-mcp-functions-dotnet -e mcpserver-dotnet
    

    このコマンドにより、テンプレート リポジトリからプロジェクト ファイルがプルされ、現在のフォルダー内のプロジェクトが初期化されます。 -e フラグでは、現在の環境の名前が設定されます。 azdでは、環境はアプリの一意のデプロイ コンテキストを維持し、複数を定義できます。 これは、Azure で作成するリソース グループの名前でも使用されます。

  1. ローカル ターミナルまたはコマンド プロンプトで、次の azd init コマンドを実行します。

    azd init --template remote-mcp-functions-java -e mcpserver-java 
    

    このコマンドにより、テンプレート リポジトリからプロジェクト ファイルがプルされ、現在のフォルダー内のプロジェクトが初期化されます。 -e フラグでは、現在の環境の名前が設定されます。 azdでは、環境はアプリの一意のデプロイ コンテキストを維持し、複数を定義できます。 これは、Azure で作成するリソースの名前でも使用されます。

  1. ローカル ターミナルまたはコマンド プロンプトで、次の azd init コマンドを実行します。

    azd init --template remote-mcp-functions-typescript -e mcpserver-ts
    

    このコマンドにより、テンプレート リポジトリからプロジェクト ファイルがプルされ、現在のフォルダー内のプロジェクトが初期化されます。 -e フラグでは、現在の環境の名前が設定されます。 azdでは、環境はアプリの一意のデプロイ コンテキストを維持し、複数を定義できます。 これは、Azure で作成するリソースの名前でも使用されます。

  1. ローカル ターミナルまたはコマンド プロンプトで、次の azd init コマンドを実行します。

    azd init --template remote-mcp-functions-python -e mcpserver-python
    

    このコマンドにより、テンプレート リポジトリからプロジェクト ファイルがプルされ、現在のフォルダー内のプロジェクトが初期化されます。 -e フラグでは、現在の環境の名前が設定されます。 azdでは、環境はアプリの一意のデプロイ コンテキストを維持し、複数を定義できます。 これは、Azure で作成するリソースの名前でも使用されます。

ストレージ エミュレーターを起動する

Azurite エミュレーターを使用して、コード プロジェクトをローカルで実行するときに Azure Storage アカウント接続をシミュレートします。

  1. まだインストールしていない場合は、 Azurite をインストールします。

  2. F1 キーを押します。 コマンド パレットで、コマンド Azurite: Start を検索して実行し、ローカル ストレージ エミュレーターを起動します。

MCP サーバーをローカルで実行する

Visual Studio Code は Azure Functions Core ツール と統合され、Azurite エミュレーターを使用してローカル開発コンピューターでこのプロジェクトを実行できます。

  1. 関数をローカルで開始するには、F5 キーまたは、左側のアクティビティ バーの [実行とデバッグ] アイコンを押します。 ターミナル パネルに、Core Tools からの出力が表示されます。 アプリは ターミナル パネルで起動し、ローカルで実行されている関数の名前を確認できます。

  2. Visual Studio Code で GitHub Copilot を構成するために使用するローカル MCP サーバー エンドポイント ( http://localhost:7071/runtime/webhooks/mcp など) をメモしておきます。

GitHub Copilot を使用して確認する

コードを確認するには、実行中のプロジェクトを Visual Studio Code の GitHub Copilot の MCP サーバーとして追加します。

  1. F1 キーを押します。 コマンド パレットで、 MCP: Add Server を検索して実行します。

  2. トランスポートの種類として HTTP (Server-Sent イベント) を選択します。

  3. 前の手順でコピーした MCP エンドポイントの URL を入力します。

  4. 生成された サーバー ID を 使用し、[ ワークスペース ] を選択して、MCP サーバー接続をワークスペース設定に保存します。

  5. コマンド パレットを開き、 MCP: List Servers を 実行し、追加したサーバーが一覧表示されて実行されていることを確認します。

  6. Copilot チャットで、[ エージェント モード] を選択し、次のプロンプトを実行します。

    Say Hello
    

    ツールの実行を求められたら、 このワークスペースで [許可 ] を選択して、アクセス許可を付与し続ける必要がないようにします。 プロンプトが実行され、 Hello World 応答が返され、関数の実行情報がログに書き込まれます。

  7. 次に、いずれかのプロジェクト ファイルでコードを選択し、次のプロンプトを実行します。

    Save this snippet as snippet1
    

    Copilot はスニペットを格納し、 getSnippets ツールを使用してスニペットを取得する方法に関する情報を要求に応答します。 ここでも、ログで関数の実行を確認し、 saveSnippets 関数が実行されたことを確認できます。

  8. Copilot チャットで、次のプロンプトを実行します。

    Retrieve snippet1 and apply to NewFile
    

    Copilot はスニペットを取得し、 NewFileという名前のファイルに追加し、コード スニペットをプロジェクトで動作させるために必要と思われる他の操作を行います。 Functions ログには、 getSnippets エンドポイントが呼び出されたことが示されます。

  9. テストが完了したら、Ctrl キーを押しながら C キーを押して Functions ホストを停止します。

コードの確認 (省略可能)

MCP サーバー ツールを定義するコードを確認できます。

MCP サーバー ツールの関数コードは、 src フォルダーに定義されています。 McpToolTrigger属性は、MCP サーバー ツールとして関数を公開します。

[Function(nameof(SayHello))]
public string SayHello(
    [McpToolTrigger(HelloToolName, HelloToolDescription)] ToolInvocationContext context
)
{
    logger.LogInformation("Saying hello");
    return "Hello I am MCP Tool!";
}
    [Function(nameof(GetSnippet))]
    public object GetSnippet(
        [McpToolTrigger(GetSnippetToolName, GetSnippetToolDescription)]
            ToolInvocationContext context,
        [BlobInput(BlobPath)] string snippetContent
    )
    {
        return snippetContent;
    }

    [Function(nameof(SaveSnippet))]
    [BlobOutput(BlobPath)]
    public string SaveSnippet(
        [McpToolTrigger(SaveSnippetToolName, SaveSnippetToolDescription)]
            ToolInvocationContext context,
        [McpToolProperty(SnippetNamePropertyName, SnippetNamePropertyDescription, true)]
            string name,
        [McpToolProperty(SnippetPropertyName, SnippetPropertyDescription, true)]
            string snippet
    )
    {
        return snippet;
    }
}

完全なプロジェクト テンプレートは 、Azure Functions .NET MCP Server GitHub リポジトリで確認できます。

MCP サーバー ツールの関数コードは、 src/main/java/com/function/ フォルダーに定義されています。 @McpToolTrigger注釈は、MCP サーバー ツールとして関数を公開します。

            description = "The messages to be logged.",
            isRequired = true,
            isArray = true)
        String messages,
        final ExecutionContext functionExecutionContext
) {
    functionExecutionContext.getLogger().info("Hello, World!");
    functionExecutionContext.getLogger().info("Tool Name: " + mcpToolInvocationContext.getName());
    functionExecutionContext.getLogger().info("Transport Type: " + mcpToolInvocationContext.getTransportType());
    
    // Handle different transport types
    if (mcpToolInvocationContext.isHttpStreamable()) {
        functionExecutionContext.getLogger().info("Session ID: " + mcpToolInvocationContext.getSessionid());
    } else if (mcpToolInvocationContext.isHttpSse()) {
        if (mcpToolInvocationContext.getClientinfo() != null) {
            functionExecutionContext.getLogger().info("Client: " + 
                mcpToolInvocationContext.getClientinfo().get("name").getAsString() + " v" +
    // Write the snippet content to the output blob
    outputBlob.setValue(snippet);
    
    return "Successfully saved snippet '" + snippetName + "' with " + snippet.length() + " characters.";
}

/**
 * Azure Function that handles retrieving a text snippet from Azure Blob Storage.
 * <p>
 * The function is triggered by an MCP Tool Trigger. The snippet name is provided
 * as an MCP tool property, and the snippet content is read from the blob at the 
 * path derived from the snippet name.
 *
 * @param mcpToolInvocationContext The JSON input from the MCP tool trigger.
 * @param snippetName   The name of the snippet to retrieve, provided as an MCP tool property.
 * @param inputBlob     The Azure Blob input binding that fetches the snippet content.
 * @param functionExecutionContext       The execution context for logging.
 */
@FunctionName("GetSnippets")
@StorageAccount("AzureWebJobsStorage")
public String getSnippet(
        @McpToolTrigger(
            name = "getSnippets",
            description = "Gets a text snippet from your snippets collection.")
        String mcpToolInvocationContext,
        @McpToolProperty(
            name = SNIPPET_NAME_PROPERTY_NAME,
            propertyType = "string",
            description = "The name of the snippet.",
            isRequired = true)
        String snippetName,
        @BlobInput(name = "inputBlob", path = BLOB_PATH)
        String inputBlob,
        final ExecutionContext functionExecutionContext
) {
    // Log the entire incoming JSON for debugging
    functionExecutionContext.getLogger().info(mcpToolInvocationContext);

    // Log the snippet name and the fetched snippet content from the blob

完全なプロジェクト テンプレートは 、Azure Functions Java MCP Server GitHub リポジトリで確認できます。

MCP サーバー ツールの関数コードは、 src/function_app.py ファイルで定義されています。 MCP 関数の注釈は、次の関数を MCP サーバー ツールとして公開します。

tool_properties_save_snippets_json = json.dumps([prop.to_dict() for prop in tool_properties_save_snippets_object])
tool_properties_get_snippets_json = json.dumps([prop.to_dict() for prop in tool_properties_get_snippets_object])


@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="hello_mcp",
    description="Hello world.",
    toolProperties="[]",
)
def hello_mcp(context) -> None:
    """

@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="save_snippet",
    description="Save a snippet with a name.",
    toolProperties=tool_properties_save_snippets_json,
)
@app.generic_output_binding(arg_name="file", type="blob", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def save_snippet(file: func.Out[str], context) -> str:
    content = json.loads(context)
    snippet_name_from_args = content["arguments"][_SNIPPET_NAME_PROPERTY_NAME]
    snippet_content_from_args = content["arguments"][_SNIPPET_PROPERTY_NAME]

    if not snippet_name_from_args:
        return "No snippet name provided"

    if not snippet_content_from_args:
        return "No snippet content provided"

    file.set(snippet_content_from_args)
    logging.info(f"Saved snippet: {snippet_content_from_args}")
    return f"Snippet '{snippet_content_from_args}' saved successfully"

完全なプロジェクト テンプレートは 、Azure Functions Python MCP Server GitHub リポジトリで確認できます。

MCP サーバー ツールの関数コードは、 src フォルダーに定義されています。 MCP 関数の登録では、次の関数が MCP サーバー ツールとして公開されます。

export async function mcpToolHello(_toolArguments:unknown, context: InvocationContext): Promise<string> {
    console.log(_toolArguments);
    // Get name from the tool arguments
    const mcptoolargs = context.triggerMetadata.mcptoolargs as {
        name?: string;
    };
    const name = mcptoolargs?.name;

    console.info(`Hello ${name}, I am MCP Tool!`);
    
    return `Hello ${name || 'World'}, I am MCP Tool!`;
}

// Register the hello tool
app.mcpTool('hello', {
    toolName: 'hello',
    description: 'Simple hello world MCP Tool that responses with a hello message.',
    toolProperties:{
        name: arg.string().describe('Required property to identify the caller.').optional()
    },
    handler: mcpToolHello
});
// SaveSnippet function - saves a snippet with a name
export async function saveSnippet(
  _toolArguments: unknown,
  context: InvocationContext
): Promise<string> {
  console.info("Saving snippet");

  // Get snippet name and content from the tool arguments
  const mcptoolargs = context.triggerMetadata.mcptoolargs as {
    snippetname?: string;
    snippet?: string;
  };

  const snippetName = mcptoolargs?.snippetname;
  const snippet = mcptoolargs?.snippet;

  if (!snippetName) {
    return "No snippet name provided";
  }

  if (!snippet) {
    return "No snippet content provided";
  }

  // Save the snippet to blob storage using the output binding
  context.extraOutputs.set(blobOutputBinding, snippet);

  console.info(`Saved snippet: ${snippetName}`);
  return snippet;
}

完全なプロジェクト テンプレートは 、Azure Functions TypeScript MCP Server GitHub リポジトリで確認できます。

MCP サーバー ツールをローカルで確認したら、プロジェクトを Azure に発行できます。

Azure にデプロイ

このプロジェクトは、azd up コマンドを使用して、Azure の Flex 従量課金プランの新しい関数アプリにこのプロジェクトをデプロイするように構成されています。 このプロジェクトには、ベスト プラクティスに従った Flex 消費プランへのセキュアなデプロイメントの作成に使用する Bicep ファイルのセットが含まれています。

  1. Visual Studio Code で、F1 キーを押してコマンド パレットを開きます。 コマンド Azure Developer CLI (azd): Package, Provison and Deploy (up)を検索して実行します。 次に、Azure アカウントを使用してサインインします。

  2. まだサインインしていない場合は、Azure アカウントで認証します。

  3. プロンプトが表示されたら、次の必須のデプロイ パラメーターを指定します。

    パラメーター Description
    Azure サブスクリプション リソースが作成されるサブスクリプション。
    Azure の場所 新しい Azure リソースを含むリソース グループを作成する Azure リージョン。 現在、Flex 従量課金プランをサポートしているリージョンのみが表示されます。

    コマンドが正常に完了した後、作成したリソースへのリンクが表示されます。

リモート MCP サーバーに接続する

これで、MCP サーバーが Azure で実行されています。 ツールにアクセスするときは、要求にシステム キーを含める必要があります。 このキーは、リモート MCP サーバーにアクセスするクライアントに対して、ある程度のアクセス制御を提供します。 このキーを取得したら、GitHub Copilot をリモート サーバーに接続できます。

  1. azdと Azure CLI を使用して、ツールにアクセスするために必要な MCP サーバー URL とシステム キー (mcp_extension) の両方を出力する次のスクリプトを実行します。

    eval $(azd env get-values --output dotenv)
    MCP_EXTENSION_KEY=$(az functionapp keys list --resource-group $AZURE_RESOURCE_GROUP \
        --name $AZURE_FUNCTION_NAME --query "systemKeys.mcp_extension" -o tsv)
    printf "MCP Server URL: %s\n" "https://$SERVICE_API_NAME.azurewebsites.net/runtime/webhooks/mcp"
    printf "MCP Server key: %s\n" "$MCP_EXTENSION_KEY"
    
  2. Visual Studio Code で F1 キーを押してコマンド パレットを開き、コマンド MCP: Open Workspace Folder MCP Configuratonを検索して実行すると、 mcp.json 構成ファイルが開きます。

  3. mcp.json構成で、前に追加した名前付き MCP サーバーを見つけ、urlの値をリモート MCP サーバー URL に変更し、次の例のように、コピーした MCP サーバー アクセス キーを含むheaders.x-functions-key要素を追加します。

    {
        "servers": {
            "remote-mcp-function": {
                "type": "http",
                "url": "https://contoso.azurewebsites.net/runtime/webhooks/mcp",
                "headers": {
                    "x-functions-key": "A1bC2dE3fH4iJ5kL6mN7oP8qR9sT0u..."
                }
            }
        }
    }
    
  4. 開いているでサーバー名の上にある mcp.json] ボタンを選択して、リモート MCP サーバーを再起動します。今回は、デプロイしたアプリを使用します。

デプロイメントを検証する

ローカルで行ったのと同じように GitHub Copilot でリモート MCP ツールを使用できるようになりましたが、Azure でコードが安全に実行されるようになりました。 前に使用したのと同じコマンドを再生して、すべてが正しく動作することを確認します。

リソースをクリーンアップする

MCP サーバーと関連リソースの操作が完了したら、次のコマンドを使用して関数アプリとその関連リソースを Azure から削除し、さらにコストが発生しないようにします。

azd down --no-prompt

--no-prompt オプションは、確認なしでリソース グループを削除するようにazdに指示します。 このコマンドは、ローカル コード プロジェクトには影響しません。

次のステップ