Partager via


Intégrer une application App Service en tant que serveur MCP pour GitHub Copilot Chat (Node.js)

Dans ce tutoriel, vous allez découvrir comment exposer les fonctionnalités d’une application Express.js via le protocole MCP (Model Context Protocol), l’ajouter en tant qu’outil à GitHub Copilot et interagir avec votre application à l’aide du langage naturel en mode Agent Copilot Chat.

Capture d’écran montrant GitHub Copilot appelant le serveur Todos MCP hébergé dans Azure App Service.

Si votre application web dispose déjà de fonctionnalités utiles, telles que le shopping, la réservation d’hôtels ou la gestion des données, il est facile de rendre ces fonctionnalités disponibles :

En ajoutant un serveur MCP à votre application web, vous pouvez permettre à un agent de comprendre et d’utiliser les fonctionnalités de votre application lorsqu’elle répond aux invites de l’utilisateur. Cela signifie que tout ce que votre application peut faire, l’agent peut également le faire.

  • Ajoutez un serveur MCP à votre application web.
  • Testez le serveur MCP localement en mode agent GitHub Copilot Chat.
  • Déployez le serveur MCP sur Azure App Service et connectez-vous à celui-ci dans GitHub Copilot Chat.

Prerequisites

Ce didacticiel part du principe que vous utilisez l’exemple utilisé dans le didacticiel : Déployer une application web Node.js + MongoDB sur Azure.

Au minimum, ouvrez l’exemple d’application dans GitHub Codespaces et déployez l’application en exécutant azd up.

Ajouter un serveur MCP à votre application web

  1. Dans le terminal codespace, ajoutez les packages npm requis à votre projet :

    npm install @modelcontextprotocol/sdk@latest zod
    
    1. Ouvrez des itinéraires/index.js. Par souci de simplicité du scénario, vous allez ajouter tout votre code de serveur MCP ici.
  2. En haut des itinéraires/index.js, ajoutez les éléments suivants :

    const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
    const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
    const { z } = require('zod');
    
  3. En bas du fichier, au-dessus de module.exports = router;, ajoutez la route suivante pour le serveur 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,
          });
        }
      }
    });
    

    Cette commande définit votre point de terminaison du serveur MCP à <url>/api/mcp et utilise le modèle de mode sans état dans le SDK TypeScript MCP.

    • server.registerTool() ajoute un outil au serveur MCP avec son implémentation.
    • Le Kit de développement logiciel (SDK) utilise zod pour la validation d’entrée.
    • description dans l’objet de configuration et describe()inputSchema fournissez des descriptions lisibles par l’homme pour les outils et les entrées. Ils aident l’agent appelant à comprendre comment utiliser les outils et leurs paramètres.

    Cet itinéraire duplique la fonctionnalité create-read-update-delete (CRUD) des itinéraires existants, ce qui n’est pas nécessaire, mais vous le conserverez par souci de simplicité. Une bonne pratique consiste à déplacer la logique de l’application vers un module, puis à appeler le module à partir de toutes les routes.

Tester le serveur MCP localement

  1. Dans le terminal codespace, exécutez l’application avec npm start.

  2. Sélectionnez Ouvrir dans le navigateur, puis ajoutez une tâche.

    Conservez npm start en cours d’exécution. Votre serveur MCP est en cours d’exécution à http://localhost:3000/api/mcp maintenant.

  3. De retour dans l’espace de code, ouvrez Copilot Chat, puis sélectionnez le mode Agent dans la zone d’invite.

  4. Sélectionnez le bouton Outils , puis sélectionnez Ajouter d’autres outils... dans la liste déroulante.

    Capture d’écran montrant comment ajouter un serveur MCP en mode Agent GitHub Copilot Chat.

  5. Sélectionnez Ajouter un serveur MCP.

  6. Sélectionnez HTTP (HTTP ou événements envoyés par le serveur).

  7. Dans Entrée de l’URL du serveur, tapez http://localhost:3000/api/mcp.

  8. Dans Entrer l’ID du serveur, tapez todos-mcp ou n’importe quel nom de votre choix.

  9. Sélectionnez Paramètres de l’espace de travail.

  10. Dans une nouvelle fenêtre de discussion du Copilot, tapez quelque chose comme « Montre-moi les tâches à faire ».

  11. Par défaut, GitHub Copilot affiche une confirmation de sécurité lorsque vous appelez un serveur MCP. Sélectionnez Continuer.

    Capture d’écran montrant le message de sécurité par défaut d’un appel MCP dans GitHub Copilot Chat.

    Vous devez maintenant voir une réponse qui indique que l’appel de l’outil MCP réussit.

    Capture d’écran montrant que la réponse de l’appel de l’outil MCP se trouve dans la fenêtre GitHub Copilot Chat.

