教學:部署 .NET MCP 伺服器到 Azure 容器應用程式

在本教學中,你將建立一個模型情境協定(MCP)伺服器,透過 ASP.NET Core 與 ModelContextProtocol.AspNetCore NuGet 套件來揭露任務管理工具。 你要把伺服器部署到 Azure 容器應用程式,然後用 VS Code 從 GitHub Copilot Chat 連接。

在本教學課程中,您會:

  • 建立一個 ASP.NET 核心應用程式,公開 MCP 工具
  • 用 GitHub Copilot 在本地測試 MCP 伺服器
  • 將應用程式容器化並部署至 Azure 容器應用程式
  • 將 GitHub Copilot 連接到已部署的 MCP 伺服器

先決條件

建立應用程式架構

在這個區段,你會建立一個新的 ASP.NET Core 專案,並將其配置為 MCP 伺服器。

  1. 建立一個新的 ASP.NET 核心網頁API專案:

    dotnet new web -n TasksMcpServer
    cd TasksMcpServer
    
  2. 新增 MCP 伺服器 NuGet 套件:

    dotnet add package ModelContextProtocol.AspNetCore --prerelease
    
  3. 以下列程式碼取代 Program.cs 的內容:

    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddMcpServer()
        .WithHttpTransport()
        .WithToolsFromAssembly();
    
    builder.Services.AddCors(options =>
    {
        options.AddDefaultPolicy(policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
    
    builder.Services.AddSingleton<TaskStore>();
    
    var app = builder.Build();
    
    app.UseCors();
    
    app.MapGet("/health", () => Results.Ok("healthy"));
    app.MapMcp("/mcp");
    
    app.Run();
    

    重點︰

    • AddMcpServer().WithHttpTransport().WithToolsFromAssembly() 註冊 MCP 伺服器並發現所有標記為 [McpServerToolType]的類別。
    • MapMcp("/mcp") 掛載可串流的 HTTP 端點於 /mcp
    • AddSingleton<TaskStore>() 註冊你在下一節建立的記憶體資料儲存。
    • 為 Azure 容器應用程式 的健康探測器單獨新增 /health 端點。 MCP 端點回傳的 JSON-RPC 回應不適合作為健康檢查。
    • 你啟用 CORS 是因為 GitHub Copilot 在 VS Code 中會對 MCP 伺服器發出跨來源請求。

定義 MCP 工具

接著,定義任務管理資料庫及將資料暴露給 AI 用戶端的 MCP 工具。

  1. 建立一個以記憶體內資料儲存命名 TaskStore.cs 的檔案:

    namespace TasksMcpServer;
    
    public record TaskItem(int Id, string Title, string Description, bool IsComplete, DateTime CreatedAt);
    
    public class TaskStore
    {
        private readonly List<TaskItem> _tasks = new()
        {
            new(1, "Buy groceries", "Milk, eggs, bread", false, DateTime.UtcNow),
            new(2, "Write docs", "Draft the MCP tutorial", true, DateTime.UtcNow.AddDays(-1)),
        };
    
        private int _nextId = 3;
    
        public List<TaskItem> GetAll() => _tasks.ToList();
    
        public TaskItem? GetById(int id) => _tasks.FirstOrDefault(t => t.Id == id);
    
        public TaskItem Create(string title, string description)
        {
            var task = new TaskItem(_nextId++, title, description, false, DateTime.UtcNow);
            _tasks.Add(task);
            return task;
        }
    
        public TaskItem? ToggleComplete(int id)
        {
            var index = _tasks.FindIndex(t => t.Id == id);
            if (index < 0) return null;
            var old = _tasks[index];
            var updated = old with { IsComplete = !old.IsComplete };
            _tasks[index] = updated;
            return updated;
        }
    
        public bool Delete(int id)
        {
            var task = _tasks.FirstOrDefault(t => t.Id == id);
            if (task is null) return false;
            _tasks.Remove(task);
            return true;
        }
    }
    

    TaskItem 紀錄定義了資料模型,包含五個屬性。 該 TaskStore 類別管理一個預先填充範例資料的記憶體清單,並提供列出、查找、建立、切換及刪除任務的方法。

  2. 建立一個以 MCP 工具定義命名 TasksMcpTools.cs 的檔案:

    using System.ComponentModel;
    using ModelContextProtocol.Server;
    
    namespace TasksMcpServer;
    
    [McpServerToolType]
    public class TasksMcpTools
    {
        private readonly TaskStore _store;
    
        public TasksMcpTools(TaskStore store)
        {
            _store = store;
        }
    
        [McpServerTool, Description("Lists all tasks with their ID, title, description, and completion status.")]
        public List<TaskItem> ListTasks()
        {
            return _store.GetAll();
        }
    
        [McpServerTool, Description("Gets a single task by its ID.")]
        public TaskItem? GetTask(
            [Description("The numeric ID of the task to retrieve")] int id)
        {
            return _store.GetById(id);
        }
    
        [McpServerTool, Description("Creates a new task with the given title and description. Returns the created task.")]
        public TaskItem CreateTask(
            [Description("A short title for the task")] string title,
            [Description("A detailed description of what the task involves")] string description)
        {
            return _store.Create(title, description);
        }
    
        [McpServerTool, Description("Toggles a task's completion status between complete and incomplete.")]
        public string ToggleTaskComplete(
            [Description("The numeric ID of the task to toggle")] int id)
        {
            var task = _store.ToggleComplete(id);
            return task is not null
                ? $"Task {task.Id} is now {(task.IsComplete ? "complete" : "incomplete")}."
                : $"Task with ID {id} not found.";
        }
    
        [McpServerTool, Description("Deletes a task by its ID.")]
        public string DeleteTask(
            [Description("The numeric ID of the task to delete")] int id)
        {
            return _store.Delete(id)
                ? $"Task {id} deleted."
                : $"Task with ID {id} not found.";
        }
    }
    

    屬性 [McpServerToolType] 標記該類別為 MCP 工具提供者。 每個 [McpServerTool] 方法都成為可調用的工具。 利用 [Description] 屬性幫助 AI 模型理解每個工具的目的與參數。

在本機測試 MCP 伺服器

在部署到 Azure 之前,先透過本地運行 MCP 伺服器並從 GitHub Copilot 連接來確認它正常運作。

  1. 執行應用程式:

    dotnet run
    

    伺服器起始於( http://localhost:5000 或控制台輸出中顯示的埠口)。 MCP 端點位於 http://localhost:5000/mcp

  2. 打開 VS Code,然後打開 Copilot 聊天 ,選擇 Agent 模式。

  3. 選擇 工具 按鈕,然後選擇 新增更多工具...>新增 MCP 伺服器

  4. 選取HTTP(HTTP 或 Server-Sent 事件)

  5. 輸入伺服器網址: http://localhost:5000/mcp

  6. 輸入伺服器 ID: tasks-mcp

  7. 選取 [工作區設定]。

  8. 在新的 Copilot 聊天提示中,輸入 :「Show me all tasks」

  9. GitHub Copilot 在啟動 MCP 工具前會顯示確認訊息。 選取繼續

你應該會看到 Copilot 從你的暫存中回傳任務列表。

小提示

試試其他提示,例如「建立任務以檢視 PR」、「標記任務 1 為完成」或「刪除任務 2」。

將應用程式容器化

把應用程式打包成 Docker 容器,這樣你可以在本地測試再部署到 Azure。

  1. 在專案根建立一個 Dockerfile

    FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
    WORKDIR /src
    COPY *.csproj .
    RUN dotnet restore
    COPY . .
    RUN dotnet publish -c Release -o /app
    
    FROM mcr.microsoft.com/dotnet/aspnet:8.0
    WORKDIR /app
    COPY --from=build /app .
    ENV ASPNETCORE_URLS=http://+:8080
    EXPOSE 8080
    ENTRYPOINT ["dotnet", "TasksMcpServer.dll"]
    

    多階段建置會使用 SDK 映像來還原、建置並發佈應用程式,然後只將已發佈的輸出複製到較小的 ASP.NET 執行時映像檔。 ASPNETCORE_URLS環境變數會設定應用程式在 8080 埠監聽。

  2. 確認容器是否在本地建置並執行:

    docker build -t tasks-mcp-server .
    docker run -p 8080:8080 tasks-mcp-server
    

    確認健全狀態端點回覆:curl http://localhost:8080/health

部署至 Azure 容器應用程式

在你將應用程式容器化後,再用 Azure CLI 部署到 Azure 容器應用程式。 這個 az containerapp up 指令會在雲端建立容器映像,所以你不需要在機器上安裝 Docker。

  1. 設定環境變數:

    RESOURCE_GROUP="mcp-tutorial-rg"
    LOCATION="eastus"
    ENVIRONMENT_NAME="mcp-env"
    APP_NAME="tasks-mcp-server"
    
  2. 建立資源群組:

    az group create --name $RESOURCE_GROUP --location $LOCATION
    
  3. 建立容器應用程式環境:

    az containerapp env create \
        --name $ENVIRONMENT_NAME \
        --resource-group $RESOURCE_GROUP \
        --location $LOCATION
    
  4. 部署容器應用程式:

    az containerapp up \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --environment $ENVIRONMENT_NAME \
        --source . \
        --ingress external \
        --target-port 8080
    
  5. 設定 CORS 以允許 GitHub Copilot 請求:

    az containerapp ingress cors enable \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --allowed-origins "*" \
        --allowed-methods "GET,POST,DELETE,OPTIONS" \
        --allowed-headers "*"
    

    備註

    在生產環境中,將萬用字元 * 來源替換為特定的可信來源。 請參閱 容器應用程式上的安全 MCP 伺服器 以獲得指引。

  6. 驗證部署:

    APP_URL=$(az containerapp show \
        --name $APP_NAME \
        --resource-group $RESOURCE_GROUP \
        --query "properties.configuration.ingress.fqdn" -o tsv)
    
    curl https://$APP_URL/health
    

將 GitHub Copilot 連接到已部署的伺服器

現在 MCP 伺服器在 Azure 運行,設定 VS Code 將 GitHub Copilot 連接到已部署的端點。

  1. 在您的專案中,建立或更新 .vscode/mcp.json

    {
        "servers": {
            "tasks-mcp-server": {
                "type": "http",
                "url": "https://<your-app-fqdn>/mcp"
            }
        }
    }
    

    用部署輸出的 FQDN 替換 <your-app-fqdn>

  2. 在 VS Code 中,以客服模式開啟 Copilot 聊天。

  3. 如果伺服器沒有自動出現,請點選 工具 按鈕並確認 tasks-mcp-server 是否被列出。 如果需要,選擇 開始

  4. 「列出我所有任務」 的提示來測試,確認已部署的 MCP 伺服器有回應。

為互動使用設定縮放

預設情況下,Azure 容器應用程式可以擴展到零複本。 對於服務互動客戶端如 Copilot 的 MCP 伺服器,冷啟動會造成明顯延遲。 設定最小副本數量,以維持至少一個實例在運行:

az containerapp update \
    --name $APP_NAME \
    --resource-group $RESOURCE_GROUP \
    --min-replicas 1

安全性考慮

本教學使用未認證的 MCP 伺服器以簡化操作。 在正式環境中運行 MCP 伺服器前,請先檢視以下建議。 當由大型語言模型 (LLM) 技術支援的 Agent 呼叫您的 MCP 伺服器時,請注意提示注入攻擊。

  • 認證與授權:使用 Microsoft Entra ID 保護你的 MCP 伺服器。 請參閱 容器應用程式上的 Secure MCP 伺服器
  • 輸入驗證:務必驗證工具參數。 在 ASP.NET Core 中使用資料註解或 FluentValidation。 請參見 ASP.NET Core 中的模型驗證
  • HTTPS:Azure 容器應用程式預設會強制執行 HTTPS,並自動使用 TLS 憑證。
  • 最低權限:只公開你使用情境所需的工具。 避免使用未經確認就進行破壞性操作的工具。
  • CORS:在執行環境中只允許受信任的網域作為來源。
  • 日誌與監控:記錄 MCP 工具的調用以進行稽核。 使用 Azure 監視器 和日誌分析。

清理資源

如果你不打算繼續使用這個應用程式,請刪除資源群組,移除你在教學中建立的所有資源:

az group delete --resource-group $RESOURCE_GROUP --yes --no-wait

下一個步驟