Compartilhar via


Início Rápido: Hospedar servidores criados com SDKs do MCP no Azure Functions

Neste guia rápido, você aprenderá a hospedar servidores MCP (Protocolo de Contexto de Modelo) no Azure Functions que você cria usando SDKs oficiais do MCP. A hospedagem no plano Flex de Consumo permite que você aproveite a escalabilidade sem servidor do Azure Functions, o modelo de cobrança conforme o uso e os recursos de segurança integrados. É ideal para servidores MCP que usam o protocolo de transporte streamable-http.

Este artigo usa um projeto de servidor MCP de exemplo criado usando SDKs oficiais do MCP.

Dica

O Functions também fornece uma extensão MCP que permite criar servidores MCP usando o modelo de programação do Azure Functions. Para obter mais informações, consulte Início Rápido: Criar um servidor MCP remoto personalizado usando o Azure Functions.

Como o novo aplicativo é executado no plano Consumo Flexível, que segue um modelo de cobrança pague pelo que você usa, concluir este início rápido gera um pequeno custo de alguns centavos ou menos em sua conta do Azure.

Importante

Embora a hospedagem de seus servidores MCP usando manipuladores personalizados tenha suporte para todos os idiomas, este cenário de início rápido atualmente só tem exemplos para C#, Python e TypeScript. Para concluir este início rápido, selecione um desses idiomas com suporte na parte superior do artigo.

Pré-requisitos

  • Python 3.11 ou superior
  • uv para o gerenciamento de pacotes do Python

Observação

Este exemplo requer que você tenha permissão para criar um aplicativo do Microsoft Entra na assinatura do Azure que você usa.

Introdução a um projeto de exemplo

A maneira mais fácil de começar é clonar um projeto de exemplo de servidor MCP criado com SDKs oficiais do MCP:

  1. No Visual Studio Code, abra uma pasta ou workspace em que você deseja criar seu projeto.
  1. No Terminal, execute este comando para inicializar o exemplo do .NET:

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

    Esse comando extrai os arquivos de projeto do repositório de modelos e inicializa o projeto na pasta atual. O sinalizador -e define um nome para o ambiente atual. Em azd, o ambiente mantém um contexto de implantação exclusivo para seu aplicativo e você pode definir mais de um. Ele também é usado em nomes dos recursos que você cria no Azure.

  1. No Terminal, execute este comando para inicializar o exemplo typeScript:

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

    Esse comando extrai os arquivos de projeto do repositório de modelos e inicializa o projeto na pasta atual. O sinalizador -e define um nome para o ambiente atual. Em azd, o ambiente mantém um contexto de implantação exclusivo para seu aplicativo e você pode definir mais de um. Ele também é usado em nomes dos recursos que você cria no Azure.

  1. No Terminal, execute este comando para inicializar o exemplo do Python:

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

    Esse comando extrai os arquivos de projeto do repositório de modelos e inicializa o projeto na pasta atual. O sinalizador -e define um nome para o ambiente atual. Em azd, o ambiente mantém um contexto de implantação exclusivo para seu aplicativo e você pode definir mais de um. Ele também é usado em nomes dos recursos que você cria no Azure.

O modelo de projeto de código é para um servidor MCP com ferramentas que acessam APIs de clima público.

Executar o servidor MCP localmente

