이 빠른 시작에서는 공식 MCP SDK를 사용하여 만든 AZURE Functions MCP(모델 컨텍스트 프로토콜) 서버에서 호스트하는 방법을 알아봅니다. Flex 소비 계획 호스팅을 사용하면 Azure Functions의 서버리스 규모, 종량제 청구 모델 및 기본 제공 보안 기능을 활용할 수 있습니다. streamable-http 전송을 사용하는 MCP 서버에 적합합니다.
이 문서에서는 공식 MCP SDK를 사용하여 빌드된 샘플 MCP 서버 프로젝트를 사용합니다.
팁 (조언)
또한 Functions는 Azure Functions 프로그래밍 모델을 사용하여 MCP 서버를 만들 수 있는 MCP 확장을 제공합니다. 자세한 내용은 빠른 시작: Azure Functions를 사용하여 사용자 지정 원격 MCP 서버 빌드를 참조하세요.
새 서버는 종량제 청구 모델을 따르는 Flex Consumption 계획에서 실행되므로, 이 빠른 시작을 완료하는 데 Azure 계정에서 약간의 비용이 발생할 수 있습니다.
중요합니다
사용자 지정 처리기를 사용하여 MCP 서버를 호스트하는 것은 모든 언어에서 지원되지만 이 빠른 시작 시나리오에는 현재 C#, Python 및 TypeScript에 대한 예제만 있습니다. 이 빠른 시작을 완료하려면 문서 맨 위에서 지원되는 언어 중 하나를 선택합니다.
필수 조건
- Node.js 22 이상
- Python 3.11 이상
- Python 패키지 관리를 위한 uv
다음 확장을 사용하는 Visual Studio Code:
Azure Functions 확장. 이 확장을 사용하려면 Azure Functions Core Tools v4.5.0 이상이 필요하며 사용할 수 없는 경우 설치를 시도합니다.
Azure Developer CLI v1.17.2 이상
Azure CLI Azure Cloud Shell에서 Azure CLI 명령을 실행할 수도 있습니다.
활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
비고
이 샘플에서는 사용하는 Azure 구독에서 Microsoft Entra 앱을 만들 수 있는 권한이 있어야 합니다.
샘플 프로젝트 시작
시작하는 가장 쉬운 방법은 공식 MCP SDK로 빌드된 MCP 서버 샘플 프로젝트를 복제하는 것입니다.
- Visual Studio Code에서 프로젝트를 만들 폴더 또는 작업 영역을 엽니다.
터미널에서 다음 명령을 실행하여 .NET 샘플을 초기화합니다.
azd init --template mcp-sdk-functions-hosting-dotnet -e mcpsdkserver-dotnet이 명령은 템플릿 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다.
-e플래그는 현재 환경의 이름을 설정합니다. 환경에서azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. Azure에서 만드는 리소스의 이름에도 사용됩니다.
터미널에서 이 명령을 실행하여 TypeScript 샘플을 초기화합니다.
azd init --template mcp-sdk-functions-hosting-node -e mcpsdkserver-node이 명령은 템플릿 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다.
-e플래그는 현재 환경의 이름을 설정합니다. 환경에서azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. Azure에서 만드는 리소스의 이름에도 사용됩니다.
터미널에서 다음 명령을 실행하여 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 와 통합되므로 로컬 개발 컴퓨터에서 이 프로젝트를 실행할 수 있습니다.
- 편집기에서 터미널 열기(
Ctrl+Shift+`)
- 루트 디렉터리에서 실행
func start하여 서버를 시작합니다. 터미널 패널에는 Core Tools의 출력이 표시됩니다.
- 루트 디렉터리에서 실행
npm install하여 종속성을 설치한 다음npm run build실행합니다. - 서버를 시작하려면
func start를 실행합니다.
- 루트 디렉터리에서 실행
uv run func start하여 가상 환경을 만들고, 종속성을 설치하고, 서버를 시작합니다.
GitHub Copilot를 사용하여 서버 테스트
Visual Studio Code에서 GitHub Copilot를 사용하여 서버를 확인하려면 다음 단계를 수행합니다.
mcp.json디렉터리에서.vscode파일 열기구성 위의 시작 단추를 선택하여 서버를 시작 합니다
local-mcp-server.Copilot 채팅 창에서 에이전트 모델이 선택되어 있는지 확인하고 , 도구 구성 아이콘을 선택하고, 채팅에서 사용하도록
MCP Server:local-mcp-server설정되어 있는지 확인합니다.채팅에서 다음 프롬프트를 실행합니다.
Return the weather forecast for New York City using #local-mcp-server부조종사 이 질문에 대답하는 데 도움이 날씨 도구 중 하나를 호출해야합니다. 도구를 실행하라는 메시지가 표시되면 이 작업 영역에서 허용 을 선택하여 매번 이 권한을 다시 부여하지 않아도 되게 하세요.
도구 기능을 로컬로 확인한 후 서버를 중지하고 프로젝트 코드를 Azure에 배포할 수 있습니다.
Azure에 배포
이 프로젝트는 azd up 명령을 사용하여 Azure의 Flex 사용량 플랜에서 새 함수 앱에 이 프로젝트를 배포하도록 구성되었습니다. 이 프로젝트에는 모범 사례를 따르는 보안 배포를 만드는 데 사용하는 Bicep 파일 azd 집합이 포함되어 있습니다.
Azure에 로그인:
azd loginVisual Studio Code를 사전 인증된 클라이언트 애플리케이션으로 구성합니다.
azd env set PRE_AUTHORIZED_CLIENT_IDS aebc6443-996d-45c2-90f0-388ff96faa56사전 인증된 애플리케이션은 더 많은 동의 프롬프트 없이 MCP 서버에 인증하고 액세스할 수 있습니다.
Visual Studio Code에서 F1 키를 눌러 명령 팔레트를 엽니다. 명령을
Azure Developer CLI (azd): Package, Provision and Deploy (up)검색하고 실행합니다. 그런 다음, Azure 계정을 사용하여 로그인합니다.메시지가 표시되면 다음과 같은 필수 배포 매개 변수를 제공합니다.
매개 변수 Description Azure 구독 리소스가 만들어지는 구독. Azure 위치 새 Azure 리소스가 포함된 리소스 그룹을 만들 Azure 지역입니다. 현재 Flex 사용량 플랜을 지원하는 지역만 표시됩니다. 명령이 성공적으로 완료되면 만든 리소스 및 배포된 MCP 서버에 대한 엔드포인트에 대한 링크가 표시됩니다. 다음 섹션에 필요한 함수 앱 이름을 기록해 둡다.
팁 (조언)
명령을 실행할
azd up때 오류가 발생하면 명령을 다시 실행하기만 하면 됩니다. 이미 존재하는 리소스 만들기를 건너뛰기 때문에 반복적으로 실행할azd up수 있습니다. 서비스에 업데이트를 배포할 때 다시 호출azd up할 수도 있습니다.
원격 MCP 서버에 연결
이제 MCP 서버가 Azure에서 실행되고 있습니다. GitHub Copilot를 원격 서버에 연결하려면 작업 영역 설정에서 구성합니다.
mcp.json파일에서 구성에서local-mcp-server를 선택하고 구성에서remote-mcp-server을 선택하여 원격 서버로 전환하세요.함수 앱의 도메인에 대한 메시지가 표시되면 이전 섹션에서 적어 두었다는 함수 앱의 이름을 입력합니다. Microsoft에 인증하라는 메시지가 표시되면 [허용 ]을 선택한 다음, Azure 계정을 선택합니다.
다음과 같은 질문을 하여 원격 서버를 확인합니다.
Return the weather forecast for Seattle using #remote-mcp-server.부조종사에서는 날씨 도구 중 하나를 호출하여 쿼리에 응답합니다.
팁 (조언)
자세히...를 선택하여 서버의 출력을 볼 수 있습니다.>출력을 표시합니다. 출력은 가능한 연결 오류에 대한 유용한 정보를 제공합니다. 기어 아이콘을 선택하여 로그 수준을 추적 으로 변경하여 클라이언트(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