Sdílet prostřednictvím


Integrace aplikace App Service jako serveru MCP pro GitHub Copilot Chat (Node.js)

V tomto kurzu se dozvíte, jak zpřístupnit funkce aplikace Express.js prostřednictvím protokolu MCP (Model Context Protocol), přidat ho jako nástroj do GitHub Copilotu a pracovat s aplikací pomocí přirozeného jazyka v režimu agenta Copilot Chat.

Snímek obrazovky znázorňující GitHub Copilot volající server Todos MCP hostovaný ve službě Azure App Service

Pokud už vaše webová aplikace obsahuje užitečné funkce, jako jsou nákupy, rezervace hotelů nebo správa dat, je snadné tyto funkce zpřístupnit pro:

Přidáním serveru MCP do webové aplikace povolíte agentovi pochopit a používat funkce vaší aplikace, když reaguje na výzvy uživatele. To znamená, že cokoli může vaše aplikace dělat, agent může také provádět.

  • Přidejte server MCP do webové aplikace.
  • Otestujte server MCP místně v režimu agenta chatu GitHub Copilot.
  • Nasaďte server MCP do služby Azure App Service a připojte se k němu v chatu GitHub Copilot.

Prerequisites

Tento kurz předpokládá, že pracujete s ukázkou použitou v kurzu: Nasazení webové aplikace Node.js + MongoDB do Azure.

Alespoň otevřete ukázkovou aplikaci v GitHub Codespaces a nasaďte aplikaci spuštěním azd up.

Přidání serveru MCP do webové aplikace

  1. V terminálu codespace přidejte do projektu požadované balíčky npm:

    npm install @modelcontextprotocol/sdk@latest zod
    
    1. Otevřete trasy/index.js. Pro zjednodušení scénáře sem přidáte veškerý kód serveru MCP.
  2. Na začátek tras/index.js přidejte následující požadavky:

    const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
    const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
    const { z } = require('zod');
    
  3. Ve spodní části souboru, nad module.exports = router;, přidejte následující trasu pro server MCP.

    router.post('/api/mcp', async function(req, res, next) {
      try {
        // Stateless server instance for each request
        const server = new McpServer({
          name: "task-crud-server", 
          version: "1.0.0"
        });
    
        // Register tools
        server.registerTool(
          "create_task",
          {
            description: 'Create a new task',
            inputSchema: { taskName: z.string().describe('Name of the task to create') },
          },
          async ({ taskName }) => {
            const task = new Task({
              taskName: taskName,
              createDate: new Date(),
            });
            await task.save();
            return { content: [ { type: 'text', text: `Task created: ${JSON.stringify(task)}` } ] };
          }
        );
    
        server.registerTool(
          "get_tasks",
          {
            description: 'Get all tasks'
          },
          async () => {
            const tasks = await Task.find();
            return { content: [ { type: 'text', text: `All tasks: ${JSON.stringify(tasks, null, 2)}` } ] };
          }
        );
    
        server.registerTool(
          "get_task",
          {
            description: 'Get a task by ID',
            inputSchema: { id: z.string().describe('Task ID') },
          },
          async ({ id }) => {
            try {
              const task = await Task.findById(id);
              if (!task) {
                  throw new Error();
              }
              return { content: [ { type: 'text', text: `Task: ${JSON.stringify(task)}` } ] };
            } catch (error) {
                return { content: [ { type: 'text', text: `Task not found with ID: ${id}` } ], isError: true };
            }
          }
        );
    
        server.registerTool(
          "update_task",
          {
            description: 'Update a task',
            inputSchema: {
              id: z.string().describe('Task ID'),
              taskName: z.string().optional().describe('New task name'),
              completed: z.boolean().optional().describe('Task completion status')
            },
          },
          async ({ id, taskName, completed }) => {
            try {
              const updateData = {};
              if (taskName !== undefined) updateData.taskName = taskName;
              if (completed !== undefined) {
                updateData.completed = completed;
                if (completed === true) {
                  updateData.completedDate = new Date();
                }
              }
    
              const task = await Task.findByIdAndUpdate(id, updateData);
              if (!task) {
                throw new Error();
              }
              return { content: [ { type: 'text', text: `Task updated: ${JSON.stringify(task)}` } ] };
            } catch (error) {
              return { content: [ { type: 'text', text: `Task not found with ID: ${id}` } ], isError: true };
            }
          }
        );
    
        server.registerTool(
          "delete_task",
          {
            description: 'Delete a task',
            inputSchema: { id: z.string().describe('Task ID to delete') },
          },
          async ({ id }) => {
            try {
              const task = await Task.findByIdAndDelete(id);
              if (!task) {
                throw new Error();
              }
              return { content: [ { type: 'text', text: `Task deleted successfully: ${JSON.stringify(task)}` } ] };
            } catch (error) {
              return { content: [ { type: 'text', text: `Task not found with ID: ${id}` } ], isError: true };
            }
          }
        );
    
        // Create fresh transport for this request
        const transport = new StreamableHTTPServerTransport({
          sessionIdGenerator: undefined,
        });
    
        // Clean up when request closes
        res.on('close', () => {
          transport.close();
          server.close();
        });
    
        await server.connect(transport);
        await transport.handleRequest(req, res, req.body);
    
      } catch (error) {
        console.error('Error handling MCP request:', error);
        if (!res.headersSent) {
          res.status(500).json({
            jsonrpc: '2.0',
            error: {
              code: -32603,
              message: 'Internal server error',
            },
            id: null,
          });
        }
      }
    });
    

    Tato trasa nastaví koncový bod serveru MCP na a použije vzorec bezstavového režimu v sadě MCP TypeScript SDK.

    • server.registerTool() přidá nástroj k serveru MCP s implementací.
    • Sada SDK používá pro ověření vstupu zod .
    • description v objektu konfigurace a describe() v inputSchema poskytují lidmi čitelné popisy pro nástroje a vstupy. Pomáhají volajícímu agentu pochopit, jak používat nástroje a jejich parametry.

    Tato trasa duplikuje funkci cruD (create-read-update-delete) existujících tras, což není nutné, ale kvůli jednoduchosti ji zachováte. Osvědčeným postupem je přesunout logiku aplikace do modulu a pak volat modul ze všech tras.

