你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

将应用服务应用集成为用于 GitHub Copilot 对话助手的 MCP 服务器 (.NET)

本教程介绍如何通过模型上下文协议(MCP)公开 ASP.NET 核心应用的功能,将其作为工具添加到 GitHub Copilot,并在 Copilot Chat 代理模式下使用自然语言与应用交互。

显示 GitHub Copilot 调用 Azure 应用服务中托管的 Todos MCP 服务器的屏幕截图。

如果 Web 应用程序已具有有用的功能(如购物、酒店预订或数据管理),则可以轻松地使这些功能可供以下功能使用:

通过将 MCP 服务器添加到 Web 应用,可以在代理响应用户提示时了解和使用应用的功能。 这意味着你的应用可以执行的任何操作,智能助手也可以执行。

  • 将 MCP 服务器添加到 Web 应用。
  • 在 GitHub Copilot Chat 代理模式下本地测试 MCP 服务器。
  • 将 MCP 服务器部署到 Azure 应用服务,并在 GitHub Copilot Chat 中连接到它。

先决条件

本教程假定你使用的是教程中使用的示例 :将 ASP.NET Core 和 Azure SQL 数据库应用部署到 Azure 应用服务

至少,在 GitHub Codespaces 中打开 示例应用程序 ,并通过运行 azd up来部署应用。

将 MCP 服务器添加到 Web 应用

  1. 在 codespace 终端中,将 NuGet ModelContextProtocol.AspNetCore 包添加到项目:

    dotnet add package ModelContextProtocol.AspNetCore --prerelease
    
  2. 创建 McpServer 文件夹,并使用以下代码在其中创建 TodosMcpTool.cs

    using DotNetCoreSqlDb.Data;
    using DotNetCoreSqlDb.Models;
    using Microsoft.EntityFrameworkCore;
    using System.ComponentModel;
    using ModelContextProtocol.Server;
    
    namespace DotNetCoreSqlDb.McpServer
    {
        [McpServerToolType]
        public class TodosMcpTool
        {
            private readonly MyDatabaseContext _db;
    
            public TodosMcpTool(MyDatabaseContext db)
            {
                _db = db;
            }
    
            [McpServerTool, Description("Creates a new todo with a description and creation date.")]
            public async Task<string> CreateTodoAsync(
                [Description("Description of the todo")] string description,
                [Description("Creation date of the todo")] DateTime createdDate)
            {
                var todo = new Todo
                {
                    Description = description,
                    CreatedDate = createdDate
                };
                _db.Todo.Add(todo);
                await _db.SaveChangesAsync();
                return $"Todo created: {todo.Description} (Id: {todo.ID})";
            }
    
            [McpServerTool, Description("Reads all todos, or a single todo if an id is provided.")]
            public async Task<List<Todo>> ReadTodosAsync(
                [Description("Id of the todo to read (optional)")] string? id = null)
            {
                if (!string.IsNullOrWhiteSpace(id) && int.TryParse(id, out int todoId))
                {
                    var todo = await _db.Todo.FindAsync(todoId);
                    if (todo == null) return new List<Todo>();
                    return new List<Todo> { todo };
                }
                var todos = await _db.Todo.OrderBy(t => t.ID).ToListAsync();
                return todos;
            }
    
            [McpServerTool, Description("Updates the specified todo fields by id.")]
            public async Task<string> UpdateTodoAsync(
                [Description("Id of the todo to update")] string id,
                [Description("New description (optional)")] string? description = null,
                [Description("New creation date (optional)")] DateTime? createdDate = null)
            {
                if (!int.TryParse(id, out int todoId))
                    return "Invalid todo id.";
                var todo = await _db.Todo.FindAsync(todoId);
                if (todo == null) return $"Todo with Id {todoId} not found.";
                if (!string.IsNullOrWhiteSpace(description)) todo.Description = description;
                if (createdDate.HasValue) todo.CreatedDate = createdDate.Value;
                await _db.SaveChangesAsync();
                return $"Todo {todo.ID} updated.";
            }
    
            [McpServerTool, Description("Deletes a todo by id.")]
            public async Task<string> DeleteTodoAsync(
                [Description("Id of the todo to delete")] string id)
            {
                if (!int.TryParse(id, out int todoId))
                    return "Invalid todo id.";
                var todo = await _db.Todo.FindAsync(todoId);
                if (todo == null) return $"Todo with Id {todoId} not found.";
                _db.Todo.Remove(todo);
                await _db.SaveChangesAsync();
                return $"Todo {todo.ID} deleted.";
            }
        }
    }
    

    上述代码使用以下特定属性为 MCP 服务器提供工具:

    • [McpServerToolType]:将 TodosMcpTool 类标记为 MCP 服务器工具类型。 它向 MCP 框架发出信号,表明此类包含应作为可调用工具公开的方法。
    • [McpServerTool]:将方法标记为 MCP 服务器的可调用作。
    • [Description]:这些为方法和参数提供人工可读的说明。 它可以帮助调用代理了解如何来使用操作及其参数。

    此代码复制了现有 TodosController代码的功能,这是不必要的,但为了简单起见,你将保留它。 最佳做法是将应用逻辑移到服务类,然后既从 TodosController 调用服务方法,又从 TodosMcpTool 调用服务方法。

  3. Program.cs中,注册 MCP 服务器服务和 CORS 服务。

    builder.Services.AddMcpServer()
        .WithHttpTransport() // With streamable HTTP
        .WithToolsFromAssembly(); // Add all classes marked with [McpServerToolType]
    
    builder.Services.AddCors(options =>
    {
        options.AddDefaultPolicy(policy =>
        {
            policy.AllowAnyOrigin()
                  .AllowAnyHeader()
                  .AllowAnyMethod();
        });
    });
    

    MCP 服务器上使用可流式传输的 HTTP 时,如果要使用客户端浏览器工具或 GitHub Copilot(在 Visual Studio Code 和 GitHub Codespaces 中)进行测试,则需要启用跨域资源共享(CORS)。

  4. Program.cs中,启用 MCP 和 CORS 中间件。

    app.MapMcp("/api/mcp");
    app.UseCors();
    

    此代码将 MCP 服务器终结点设置为 <url>/api/mcp