O Visual Studio Code integra-se às Ferramentas Principais do Azure Functions para permitir que você execute esse projeto em seu computador de desenvolvimento local.

  1. Abrir Terminal no editor (Ctrl+Shift+` )
  1. No diretório raiz, execute func start para iniciar o servidor. O painel Terminal exibe a saída das Core Tools.
  1. No diretório raiz, execute npm install para instalar dependências e execute npm run build.
  2. Para iniciar o servidor, execute func start.
  1. No diretório raiz, execute uv run func start para criar ambiente virtual, instalar dependências e iniciar o servidor.

Testar o servidor usando o GitHub Copilot

Para verificar seu servidor usando o GitHub Copilot no Visual Studio Code, siga estas etapas:

  1. Abra o arquivo mcp.json no diretório .vscode.

  2. Inicie o servidor selecionando o botão Iniciar acima da local-mcp-server configuração.

  3. Na janela Do Chat do Copilot, verifique se o modelo do Agente está selecionado, selecione o ícone Configurar ferramentas e verifique se ele MCP Server:local-mcp-server está habilitado no chat.

  4. Execute este comando no bate-papo:

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

    Copilot deve chamar uma das ferramentas meteorológicas para ajudar a responder a essa pergunta. Quando solicitado a executar a ferramenta, selecione Permitir neste Workspace para que você não precise conceder novamente esta permissão.

Depois de verificar a funcionalidade da ferramenta localmente, você pode parar o servidor e implantar o código do projeto no Azure.

Publicar no Azure

Este projeto está configurado para usar o comando azd up para implantar este projeto em um novo aplicativo de funções em um plano de Consumo Flex no Azure. O projeto inclui um conjunto de arquivos Bicep que azd usa para criar uma implantação segura que segue as práticas recomendadas.

  1. Entrar no Azure:

    azd login
    
  2. Configure o Visual Studio Code como um aplicativo cliente pré-autenticado:

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

    Um aplicativo pré-autenticado pode autenticar e acessar seu servidor MCP sem exigir mais solicitações de consentimento.

  3. No Visual Studio Code, pressione F1 para abrir a paleta de comandos. Pesquise e execute o comando Azure Developer CLI (azd): Package, Provision and Deploy (up). Em seguida, entre usando sua conta do Azure.

  4. Quando solicitado, forneça esses parâmetros de implantação obrigatórios:

    Parâmetro Description
    Assinatura do Azure Assinatura na qual seus recursos serão criados.
    Localização do Azure Região do Azure na qual criar o grupo de recursos que contém os novos recursos do Azure. Somente regiões que atualmente dão suporte para o plano de Consumo Flex são mostradas.

    Depois que o comando for concluído com êxito, você verá links para os recursos criados e o endpoint para o servidor MCP implantado. Anote o nome do aplicativo de funções, que você precisa para a próxima seção.

    Dica

    Se ocorrer um erro ao executar o azd up comando, basta executar novamente o comando. Você pode executar azd up repetidamente porque ele ignora criar quaisquer recursos que já existem. Você também pode chamar azd up novamente ao implantar atualizações em seu serviço.

Conectar-se ao servidor MCP remoto

Seu servidor MCP agora está em execução no Azure. Para conectar o GitHub Copilot ao servidor remoto, defina-o nas configurações do workspace.

  1. No arquivo mcp.json, realize a troca para o servidor remoto selecionando o comando Parar para a configuração local-mcp-server, e o comando Iniciar na configuração remote-mcp-server.

  2. Quando solicitado a informar o domínio do aplicativo de função, insira o nome do aplicativo de função que você anotou na seção anterior. Quando solicitado a autenticar na Microsoft, selecione Permitir e escolha sua conta do Azure.

  3. Verifique o servidor remoto fazendo uma pergunta como:

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

    Copilot chama uma das ferramentas meteorológicas para atender à consulta.

Dica

Você pode ver a saída de um servidor selecionando Mais...>Mostrar Saída. A saída fornece informações úteis sobre possíveis falhas de conexão. Você também pode selecionar o ícone de engrenagem para alterar os níveis de log para Rastreamentos para obter mais detalhes sobre as interações entre o cliente (Visual Studio Code) e o servidor.

Examinar o código (opcional)

Você pode examinar o código que define o servidor MCP:

O código do servidor MCP é definido na raiz do projeto. O servidor usa o SDK oficial do MCP do C# para definir estas ferramentas relacionadas ao clima:

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()}
                """));
    }
}

Você pode exibir o modelo de projeto completo no repositório do GitHub do Azure Functions .NET MCP SDK hosting.

O código do servidor MCP é definido no server.py arquivo. O servidor usa o SDK oficial do Python MCP para definir ferramentas relacionadas ao clima. Essa é a definição da get_forecast ferramenta:

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)

Você pode visualizar o modelo de projeto completo no repositório GitHub do Azure Functions Python MCP SDK.

O código do servidor MCP é definido na src pasta. O servidor usa o SDK do MCP Node.js oficial para definir ferramentas relacionadas ao clima. Essa é a definição da get-forecast ferramenta:

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;
}

Você pode exibir o modelo de projeto completo no repositório GitHub do Azure Functions TypeScript MCP SDK.

Limpar os recursos

Quando terminar de trabalhar com seu servidor MCP e recursos relacionados, use este comando para excluir o aplicativo de funções e seus recursos relacionados do Azure para evitar incorrer em custos adicionais:

azd down