Teilen über


Integrieren einer App Service-App als MCP-Server für GitHub Copilot Chat (Node.js)

In diesem Tutorial erfahren Sie, wie Sie die Funktionen einer Express.js-App über Model Context Protocol (MCP) verfügbar machen, sie GitHub Copilot als Tool hinzufügen und mit Ihrer App im Agent-Modus von Copilot-Chat interagieren.

Screenshot, der zeigt, wie GitHub Copilot den Todos MCP-Server aufruft, der in Azure App Service gehostet wird.

Wenn Ihre Webanwendung bereits nützliche Funktionen wie Shopping, Hotelbuchung oder Datenverwaltung aufweist, ist es einfach, diese Funktionen verfügbar zu machen für:

Indem Sie Ihrer Web-App einen MCP-Server hinzufügen, können Sie einen Agent aktivieren, um die Funktionen Ihrer App zu verstehen und zu verwenden, wenn er auf Benutzeraufforderungen reagiert. Dies bedeutet, dass ihre App alles tun kann, was der Agent auch tun kann.

  • Fügen Sie Ihrer Web-App einen MCP-Server hinzu.
  • Testen Sie den MCP-Server lokal im GitHub Copilot Chat-Agent-Modus.
  • Stellen Sie den MCP-Server in Azure App Service bereit und stellen Sie eine Verbindung mit dem Server in GitHub Copilot Chat her.

Prerequisites

In diesem Lernprogramm wird davon ausgegangen, dass Sie mit dem Beispiel arbeiten, das im Lernprogramm verwendet wird: Bereitstellen einer Node.js + MongoDB-Web-App in Azure.

Öffnen Sie mindestens die Beispielanwendung in GitHub Codespaces, und stellen Sie die App durch Ausführen azd upbereit.

Hinzufügen eines MCP-Servers zu Ihrer Web-App

  1. Fügen Sie im Codespace-Terminal dem Projekt die erforderlichen npm-Pakete hinzu:

    npm install @modelcontextprotocol/sdk@latest zod
    
    1. Öffnen Sie Routen/index.js. Aus Gründen der Einfachheit des Szenarios fügen Sie hier den gesamten MCP-Servercode hinzu.
  2. Fügen Sie oben in routes/index.js die folgenden Anforderungen hinzu.

    const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
    const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
    const { z } = require('zod');
    
  3. Fügen Sie am Ende der Datei oberhalb von module.exports = router; die folgende Route für den MCP-Server hinzu.

    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,
          });
        }
      }
    });
    

    Diese Route legt ihren MCP-Serverendpunkt auf <url>/api/mcp fest und verwendet das zustandslose Modusmuster im MCP TypeScript SDK.

    • server.registerTool() fügt dem MCP-Server mit seiner Implementierung ein Tool hinzu.
    • Das SDK verwendet Zod für die Eingabeüberprüfung.
    • description im Konfigurationsobjekt und describe() in inputSchema bieten lesbare Beschreibungen für Tools und Eingaben. Sie helfen dem aufrufenden Agent zu verstehen, wie die Tools und ihre Parameter verwendet werden.

    Diese Route dupliziert die CruD-Funktionalität (Create-read-update-delete) der vorhandenen Routen, die unnötig ist, aber Sie behalten sie zur Vereinfachung bei. Es empfiehlt sich, die App-Logik in ein Modul zu verschieben und dann das Modul aus allen Routen aufzurufen.

Testen des MCP-Servers lokal

  1. Führen Sie im Codespace-Terminal die Anwendung mit npm start aus.

  2. Wählen Sie im Browser öffnen und dann eine Aufgabe hinzufügen.

    Lassen Sie npm start weiter laufen. Ihr MCP-Server wird zurzeit http://localhost:3000/api/mcp ausgeführt.

  3. Öffnen Sie im Codespace den Copilot-Chat, und wählen Sie dann im Eingabeaufforderungsfeld den Agent-Modus aus.

  4. Wählen Sie die Schaltfläche "Extras " und dann im Dropdownmenü " Weitere Tools hinzufügen" aus.

    Screenshot, der zeigt, wie Sie einen MCP-Server im GitHub Copilot Chat-Agent-Modus hinzufügen.

  5. Wählen Sie "MCP-Server hinzufügen" aus.

  6. Wählen Sie HTTP (HTTP oder Server-Sent Events) aus.

  7. Geben Sie unter "Server-URL eingeben"http://localhost:3000/api/mcp ein.

  8. Geben Sie in "Server-ID eingeben" "todos-mcp" oder einen beliebigen Namen ein.

  9. Wählen Sie "Arbeitsbereichseinstellungen" aus.

  10. Geben Sie in einem neuen Copilot-Chatfenster so etwas wie "Zeig mir die Todos." ein.

  11. Standardmäßig zeigt GitHub Copilot eine Sicherheitsbestätigung an, wenn Sie einen MCP-Server aufrufen. Wählen Sie "Weiter" aus.

    Screenshot der Standardsicherheitsmeldung eines MCP-Aufrufs in GitHub Copilot Chat.

    Nun sollte eine Antwort angezeigt werden, die angibt, dass der MCP-Toolaufruf erfolgreich ist.

    Screenshot: Die Antwort des MCP-Toolaufrufs wird im GitHub Copilot Chat-Fenster angezeigt.