Místní testování serveru MCP

  1. V terminálu codespace spusťte aplikaci pomocí npm startpříkazu .

  2. Vyberte Otevřít v prohlížeči a přidejte úkol.

    Nechejte npm start běžet. Váš server MCP teď běží http://localhost:3000/api/mcp .

  3. Zpátky v codespace otevřete Copilot Chat a pak v poli výzvy vyberte režim agenta .

  4. Vyberte tlačítko Nástroje a pak v rozevíracím seznamu vyberte Přidat další nástroje.

    Snímek obrazovky znázorňující, jak přidat server MCP v režimu agenta chatu GitHub Copilot

  5. Vyberte Přidat server MCP.

  6. Vyberte HTTP (HTTP nebo Server-Sent události).

  7. V Zadejte adresu URL serveru zadejte http://localhost:3000/api/mcp.

  8. Do pole Zadejte ID serveru zadejte todos-mcp nebo libovolný název, který se vám líbí.

  9. Vyberte Nastavení pracovního prostoru.

  10. V novém okně chatu Copilot zadejte něco jako "Ukaž mi úkoly."

  11. GitHub Copilot ve výchozím nastavení zobrazí potvrzení zabezpečení při vyvolání serveru MCP. Zvolte Pokračovat.

    Snímek obrazovky znázorňující výchozí zprávu zabezpečení z volání MCP v chatu GitHub Copilot

    Teď byste měli vidět odpověď, která značí, že volání nástroje MCP proběhlo úspěšně.

    Snímek obrazovky ukazující, že odpověď z volání nástroje MCP je zobrazena v okně chatu GitHub Copilot

Nasazení serveru MCP do služby App Service

  1. Zpátky v terminálu codespace nasaďte změny potvrzením změn (metoda GitHub Actions) nebo spuštěním azd up (metoda Azure Developer CLI).

  2. Ve výstupu AZD vyhledejte adresu URL vaší aplikace. Adresa URL vypadá takto ve výstupu AZD:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Po azd up dokončení otevřete soubor .vscode/mcp.json. Změňte adresu URL na <app-url>/api/mcp.

  4. Nad upravenou konfigurací serveru MCP vyberte Spustit.

    Snímek obrazovky znázorňující ruční spuštění serveru MCP z místního souboru mcp.json

  5. Spusťte nové okno GitHub Copilot Chatu. V agentu Copilot byste měli být schopni zobrazovat, vytvářet, aktualizovat a odstraňovat úlohy.

Osvědčené postupy zabezpečení

Když je server MCP volán agentem využívajícím velké jazykové modely (LLM), mějte na paměti útoky injekce promptu. Zvažte následující osvědčené postupy zabezpečení:

  • Ověřování a autorizace: Zabezpečte server MCP pomocí ověřování Microsoft Entra, abyste zajistili, že k vašim nástrojům mají přístup jenom autorizovaní uživatelé nebo agenti. Podrobné pokyny najdete v tématu Volání protokolu Zabezpečeného modelového kontextu do služby Azure App Service ze sady Visual Studio Code s ověřováním Microsoft Entra.
  • Ověřování vstupu a sanitizace: Ukázkový kód v tomto kurzu používá pro ověřování vstupu zod a zajišťuje, aby příchozí data odpovídala očekávanému schématu. Pokud chcete další zabezpečení, zvažte následující:
    • Ověřování a sanitizace veškerého uživatelského vstupu před zpracováním, zejména pro pole použitá v databázových dotazech nebo výstupu.
    • Únik výstupu v odpovědích, aby se zabránilo skriptování mezi weby (XSS), pokud vaše API je spotřebováváno prohlížeči.
    • Použití striktních schémat a výchozích hodnot v modelech, abyste se vyhnuli neočekávaným datům.
  • HTTPS: Ukázka spoléhá na službu Azure App Service, která ve výchozím nastavení vynucuje HTTPS a poskytuje bezplatné certifikáty TLS/SSL k šifrování přenášených dat.
  • Princip nejnižších oprávnění: Zpřístupněte pouze potřebné nástroje a data potřebná pro váš případ použití. Vyhněte se zveřejnění citlivých operací, pokud to není nutné.
  • Omezování rychlosti a omezování: Použití služby API Management nebo vlastního middlewaru k prevenci zneužití a útoků na dostupnost služby.
  • Protokolování a monitorování: Přístup k protokolům a využití koncových bodů MCP pro auditování a detekci anomálií Monitorování podezřelých aktivit
  • Konfigurace CORS: Omezte požadavky mezi zdroji na důvěryhodné domény, pokud je váš server MCP přístupný z prohlížečů. Další informace najdete v tématu Povolení CORS.
  • Pravidelné aktualizace: Udržujte závislosti aktuální a zmírněte tak známá ohrožení zabezpečení.

Další zdroje informací

Integrace AI do aplikací Azure App Service