Déployer votre serveur MCP sur App Service

  1. De retour dans le terminal Codespace, déployez vos modifications en commitant vos modifications (méthode GitHub Actions) ou en exécutant azd up (méthode CLI du développeur Azure).

  2. Dans la sortie AZD, recherchez l’URL de votre application. L’URL ressemble à ceci dans la sortie AZD :

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Une fois azd up terminé, ouvrez .vscode/mcp.json. Définissez l’URL sur <app-url>/api/mcp.

  4. Au-dessus de la configuration du serveur MCP modifiée, sélectionnez Démarrer.

    Capture d’écran montrant comment démarrer manuellement un serveur MCP à partir du fichier mcp.json local.

  5. Démarrez une nouvelle fenêtre gitHub Copilot Chat. Vous devez être en mesure d’afficher, de créer, de mettre à jour et de supprimer des tâches dans l’agent Copilot.

Bonnes pratiques de sécurité

Lorsque votre serveur MCP est sollicité par un agent utilisant de grands modèles de langage (LLM), soyez conscient des attaques par injection de commande. Tenez compte des meilleures pratiques de sécurité suivantes :

  • Authentification et autorisation : Sécurisez votre serveur MCP avec l’authentification Microsoft Entra pour vous assurer que seuls les utilisateurs ou agents autorisés peuvent accéder à vos outils. Consultez Protocole de Contexte de Modèle Sécurisé pour les appels vers Azure App Service depuis Visual Studio Code avec l'authentification Microsoft Entra pour un guide étape par étape.
  • Validation d’entrée et assainissement : l’exemple de code de ce didacticiel utilise zod pour la validation d’entrée, ce qui garantit que les données entrantes correspondent au schéma attendu. Pour une sécurité supplémentaire, envisagez les éléments suivants :
    • Validation et nettoyage de toutes les entrées utilisateur avant le traitement, en particulier pour les champs utilisés dans les requêtes de base de données ou la sortie.
    • Échappement des données de sortie dans les réponses pour empêcher le scripting inter-site (XSS) si votre API est consommée par des navigateurs.
    • Appliquer des schémas stricts et des valeurs par défaut dans vos modèles pour éviter les données inattendues.
  • HTTPS : L’exemple s’appuie sur Azure App Service, qui applique HTTPS par défaut et fournit des certificats TLS/SSL gratuits pour chiffrer les données en transit.
  • Principe des privilèges minimum : exposez uniquement les outils et données nécessaires pour votre cas d’usage. Évitez d’exposer des opérations sensibles, sauf si nécessaire.
  • Limitation et limitation du débit : utilisez gestion des API ou intergiciel personnalisé pour éviter les attaques par déni de service et d’abus.
  • Journalisation et surveillance : journalisation de l’accès et de l’utilisation des points de terminaison MCP pour l’audit et la détection des anomalies. Surveillez les activités suspectes.
  • Configuration CORS : limitez les demandes d’origine croisée aux domaines approuvés si votre serveur MCP est accessible à partir de navigateurs. Pour plus d’informations, consultez Activer CORS.
  • Mises à jour régulières : conservez vos dépendances à jour pour atténuer les vulnérabilités connues.

Plus de ressources

Intégrer l’IA à vos applications Azure App Service