次の方法で共有


App Service アプリを GitHub Copilot Chat (.NET) 用 MCP サーバーとして統合する

このチュートリアルでは、モデル コンテキスト プロトコル (MCP) を使用して ASP.NET Core アプリの機能を公開し、GitHub Copilot にツールとして追加し、Copilot チャット エージェント モードで自然言語を使用してアプリと対話する方法について説明します。

Azure App Service でホストされている Todos MCP サーバーを呼び出す GitHub Copilot を示すスクリーンショット。

Web アプリケーションにショッピング、ホテル予約、データ管理などの便利な機能が既にある場合は、これらの機能を次の目的で簡単に利用できます。

MCP サーバーを Web アプリに追加することで、エージェントがユーザー プロンプトに応答したときにアプリの機能を理解して使用できるようになります。 つまり、アプリでできることは何でも、エージェントでも実行できます。

  • MCP サーバーを Web アプリに追加します。
  • GitHub Copilot Chat エージェント モードで MCP サーバーをローカルでテストします。
  • MCP サーバーを Azure App Service にデプロイし、GitHub Copilot Chat でそれに接続します。

[前提条件]

このチュートリアルでは、「 チュートリアル: ASP.NET Core と Azure SQL Database アプリを Azure App Service にデプロイする」で使用するサンプルを使用していることを前提としています。

少なくとも、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の機能を複製していますが、これは不要ですが、わかりやすくするために保持します。 ベスト プラクティスは、アプリ ロジックをサービス クラスに移動してから、 TodosControllerTodosMcpTool の両方からサービス メソッドを呼び出す方法です。

  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 チャット エージェント モードで MCP サーバーを追加する方法を示すスクリーンショット。

  5. [ MCP サーバーの追加] を選択します

  6. HTTP (HTTP または Server-Sent イベント) を選択します。

  7. [ サーバー URL の入力] に「 http://localhost:5093/api/mcp」と入力します。

  8. [ サーバー ID の入力] に、 todos-mcp または任意の名前を入力します。

  9. [ ワークスペースの設定] を選択します

  10. 新しい Copilot Chat ウィンドウで、"ToDo を見せて" のような内容を入力します。

  11. 既定では、MCP サーバーを呼び出すと、GitHub Copilot にセキュリティ確認が表示されます。 続行を選択します。

    GitHub Copilot Chat での MCP 呼び出しからの既定のセキュリティ メッセージを示すスクリーンショット。

    MCP ツールの呼び出しが成功したことを示す応答が表示されます。

    GitHub Copilot チャット ウィンドウの MCP ツール呼び出しからの応答を示すスクリーンショット。

MCP サーバーを App Service にデプロイする

  1. codespace ターミナルに戻り、変更をコミットして変更をデプロイするか (GitHub Actions メソッド)、または azd up (Azure Developer 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 App Service へのセキュリティで保護されたモデル コンテキスト プロトコルの呼び出し に関するページを参照してください。
  • 入力の検証とサニタイズ: このチュートリアルのコード例では、入力の検証とサニタイズを省略して、わかりやすくします。 運用環境のシナリオでは、アプリケーションを保護するために、常に適切な検証とサニタイズを実装します。 ASP.NET Core については、「 ASP.NET Core でのモデルの検証」を参照してください。
  • HTTPS: このサンプルは、既定で HTTPS を適用し、転送中のデータを暗号化するための無料の TLS/SSL 証明書を提供する Azure App Service に依存しています。
  • 最小限の特権の原則: ユース ケースに必要なツールとデータのみを公開します。 必要な場合を除き、機密性の高い操作を公開しないでください。
  • レート制限と調整: API Management またはカスタム ミドルウェアを使用して、不正使用やサービス拒否攻撃を防ぎます。
  • ログ記録と監視: 監査と異常検出のための MCP エンドポイントのログ アクセスと使用状況。 疑わしいアクティビティを監視します。
  • CORS 構成: MCP サーバーがブラウザーからアクセスされる場合は、クロスオリジン要求を信頼されたドメインに制限します。 詳細については、「 CORS を有効にする」を参照してください。
  • 定期的な更新: 既知の脆弱性を軽減するために、依存関係を最新の状態に保ちます。

その他のリソース

AI を Azure App Service アプリケーションに統合する