MCP-Server auf App Service bereitstellen

  1. Stellen Sie ihre Änderungen wieder im Codespace-Terminal bereit, indem Sie Änderungen übernehmen (GitHub Actions-Methode) oder azd up ausführen (Azure Developer CLI-Methode).

  2. Suchen Sie in der AZD-Ausgabe die URL Ihrer App. Die URL sieht in der AZD-Ausgabe wie folgt aus:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Öffnen Sie nach Abschluss von azd up.vscode/mcp.json. Ändern Sie die URL in <app-url>/api/mcp.

  4. Wählen Sie oberhalb der geänderten MCP-Serverkonfiguration "Start" aus.

    Screenshot, der zeigt, wie Sie einen MCP-Server manuell aus der lokalen mcp.json Datei starten.

  5. Starten Sie ein neues GitHub Copilot-Chatfenster. Sie sollten in der Lage sein, Aufgaben im Copilot-Agent anzuzeigen, zu erstellen, zu aktualisieren und zu löschen.

Bewährte Sicherheitsmethoden

Wenn Ihr MCP-Server von einem Agent aufgerufen wird, der von großen Sprachmodellen (LLM) unterstützt wird, sollten Sie sich über Eingabeaufforderungsangriffe bewusst sein. Berücksichtigen Sie die folgenden bewährten Sicherheitsmethoden:

  • Authentifizierung und Autorisierung: Sichern Sie Ihren MCP-Server mit der Microsoft Entra-Authentifizierung, um sicherzustellen, dass nur autorisierte Benutzer oder Agents auf Ihre Tools zugreifen können. Eine schrittweise Anleitung finden Sie unter Secure Model Context Protocol-Aufrufe an Azure App Service von Visual Studio Code mit microsoft Entra-Authentifizierung .
  • Eingabeüberprüfung und Bereinigung: Der Beispielcode in diesem Lernprogramm verwendet Zod für die Eingabeüberprüfung, um sicherzustellen, dass eingehende Daten mit dem erwarteten Schema übereinstimmen. Berücksichtigen Sie für zusätzliche Sicherheit Folgendes:
    • Überprüfen und Bereinigung aller Benutzereingaben vor der Verarbeitung, insbesondere für Felder, die in Datenbankabfragen oder -ausgaben verwendet werden.
    • Die Ausgabe in Antworten mit Escapezeichen versehen, um das Cross-Site Scripting (XSS) zu verhindern, wenn Ihre API von Browsern verwendet wird.
    • Anwenden strenger Schemas und Standardwerte in Ihren Modellen, um unerwartete Daten zu vermeiden.
  • HTTPS: Das Beispiel basiert auf Azure App Service, der STANDARDMÄßIG HTTPS erzwingt und kostenlose TLS/SSL-Zertifikate zum Verschlüsseln von Daten während der Übertragung bereitstellt.
  • Prinzip der geringsten Rechte: Machen Sie nur die erforderlichen Tools und Daten verfügbar, die für Ihren Anwendungsfall erforderlich sind. Vermeiden Sie das Verfügbarmachen vertraulicher Vorgänge, es sei denn, dies ist erforderlich.
  • Rate Limiting and Throttling: Verwenden Sie API-Verwaltung oder benutzerdefinierte Middleware, um Missbrauch und Denial-of-Service-Angriffe zu verhindern.
  • Protokollierung und Überwachung: Protokollieren des Zugriffs und der Verwendung von MCP-Endpunkten für die Überwachung und Anomalieerkennung. Überwachen Sie verdächtige Aktivitäten.
  • CORS-Konfiguration: Beschränken Sie ursprungsübergreifende Anforderungen auf vertrauenswürdige Domänen, wenn auf Ihren MCP-Server über Browser zugegriffen wird. Weitere Informationen finden Sie unter Aktivieren von CORS.
  • Regelmäßige Updates: Halten Sie Ihre Abhängigkeiten auf dem neuesten Stand, um bekannte Sicherheitsrisiken zu mindern.

Weitere Ressourcen

Integrieren von KI in Ihre Azure App Service-Anwendungen