빠른 시작: Azure Functions 사용하여 사용자 지정 원격 MCP 서버 빌드

이 빠른 시작에서는 Azure 개발자 CLI(azd)를 사용하여 템플릿 프로젝트에서 사용자 지정 MCP(원격 모델 컨텍스트 프로토콜) 서버를 만듭니다. 이 MCP 서버는 Azure Functions MCP 서버 확장을 사용하여 AI 모델, 에이전트 및 도우미용 도구를 제공합니다. MCP 서버 확장을 사용하여 대화형 MCP 앱을 만들 수도 있습니다.

프로젝트를 로컬로 실행하고 GitHub Copilot 사용하여 코드를 확인한 후에는 안전하고 확장 가능한 배포에 대한 현재 모범 사례를 따르는 Azure Functions 새 서버리스 함수 앱에 배포합니다.

새 앱은 pay for-what-you-use 청구 모델을 따르는 Flex Consumption 계획에서 실행되므로 이 빠른 시작을 완료하면 Azure 계정에서 몇 USD 센트 이하의 비용이 발생합니다.

중요합니다

사용자 지정 MCP 서버 만들기는 모든 Functions 언어에서 지원되지만 이 빠른 시작 시나리오에는 현재 C#, Java, 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
    

    이 명령은 template 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다. -e 플래그는 현재 환경의 이름을 설정합니다. 환경에서 azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. 또한 Azure 만드는 리소스 그룹 이름의 일부이기도 합니다.

  1. 로컬 터미널 또는 명령 프롬프트에서 다음 azd init 명령을 실행합니다.

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

    이 명령은 template 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다. -e 플래그는 현재 환경의 이름을 설정합니다. 환경에서 azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. 또한 Azure 만드는 리소스 이름의 일부이기도 합니다.

  1. 로컬 터미널 또는 명령 프롬프트에서 다음 azd init 명령을 실행합니다.

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

    이 명령은 template 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다. -e 플래그는 현재 환경의 이름을 설정합니다. 환경에서 azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. 또한 Azure 만드는 리소스 이름의 일부이기도 합니다.

  1. 로컬 터미널 또는 명령 프롬프트에서 다음 azd init 명령을 실행합니다.

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

    이 명령은 template 리포지토리에서 프로젝트 파일을 끌어와 현재 폴더에서 프로젝트를 초기화합니다. -e 플래그는 현재 환경의 이름을 설정합니다. 환경에서 azd는 앱에 대한 고유한 배포 컨텍스트를 유지 관리하며 둘 이상을 정의할 수 있습니다. 또한 Azure 만드는 리소스 이름의 일부이기도 합니다.

스토리지 에뮬레이터 시작

Azurite 에뮬레이터를 사용하여 코드 프로젝트를 로컬로 실행할 때 Azure Storage 계정 연결을 시뮬레이션합니다.

  1. 아직 설치하지 않은 경우 Azurite를 설치합니다.

  2. F1 키를 누릅니다. 명령 팔레트에서 명령을 Azurite: Start 검색하고 실행하여 로컬 스토리지 에뮬레이터를 시작합니다.

로컬로 MCP 서버 실행

터미널 창에서 프로젝트 폴더로 FunctionsMcpTool 이동합니다.

cd src/FunctionsMcpTool

터미널 창에서 프로젝트 폴더로 FunctionsMcpTool 이동합니다.

cd samples/FunctionsMcpTool

Visual Studio Code Azure Functions Core 도구 통합하여 로컬 개발 컴퓨터에서 이 프로젝트를 실행할 수 있습니다. Functions 앱을 로컬로 시작하려면 F5 키를 누르거나 왼쪽 작업 표시줄에서 실행 및 디버그 아이콘을 선택합니다.

터미널 패널에는 Core Tools의 출력이 표시됩니다. 앱이 터미널 패널에서 시작되고 로컬로 실행되는 함수의 이름을 볼 수 있습니다.

GitHub Copilot 사용하여 확인

프로젝트 템플릿에는 .vscode/mcp.json 파일이 포함되어 있으며, 이는 로컬 MCP 엔드포인트를 가리키는 local-mcp-function 서버를 이미 정의합니다. 이 구성을 사용하여 Visual Studio Code GitHub Copilot 사용하여 코드를 확인합니다.

  1. .vscode/mcp.json 파일을 열고 구성 위의 local-mcp-function 단추를 선택합니다.

  2. Copilot Chat 창에서 Agent 모드가 선택되어 있는지 확인하고, Configure 도구 아이콘을 선택하고, 채팅에서 MCP Server:local-mcp-function 사용하도록 설정되어 있는지 확인합니다.

  3. 다음 프롬프트를 실행합니다.

    Say Hello
    

    도구를 실행하라는 메시지가 표시되면 권한을 계속 부여할 필요가 없도록 이 작업 영역에서 허용 을 선택합니다. 프롬프트가 실행되고 헬로 월드 응답 및 함수 실행 정보가 로그에 기록됩니다.

  4. 이제 프로젝트 파일 중 하나에서 일부 코드를 선택하고 다음 프롬프트를 실행합니다.

    Save this snippet as snippet1
    

    Copilot 코드 조각을 저장하고 getSnippets 도구를 사용하여 코드 조각을 검색하는 방법에 대한 정보를 사용하여 요청에 응답합니다. 로그에서 함수 실행을 다시 검토하고 saveSnippets 함수가 실행되었는지 확인할 수 있습니다.

  5. Copilot 채팅에서 다음 프롬프트를 실행합니다.

    Retrieve snippet1 and apply to NewFile
    

    Copilot 코드 조각을 검색하고, NewFile 파일에 추가하고, 코드 조각이 프로젝트에서 작동하도록 하는 데 필요한 다른 작업을 수행합니다. Functions 로그는 엔드포인트가 getSnippets 호출되었음을 보여 줍니다.

  6. 테스트를 마쳤으면 Ctrl+C를 눌러 Functions 호스트를 중지합니다.

