次の方法で共有


クイック スタート: Azure Functions 上の MCP SDK を使用して構築されたホスト サーバー

このクイック スタートでは、公式の MCP SDK を使用して作成した Azure Functions モデル コンテキスト プロトコル (MCP) サーバーでホストする方法について説明します。 Flex Consumption プランホスティングを使用すると、Azure Functions のサーバーレス スケール、従量課金制の課金モデル、組み込みのセキュリティ機能を利用できます。 streamable-http トランスポートを使用する MCP サーバーに最適です。

この記事では、公式の MCP SDK を使用して構築されたサンプル MCP サーバー プロジェクトを使用します。

ヒント

Functions には、Azure Functions プログラミング モデルを使用して MCP サーバーを作成できる MCP 拡張機能も用意されています。 詳細については、「 クイック スタート: Azure Functions を使用してカスタム リモート MCP サーバーを構築する」を参照してください。

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

Important

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

[前提条件]

  • Python 3.11 以降
  • Python パッケージ管理用としての uv

このサンプルでは、使用する Azure サブスクリプションで Microsoft Entra アプリ を作成するためのアクセス許可が必要です。

サンプル プロジェクトを始める

開始する最も簡単な方法は、公式の MCP SDK で構築された MCP サーバー サンプル プロジェクトを複製することです。

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

    azd init --template mcp-sdk-functions-hosting-dotnet -e mcpsdkserver-dotnet
    

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

  1. ターミナルで、次のコマンドを実行して TypeScript サンプルを初期化します。

    azd init --template mcp-sdk-functions-hosting-node  -e mcpsdkserver-node
    

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

  1. ターミナルで、次のコマンドを実行して Python サンプルを初期化します。

    azd init --template mcp-sdk-functions-hosting-python -e mcpsdkserver-python
    

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

コード プロジェクト テンプレートは、パブリック気象 API にアクセスするツールを備えた MCP サーバー用です。

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

