Partilhar via


Integrar um aplicativo do Serviço de Aplicativo como um Servidor MCP para o Chat do Copiloto do GitHub (Node.js)

Neste tutorial, você aprenderá como expor a funcionalidade de um aplicativo Express.js por meio do MCP (Model Context Protocol), adicioná-lo como uma ferramenta ao GitHub Copilot e interagir com seu aplicativo usando linguagem natural no modo de agente do Copilot Chat.

Captura de tela mostrando o Copilot do GitHub chamando o servidor Todos MCP hospedado no Serviço de Aplicativo do Azure.

Se o seu aplicativo Web já tiver recursos úteis, como compras, reservas de hotéis ou gerenciamento de dados, é fácil disponibilizar esses recursos para:

Ao adicionar um servidor MCP ao seu aplicativo Web, você permite que um agente compreenda e use os recursos do seu aplicativo quando ele responde às solicitações do usuário. Isso significa que tudo o que seu aplicativo pode fazer, o agente também pode fazer.

  • Adicione um servidor MCP ao seu aplicativo Web.
  • Teste o servidor MCP localmente no modo de agente do GitHub Copilot Chat.
  • Implante o servidor MCP no Serviço de Aplicativo do Azure e conecte-se a ele no Chat do Copiloto do GitHub.

Prerequisites

Este tutorial pressupõe que você esteja trabalhando com o exemplo usado em Tutorial: Implantar um aplicativo Web Node.js + MongoDB no Azure.

No mínimo, abra o aplicativo de exemplo no GitHub Codespaces e implante o aplicativo executando azd up.

Adicionar servidor MCP ao seu aplicativo Web

  1. No terminal codespace, adicione os pacotes npm necessários ao seu projeto:

    npm install @modelcontextprotocol/sdk@latest zod
    
    1. Rotas abertas/index.js. Para simplificar o cenário, você adicionará todo o código do servidor MCP aqui.
  2. Na parte superior de rotas/index.js, adicione os seguintes requisitos:

    const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
    const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
    const { z } = require('zod');
    
  3. Na parte inferior do arquivo, acima module.exports = router; , adicione a seguinte rota para o servidor 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,
          });
        }
      }
    });
    

    Essa rota define o ponto de extremidade do servidor MCP como <url>/api/mcp e usa o padrão de modo sem estado no MCP TypeScript SDK.

    • server.registerTool() adiciona uma ferramenta ao servidor MCP com sua implementação.
    • O SDK usa zod para validação de entrada.
    • description no objeto de configuração e describe() em inputSchema fornecem descrições de leitura fácil para ferramentas e entrada. Eles ajudam o agente chamador a entender como usar as ferramentas e seus parâmetros.

    Essa rota duplica a funcionalidade create-read-update-delete (CRUD) das rotas existentes, o que é desnecessário, mas você a manterá para simplificar. Uma prática recomendada seria mover a lógica do aplicativo para um módulo e, em seguida, chamar o módulo de todas as rotas.

Teste o servidor MCP localmente

  1. No terminal codespace, execute o aplicativo com npm start.

  2. Selecione Abrir no Navegador e adicione uma tarefa.

    Deixe correr npm start . O seu servidor MCP está em funcionamento agora no http://localhost:3000/api/mcp.

  3. De volta ao espaço de código, abra o Copilot Chat e selecione Modo de agente na caixa de prompt.

  4. Selecione o botão Ferramentas e, em seguida, selecione Adicionar Mais Ferramentas... no menu pendente.

    Captura de tela mostrando como adicionar um servidor MCP no modo de agente do GitHub Copilot Chat.

  5. Selecione Adicionar Servidor MCP.

  6. Selecione HTTP (HTTP ou Server-Sent Eventos).

  7. Em Inserir URL do Servidor, digite http://localhost:3000/api/mcp.

  8. Em Enter Server ID, digite todos-mcp ou qualquer nome que desejar.

  9. Selecione Configurações do espaço de trabalho.

  10. Em uma nova janela do Copilot Chat, digite algo como "Mostre-me as tarefas".

  11. Por padrão, o GitHub Copilot mostra uma confirmação de segurança quando você invoca um servidor MCP. Selecione Continuar.

    Captura de tela mostrando a mensagem de segurança padrão de uma invocação MCP no GitHub Copilot Chat.

    Agora você verá uma resposta que indica que a chamada da ferramenta MCP foi bem-sucedida.

    Captura de tela mostrando a resposta da chamada da ferramenta MCP na janela do GitHub Copilot Chat.

Implantar o seu servidor MCP no App Service

  1. De volta ao terminal do codespace, implante suas alterações confirmando suas alterações (método GitHub Actions) ou executando azd up (método Azure Developer CLI).

  2. Na saída AZD, localize o URL do seu aplicativo. O URL tem esta aparência na saída AZD:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. Quando azd up terminar, abra .vscode/mcp.json. Altere o URL para <app-url>/api/mcp.

  4. Acima da configuração modificada do servidor MCP, selecione Iniciar.

    Captura de ecrã a mostrar como iniciar manualmente um servidor MCP a partir do ficheiro mcp.json local.

  5. Inicie uma nova janela de bate-papo do GitHub Copilot. Você deve ser capaz de visualizar, criar, atualizar e excluir tarefas no agente Copilot.

Práticas recomendadas de segurança

Quando o seu servidor MCP é chamado por um agente alimentado por modelos de linguagem grandes (LLM), esteja ciente de ataques de injeção imediata . Considere as seguintes práticas recomendadas de segurança:

  • Autenticação e Autorização: Proteja o seu servidor MCP com autenticação Microsoft Entra para garantir que apenas utilizadores ou agentes autorizados possam aceder às suas ferramentas. Consulte as chamadas do protocolo de contexto de modelo seguro para o Azure App Service a partir do Visual Studio Code, utilizando a autenticação Microsoft Entra para um guia passo a passo.
  • Validação e limpeza de entrada: O código de exemplo neste tutorial usa zod para validação de entrada, garantindo que os dados recebidos correspondam ao esquema esperado. Para segurança adicional, considere:
    • Validação e higienização de todas as entradas do usuário antes do processamento, especialmente para campos usados em consultas ou saídas de banco de dados.
    • Escapar da saída em respostas para evitar scripts entre sites (XSS) se sua API for consumida por navegadores.
    • Aplicação de esquemas estritos e valores padrão em seus modelos para evitar dados inesperados.
  • HTTPS: O exemplo depende do Serviço de Aplicativo do Azure, que impõe HTTPS por padrão e fornece certificados TLS/SSL gratuitos para criptografar dados em trânsito.
  • Princípio do Privilégio Mínimo: Exponha apenas as ferramentas e os dados necessários para o seu caso de uso. Evite expor operações sensíveis, a menos que seja necessário.
  • Limitação de Taxa e Controle de Fluxo: use Gerenciamento de API ou middleware personalizado para evitar abusos e ataques de negação de serviço.
  • Registo e monitorização: Registe e monitore o acesso e uso de pontos de extremidade MCP para auditoria e deteção de anomalias. Monitore atividades suspeitas.
  • Configuração do CORS: restrinja solicitações de origem cruzada a domínios confiáveis se o servidor MCP for acessado a partir de navegadores. Para obter mais informações, consulte Habilitar CORS.
  • Atualizações regulares: mantenha suas dependências atualizadas para mitigar vulnerabilidades conhecidas.

Mais recursos

Integre a IA em seus aplicativos do Serviço de Aplicativo do Azure