코드 검토(선택 사항)

MCP 서버 도구를 정의하는 코드를 검토할 수 있습니다.

MCP 서버 도구에 대한 함수 코드는 폴더에 src 정의되어 있습니다. 이 특성은 McpToolTrigger 함수를 MCP 서버 도구로 노출합니다.

[Function(nameof(SayHello))]
public string SayHello(
    [McpToolTrigger(HelloToolName, HelloToolDescription)] ToolInvocationContext context
)
{
    logger.LogInformation("C# MCP tool trigger function processed a request.");
    return "Hello I am MCP Tool!";
}

public class SnippetsTool(ILogger<SnippetsTool> logger)
{
    private const string BlobPath = "snippets/{mcptoolargs.Name}.json";

    private static BlobServiceClient GetBlobServiceClient() =>
        new(Environment.GetEnvironmentVariable("AzureWebJobsStorage"));

    [Function(nameof(GetSnippet))]
    public Snippet? GetSnippet(
        [McpToolTrigger(GetSnippetToolName, GetSnippetToolDescription)]
            ToolInvocationContext context,
        [McpToolProperty(SnippetNamePropertyName, SnippetNamePropertyDescription, true)]
            string name,
        [BlobInput(BlobPath)] string? snippetContent
    )
    {
        if (snippetContent is null)
        {
            return null;
        }

        return new Snippet { Name = name, Content = snippetContent };
    }

전체 프로젝트 템플릿은 Azure Functions .NET MCP Server GitHub 리포지토리에서 볼 수 있습니다.

MCP 서버 도구에 대한 함수 코드는 폴더에 samples/FunctionsMcpTool/src/main/java/com/function/ 정의되어 있습니다. 주석은 @McpToolTrigger 함수를 MCP 서버 도구로 노출합니다.

@FunctionName("HelloWorld")
public String logCustomTriggerInput(
        @McpToolTrigger(
                name = "helloWorld",
                description = "Says hello and logs the messages that are provided.")
        McpToolInvocationContext mcpToolInvocationContext,
        @McpToolProperty(
            name = "messages",
            propertyType = "string",
            description = "The messages to be logged.",
            isRequired = true,
            isArray = true)
        String messages,
        final ExecutionContext functionExecutionContext
) {
@FunctionName("SaveSnippets")
@StorageAccount("AzureWebJobsStorage")
public String saveSnippet(
        @McpToolTrigger(
                name = "saveSnippets",
                description = "Saves a text snippet to your snippets collection.")
        String mcpToolInvocationContext,
        @McpToolProperty(
            name = SNIPPET_NAME_PROPERTY_NAME,
            propertyType = "string",
            description = "The name of the snippet.",
            isRequired = true)
        String snippetName,
        @McpToolProperty(
            name = SNIPPET_PROPERTY_NAME,
            propertyType = "string",
            description = "The content of the snippet.",
            isRequired = true)
        String snippet,
        @BlobOutput(name = "outputBlob", path = BLOB_PATH)
        OutputBinding<String> outputBlob,
        final ExecutionContext functionExecutionContext
) {
    // Log the entire incoming JSON for debugging
    functionExecutionContext.getLogger().info(mcpToolInvocationContext);

    // Log the snippet name and content
    functionExecutionContext.getLogger().info("Saving snippet with name: " + snippetName);
    functionExecutionContext.getLogger().info("Snippet content:\n" + snippet);

    // Write the snippet content to the output blob
    outputBlob.setValue(snippet);
    
    return "Successfully saved snippet '" + snippetName + "' with " + snippet.length() + " characters.";

전체 프로젝트 템플릿은 Azure Functions Java MCP Server GitHub 리포지토리에서 볼 수 있습니다.

MCP 서버 도구에 대한 함수 코드는 파일에 정의되어 있습니다 src/function_app.py . MCP 함수 주석은 이러한 함수를 MCP 서버 도구로 노출합니다.

@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.blob_input(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def get_snippet(file: func.InputStream, snippetname: str) -> str:
    """Retrieve a snippet by name from Azure Blob Storage."""
    snippet_content = file.read().decode("utf-8")
    logging.info(f"Retrieved snippet: {snippet_content}")
    return snippet_content
@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.mcp_tool_property(arg_name="snippet", description="The content of the snippet.")
@app.blob_output(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def save_snippet(file: func.Out[str], snippetname: str, snippet: str) -> str:
    """Save a snippet with a name to Azure Blob Storage."""
    if not snippetname:
        return "No snippet name provided"

    if not snippet:
        return "No snippet content provided"

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

Azure Functions Python MCP Server GitHub 리포지토리에서 전체 프로젝트 템플릿을 볼 수 있습니다.

MCP 서버 도구에 대한 함수 코드는 폴더에 mcp-tools/src 정의되어 있습니다. MCP 함수 등록은 이러한 함수를 MCP 서버 도구로 노출합니다.

// Hello function - responds with hello message
export async function mcpToolHello(_toolArguments: unknown, context: InvocationContext): Promise<string> {
    const mcptoolargs = context.triggerMetadata.mcptoolargs as {
        name?: string;
    };
    const name = mcptoolargs?.name;

    context.log(`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 responds with a hello message.',
    toolProperties: {
        name: arg.string().describe('Required property to identify the caller.').optional()
    },
    handler: mcpToolHello
});
// Register the GetSnippet tool
app.mcpTool("getSnippet", {
  toolName: GET_SNIPPET_TOOL_NAME,
  description: GET_SNIPPET_TOOL_DESCRIPTION,
  toolProperties: {
    [SNIPPET_NAME_PROPERTY_NAME]: arg.string().describe(SNIPPET_NAME_PROPERTY_DESCRIPTION)
  },
  extraInputs: [blobInputBinding],
  handler: getSnippet,
});

// Register the SaveSnippet tool
app.mcpTool("saveSnippet", {
  toolName: SAVE_SNIPPET_TOOL_NAME,
  description: SAVE_SNIPPET_TOOL_DESCRIPTION,
  toolProperties: {
    [SNIPPET_NAME_PROPERTY_NAME]: arg.string().describe(SNIPPET_NAME_PROPERTY_DESCRIPTION),
    [SNIPPET_PROPERTY_NAME]: arg.string().describe(SNIPPET_PROPERTY_DESCRIPTION)
  },
  extraOutputs: [blobOutputBinding],
  handler: saveSnippet,
});

전체 프로젝트 템플릿은 Azure Functions TypeScript MCP Server GitHub 리포지토리에서 볼 수 있습니다.

MCP 서버 도구를 로컬로 확인한 후 프로젝트를 Azure 게시할 수 있습니다.

Azure에 배포

이 프로젝트는 azd 사용하여 Azure Flex Consumption 계획의 새 함수 앱에 이 프로젝트를 배포하도록 구성됩니다. 이 프로젝트에는 azd가 모범 사례를 따르는 Flex 소비 계획에 대한 보안 배포를 만드는 데 사용하는 Bicep 파일들이 포함되어 있습니다.

  1. Visual Studio Code F1 키를 눌러 명령 팔레트를 엽니다. 명령 Azure Developer CLI (azd): Package, Provision and Deploy (up) 검색하고 실행합니다. 그런 다음 Azure 계정을 사용하여 로그인합니다.

  2. 메시지가 표시되면 다음 필수 배포 매개 변수를 선택합니다.

    매개 변수 Description
    Azure 구독 리소스가 만들어지는 구독.
    Azure 위치 새 Azure 리소스가 포함될 리소스 그룹을 만들 Azure 지역입니다. 현재 Flex 사용량 플랜을 지원하는 지역만 표시됩니다.
    vnetEnabled False 가상 네트워크 리소스 만들기를 건너뛰면 배포가 간소화됩니다.

    명령이 성공적으로 완료되면, 만든 리소스에 대한 링크가 표시됩니다.

원격 MCP 서버에 연결

이제 MCP 서버가 Azure 실행 중입니다. 프로젝트 템플릿에는 원격 서버에 연결하도록 이미 구성된 remote-mcp-function 항목이 .vscode/mcp.json에 포함되어 있습니다. 이 서버를 시작하면 VS Code에서 원격 MCP 엔드포인트에 액세스하는 데 필요한 함수 앱 이름 및 시스템 키를 묻는 메시지를 표시합니다.

  1. azd 및 Azure CLI 사용하여 도구에 액세스하는 데 필요한 함수 앱 이름과 시스템 키(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 "Function app name: %s\n" "$SERVICE_API_NAME"
    printf "MCP Server key: %s\n" "$MCP_EXTENSION_KEY"
    
  2. .vscode/mcp.json 구성 위의 시작을 선택합니다.

  3. 메시지가 표시되면 이전 단계의 함수 앱 이름 및 시스템 키 값을 입력합니다.

배포를 확인하십시오

이제 로컬에서 했던 것처럼 원격 MCP 도구를 GitHub Copilot 사용할 수 있지만 이제 코드는 Azure 안전하게 실행됩니다. 앞에서 사용한 것과 동일한 명령을 재생하여 모든 것이 올바르게 작동하는지 확인합니다.

자원을 정리하세요

MCP 서버 및 관련 리소스 작업을 마쳤으면 이 명령을 사용하여 Azure 함수 앱 및 관련 리소스를 삭제하여 추가 비용이 발생하지 않도록 합니다.

azd down 

다음 단계