Visual Studio Code は Azure Functions Core Tools と統合され、ローカル開発コンピューターでこのプロジェクトを実行できます。

  1. エディターでターミナルを開く (Ctrl+Shift+` )
  1. ルート ディレクトリで、 func start を実行してサーバーを起動します。 ターミナル パネルに、Core Tools からの出力が表示されます。
  1. ルート ディレクトリで、 npm install を実行して依存関係をインストールし、 npm run build実行します。
  2. サーバーを起動するには、 func startを実行します。
  1. ルート ディレクトリで、 uv run func start を実行して仮想環境を作成し、依存関係をインストールして、サーバーを起動します。

GitHub Copilot を使用してサーバーをテストする

Visual Studio Code で GitHub Copilot を使用してサーバーを確認するには、次の手順に従います。

  1. mcp.json ディレクトリ内の .vscode ファイルを開きます。

  2. 構成の上にある local-mcp-server] ボタンを選択して、サーバーを起動します。

  3. [Copilot チャット ] ウィンドウで、 エージェント モデルが選択されていることを確認し、[ツールの 構成 ] アイコンを選択して、チャットで MCP Server:local-mcp-server が有効になっていることを確認します。

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

    Return the weather forecast for New York City using #local-mcp-server
    

    Copilot は、この質問に答えるために、気象ツールの 1 つを呼び出す必要があります。 ツールの実行を求められたら、このワークスペースで許可 を選択して、アクセス許可を再度付与する手間を省きましょう。

ツール機能をローカルで確認したら、サーバーを停止し、プロジェクト コードを Azure にデプロイできます。

Azure にデプロイ

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

  1. Azure にサインインします。

    azd login
    
  2. 事前認証されたクライアント アプリケーションとして Visual Studio Code を構成します。

    azd env set PRE_AUTHORIZED_CLIENT_IDS aebc6443-996d-45c2-90f0-388ff96faa56
    

    事前認証されたアプリケーションは、同意プロンプトを追加しなくても、MCP サーバーに対する認証とアクセスを行うことができます。

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

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

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

    コマンドが正常に完了すると、作成したリソースと、デプロイされた MCP サーバーのエンドポイントへのリンクが表示されます。 次のセクションに必要な関数アプリ名を書き留めておきます。

    ヒント

    azd up コマンドの実行時にエラーが発生した場合は、コマンドを再実行してください。 azd upは、既に存在するリソースの作成をスキップするため、繰り返し実行できます。 サービスに更新プログラムをデプロイするときに、 azd up をもう一度呼び出すこともできます。

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

これで、MCP サーバーが Azure で実行されています。 GitHub Copilot をリモート サーバーに接続するには、ワークスペース設定で構成します。

  1. mcp.json ファイルで、構成の local-mcp-server] を選択し、構成で remote-mcp-server を選択してリモート サーバーに切り替えます。

  2. 関数アプリのドメインの入力を求められたら、前のセクションで説明した関数アプリの名前を入力します。 Microsoft に対する認証を求められたら、[ 許可 ] を選択し、Azure アカウントを選択します。

  3. 次のような質問をして、リモート サーバーを確認します。

    Return the weather forecast for Seattle using #remote-mcp-server.
    

    Copilot は、いずれかの気象ツールを呼び出してクエリに応答します。

ヒント

サーバーの出力は、[その他] を選択して確認できます 。..>出力を表示します。 この出力は、接続エラーの可能性に関する有用な情報を提供します。 歯車アイコンを選択してログ レベルを トレース に変更し、クライアント (Visual Studio Code) とサーバーの間の相互作用の詳細を取得することもできます。

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

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

MCP サーバー コードはプロジェクト ルートで定義されます。 サーバーは公式の C# MCP SDK を使用して、これらの気象関連ツールを定義します。

using ModelContextProtocol;
using ModelContextProtocol.Server;
using System.ComponentModel;
using System.Globalization;
using System.Text.Json;

namespace QuickstartWeatherServer.Tools;

[McpServerToolType]
public sealed class WeatherTools
{
    [McpServerTool, Description("Get weather alerts for a US state.")]
    public static async Task<string> GetAlerts(
        HttpClient client,
        [Description("The US state to get alerts for. Use the 2 letter abbreviation for the state (e.g. NY).")] string state)
    {
        using var jsonDocument = await client.ReadJsonDocumentAsync($"/alerts/active/area/{state}");
        var jsonElement = jsonDocument.RootElement;
        var alerts = jsonElement.GetProperty("features").EnumerateArray();

        if (!alerts.Any())
        {
            return "No active alerts for this state.";
        }

        return string.Join("\n--\n", alerts.Select(alert =>
        {
            JsonElement properties = alert.GetProperty("properties");
            return $"""
                    Event: {properties.GetProperty("event").GetString()}
                    Area: {properties.GetProperty("areaDesc").GetString()}
                    Severity: {properties.GetProperty("severity").GetString()}
                    Description: {properties.GetProperty("description").GetString()}
                    Instruction: {properties.GetProperty("instruction").GetString()}
                    """;
        }));
    }

    [McpServerTool, Description("Get weather forecast for a location.")]
    public static async Task<string> GetForecast(
        HttpClient client,
        [Description("Latitude of the location.")] double latitude,
        [Description("Longitude of the location.")] double longitude)
    {
        var pointUrl = string.Create(CultureInfo.InvariantCulture, $"/points/{latitude},{longitude}");
        using var jsonDocument = await client.ReadJsonDocumentAsync(pointUrl);
        var forecastUrl = jsonDocument.RootElement.GetProperty("properties").GetProperty("forecast").GetString()
            ?? throw new Exception($"No forecast URL provided by {client.BaseAddress}points/{latitude},{longitude}");

        using var forecastDocument = await client.ReadJsonDocumentAsync(forecastUrl);
        var periods = forecastDocument.RootElement.GetProperty("properties").GetProperty("periods").EnumerateArray();

        return string.Join("\n---\n", periods.Select(period => $"""
                {period.GetProperty("name").GetString()}
                Temperature: {period.GetProperty("temperature").GetInt32()}°F
                Wind: {period.GetProperty("windSpeed").GetString()} {period.GetProperty("windDirection").GetString()}
                Forecast: {period.GetProperty("detailedForecast").GetString()}
                """));
    }
}

GitHub リポジトリを ホストする Azure Functions .NET MCP SDK で、完全なプロジェクト テンプレートを表示できます。

MCP サーバー コードは、 server.py ファイルで定義されます。 サーバーは、公式の Python MCP SDK を使用して気象関連のツールを定義します。 これは、 get_forecast ツールの定義です。

import os
import sys
import warnings
import logging
from typing import Any
from pathlib import Path

import httpx
from azure.identity import OnBehalfOfCredential, ManagedIdentityCredential
from mcp.server.fastmcp import FastMCP
from fastmcp.server.dependencies import get_http_request
from starlette.requests import Request
from starlette.responses import HTMLResponse

# Initialize FastMCP server
mcp = FastMCP("weather", stateless_http=True)

# Constants
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """
    # First get the forecast grid endpoint
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return "Unable to fetch forecast data for this location."

    # Get the forecast URL from the points response
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast."

    # Format the periods into a readable forecast
    periods = forecast_data["properties"]["periods"]
    forecasts = []
    for period in periods[:5]:  # Only show next 5 periods
        forecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""
        forecasts.append(forecast)

    return "\n---\n".join(forecasts)

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

MCP サーバー コードは、 src フォルダーに定義されています。 サーバーは、公式の Node.js MCP SDK を使用して気象関連のツールを定義します。 これは、 get-forecast ツールの定義です。

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { ManagedIdentityCredential, OnBehalfOfCredential } from '@azure/identity';

const NWS_API_BASE = "https://api.weather.gov";
const USER_AGENT = "weather-app/1.0";

// Function to create a new server instance for each request (stateless)
export const createServer = () => {
  const server = new McpServer({
    name: "weather",
    version: "1.0.0",
  });
  server.registerTool(
    "get-forecast",
    {
      title: "Get Weather Forecast",
      description: "Get weather forecast for a location",
      inputSchema: {
        latitude: z.number().min(-90).max(90).describe("Latitude of the location"),
        longitude: z
          .number()
          .min(-180)
          .max(180)
          .describe("Longitude of the location"),
      },
      outputSchema: z.object({
        forecast: z.string(),
      }),
    },
    async ({ latitude, longitude }) => {
      // Get grid point data
      const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
      const pointsData = await makeNWSRequest<PointsResponse>(pointsUrl);

      if (!pointsData) {
        const output = { forecast: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).` };
        return {
          content: [{ type: "text", text: JSON.stringify(output) }],
          structuredContent: output,
        };
      }

      const forecastUrl = pointsData.properties?.forecast;
      if (!forecastUrl) {
        const output = { forecast: "Failed to get forecast URL from grid point data" };
        return {
          content: [{ type: "text", text: JSON.stringify(output) }],
          structuredContent: output,
        };
      }

      // Get forecast data
      const forecastData = await makeNWSRequest<ForecastResponse>(forecastUrl);
      if (!forecastData) {
        const output = { forecast: "Failed to retrieve forecast data" };
        return {
          content: [{ type: "text", text: JSON.stringify(output) }],
          structuredContent: output,
        };
      }

      const periods = forecastData.properties?.periods || [];
      if (periods.length === 0) {
        const output = { forecast: "No forecast periods available" };
        return {
          content: [{ type: "text", text: JSON.stringify(output) }],
          structuredContent: output,
        };
      }

      // Format forecast periods
      const formattedForecast = periods.map((period: ForecastPeriod) =>
        [
          `${period.name || "Unknown"}:`,
          `Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`,
          `Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`,
          `${period.shortForecast || "No forecast available"}`,
          "---",
        ].join("\n"),
      );

      const forecastText = `Forecast for ${latitude}, ${longitude}:\n\n${formattedForecast.join("\n")}`;
      const output = { forecast: forecastText };

      return {
        content: [{ type: "text", text: forecastText }],
        structuredContent: output,
      };
    },
  );
  return server;
}

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

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

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

azd down