在本地测试 MCP 服务器

  1. 在 codespace 终端中,使用 dotnet run.. 运行应用程序。

  2. 浏览器中选择“打开”,然后添加任务。

    保持 dotnet run 运行状态。 现在,您的 MCP 服务器正在 http://localhost:5093/api/mcp 运行。

  3. 返回代码空间,打开 Copilot Chat,然后在提示框中选择 代理 模式。

  4. 选择 “工具 ”按钮,然后在下拉列表中选择“ 添加更多工具...”

    显示如何在 GitHub Copilot Chat 代理模式下添加 MCP 服务器的屏幕截图。

  5. 选择 “添加 MCP 服务器”。

  6. 选择 HTTP(HTTP 或服务器发送的事件)

  7. Enter 服务器 URL 中,键入 http://localhost:5093/api/mcp

  8. Enter 服务器 ID 中,键入 todos-mcp 或任何喜欢的名称。

  9. 选择 工作区设置

  10. 在新的 Copilot 对话助手窗口中,键入类似于“显示待办事项”的内容。

  11. 默认情况下,GitHub Copilot 在调用 MCP 服务器时会显示安全确认。 选择继续

    显示 GitHub Copilot Chat 中 MCP 调用的默认安全消息的屏幕截图。

    现在应会看到一个响应,指示 MCP 工具调用成功。

    屏幕截图显示了在 GitHub Copilot Chat 窗口中调用 MCP 工具时的响应。

将 MCP 服务器部署到应用服务

  1. 返回 codespace 终端,通过提交更改(GitHub Actions 方法)或运行 azd up (Azure 开发人员 CLI 方法)来部署更改。

  2. 在 AZD 输出中,找到应用的 URL。 该 URL 在 AZD 输出中如下所示:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. azd up 完成后,打开 .vscode/mcp.json。 将 URL 更改为 <app-url>/api/mcp

  4. 在修改后的 MCP 服务器配置上方,选择“ 开始”。

    显示如何从本地 mcp.json 文件手动启动 MCP 服务器的屏幕截图。

  5. 启动新的 GitHub Copilot 聊天窗口。 你应该能够在 Copilot 代理中查看、创建、更新和删除任务。

安全最佳做法

当 MCP 服务器由由大型语言模型(LLM)提供支持的代理调用时,请注意 提示注入 攻击。 请考虑以下安全最佳做法:

  • 身份验证和授权:使用 Microsoft Entra 身份验证保护 MCP 服务器,以确保只有经过授权的用户或代理才能访问工具。 有关分步指南,请参阅 使用 Microsoft Entra 身份验证通过 Visual Studio Code 对 Azure 应用服务的安全模型上下文协议调用
  • 输入验证和清理:本教程中的示例代码省略输入验证和清理,以便简单明了。 在生产方案中,始终实现适当的验证和清理来保护应用程序。 有关 ASP.NET Core,请参阅 ASP.NET Core 中的模型验证
  • HTTPS: 此示例依赖于 Azure 应用服务,该服务默认强制实施 HTTPS,并提供免费的 TLS/SSL 证书来加密传输中的数据。
  • 最低特权原则:仅公开用例所需的工具和数据。 除非必要,否则避免公开敏感操作。
  • 速率限制和限制:使用 API 管理 或自定义中间件来防止滥用和拒绝服务攻击。
  • 日志记录和监视:用于审核和异常检测的 MCP 终结点的日志访问和使用情况。 监视可疑活动。
  • CORS 配置:如果从浏览器访问 MCP 服务器,请将跨域请求限制为受信任的域。 有关详细信息,请参阅 “启用 CORS”。
  • 常规更新:使依赖项保持最新,以缓解已知漏洞。

更多资源

将 AI 集成到 Azure 应用服务应用程序中