다음을 통해 공유


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

이 빠른 시작에서는 Azure 개발자 CLI()를 사용하여 템플릿 프로젝트에서 사용자 지정 MCP(azd원격 모델 컨텍스트 프로토콜) 서버를 만듭니다. MCP 서버는 Azure Functions MCP 서버 확장을 사용하여 AI 모델, 에이전트 및 도우미에 대한 도구를 제공합니다. 프로젝트를 로컬로 실행하고 GitHub Copilot를 사용하여 코드를 확인한 후에는 안전하고 확장 가능한 배포에 대한 현재 모범 사례를 따르는 Azure Functions의 새 서버리스 함수 앱에 배포합니다.

팁 (조언)

또한 Functions를 사용하면 코드 프로젝트를 변경하지 않고도 Flex Consumption 계획 앱에 기존 MCP 서버 코드 프로젝트를 배포할 수 있습니다. 자세한 내용은 빠른 시작: Azure Functions에서 기존 MCP 서버 호스트를 참조하세요.

새 앱은 사용한 만큼 지불 청구 모델을 따르는 Flex 사용량 플랜에서 실행되므로 이 빠른 시작을 완료하면 Azure 계정에서 몇 센트 또는 그 미만의 소액 비용이 발생합니다.

중요합니다

사용자 지정 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: 서버 추가를 검색하고 실행합니다.

  2. 전송 유형에 대한 HTTP(Server-Sent 이벤트) 를 선택합니다.

  3. 이전 단계에서 복사한 MCP 엔드포인트의 URL을 입력합니다.

  4. 생성된 서버 ID 를 사용하고 작업 영역을 선택하여 MCP 서버 연결을 작업 영역 설정에 저장합니다.

  5. 명령 팔레트를 열고 MCP: 서버 나열 을 실행하고 추가한 서버가 나열되고 실행 중인지 확인합니다.

  6. 코필로트 채팅에서 에이전트 모드를 선택하고 다음 프롬프트를 실행합니다.

    Say Hello
    

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

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

    Save this snippet as snippet1
    

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

  8. 코필로트 채팅에서 다음 프롬프트를 실행합니다.

    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 서버 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 서버 GitHub 리포지토리에서 전체 프로젝트 템플릿을 볼 수 있습니다.

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

Azure에 배포

이 프로젝트는 azd up 명령을 사용하여 Azure의 Flex 사용량 플랜에서 새 함수 앱에 이 프로젝트를 배포하도록 구성되었습니다. 이 프로젝트에는 모범 사례를 따르는 Flex 사용 계획에 대한 보안 배포를 만드는 데 사용하는 Bicep 파일 azd 집합이 포함되어 있습니다.

  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 서버를 찾고, 값을 원격 MCP 서버 URL로 변경 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 서버를 다시 시작합니다.

배포를 확인하십시오

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

자원을 정리하세요

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

azd down --no-prompt

비고

--no-prompt 옵션은 사용자의 확인 없이 리소스 그룹을 삭제하도록 지시 azd 합니다. 이 명령은 로컬 코드 프로젝트에는 영향을 미치지 않습니다.

다음 단계