App Service uygulamasını GitHub Copilot Sohbeti için MCP Sunucusu olarak tümleştirme (Node.js)

Bu öğreticide, Model Bağlam Protokolü (MCP) aracılığıyla bir Express.js uygulamasının işlevselliğini kullanıma sunma, GitHub Copilot'a araç olarak ekleme ve Copilot Sohbet aracı modunda doğal dil kullanarak uygulamanızla etkileşim kurma hakkında bilgi edineceksiniz.

Azure App Service'te barındırılan Todos MCP sunucusunu çağıran GitHub Copilot'ı gösteren ekran görüntüsü.

Web uygulamanızın alışveriş, otel rezervasyonu veya veri yönetimi gibi yararlı özellikleri zaten varsa, bu özellikleri şu özellikler için kullanılabilir hale getirmek kolaydır:

Web uygulamanıza bir MCP sunucusu ekleyerek, bir aracının kullanıcı istemlerine yanıt verirken uygulamanızın özelliklerini anlamasını ve kullanmasını sağlarsınız. Bu, uygulamanızın yapabileceği her şeyi aracın da yapabileceği anlamına gelir.

  • Web uygulamanıza bir MCP sunucusu ekleyin.
  • MCP sunucusunu GitHub Copilot Sohbet aracı modunda yerel olarak test edin.
  • MCP sunucusunu Azure App Service'e dağıtın ve GitHub Copilot Sohbeti'nde bu sunucuya bağlanın.

Prerequisites

Bu öğreticide , Öğretici: Azure'a Node.js + MongoDB web uygulaması dağıtma bölümünde kullanılan örnekle çalıştığınız varsayılır.

En azından GitHub Codespaces'ta örnek uygulamayı açın ve komutunu çalıştırarak azd upuygulamayı dağıtın.

Web uygulamanıza MCP sunucusu ekleme

  1. Codespace terminalinde, gerekli npm paketlerini projenize ekleyin:

    npm install @modelcontextprotocol/sdk@latest zod
    
    1. Yolları/index.jsaçın. Senaryonun basitliği için tüm MCP sunucu kodunuzu buraya ekleyeceksiniz.
  2. routes/index.jsüst kısmına aşağıdakileri ekleyin:

    const { McpServer } = require('@modelcontextprotocol/sdk/server/mcp.js');
    const { StreamableHTTPServerTransport } = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
    const { z } = require('zod');
    
  3. Dosyanın en altına, yukarıda module.exports = router; MCP sunucusu için aşağıdaki yolu ekleyin.

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

    Bu yol, MCP sunucu uç noktanızı <url>/api/mcp olarak ayarlar ve MCP TypeScript SDK'sında durum bilgisi olmayan mod desenini kullanır.

    • server.registerTool() uygulamasıyla MCP sunucusuna bir araç ekler.
    • SDK, giriş doğrulaması için zod kullanır.
    • description içinde ve describe() yapılandırma nesnesinde inputSchema araçlar ve girişler için okunabilir açıklamalar sağlar. Arama aracısının araçları ve parametrelerini kullanmayı anlamasına yardımcı olur.

    Bu rota, mevcut rotaların create-read-update-delete (CRUD) işlevselliğini yineler; bu gereksiz olmasına rağmen basitlik sağlamak için bunu devam ettirirsiniz. En iyi yöntem, uygulama mantığını bir modüle taşımak ve ardından modülü tüm yollardan çağırmaktır.

MCP sunucusunu yerel olarak test edin

  1. Codespace terminalinde, uygulamayı npm start ile çalıştırın.

  2. Tarayıcıda Aç'ı seçin ve bir görev ekleyin.

    Koşmaya devam edin npm start . MCP sunucunuz şu anda http://localhost:3000/api/mcp çalışıyor.

  3. Codespace'a geri dönün, Copilot Sohbeti'ni açın ve ardından istem kutusunda Aracı modu'nu seçin.

  4. Araçlar düğmesini ve ardından açılan listeden Daha Fazla Araç Ekle... öğesini seçin.

    GitHub Copilot Sohbet aracı modunda MCP sunucusu eklemeyi gösteren ekran görüntüsü.

  5. MCP Sunucusu Ekle'yi seçin.

  6. HTTP (HTTP veya Server-Sent Olayları) öğesini seçin.

  7. Sunucu URL'sini Girin alanına yazınhttp://localhost:3000/api/mcp.

  8. Sunucu Kimliğini Girin alanına todos-mcp veya istediğiniz adı yazın.

  9. Çalışma Alanı Ayarları'nı seçin.

  10. Yeni bir Copilot Sohbet penceresine "Yapılacakları göster" gibi bir ifade yazın.

  11. Varsayılan olarak, Bir MCP sunucusunu çağırdığınızda GitHub Copilot size bir güvenlik onayı gösterir. Devamtuşuna basın.

    GitHub Copilot Chat'teki bir MCP çağrısından gelen varsayılan güvenlik iletisini gösteren ekran görüntüsü.

    Şimdi MCP araç çağrısının başarılı olduğunu belirten bir yanıt görmeniz gerekir.

    MCP aracından gelen yanıtı GitHub Copilot Sohbet penceresinde gösteren ekran görüntüsü.

MCP sunucunuzu App Service'e dağıtma

  1. Kod alanı terminaline geri dönerek, değişikliklerinizi işleyerek (GitHub Actions yöntemi) veya azd up komutunu çalıştırarak (Azure Geliştirici CLI yöntemi) değişikliklerinizi dağıtın.

  2. AZD çıkışında uygulamanızın URL'sini bulun. AZD çıktısındaki URL şöyle görünür:

     Deploying services (azd deploy)
    
       (✓) Done: Deploying service web
       - Endpoint: <app-url>
     
  3. İşlem tamamlandıktan sonra azd up.vscode/mcp.jsondosyasını açın. URL'yi <app-url>/api/mcp olarak değiştirin.

  4. Değiştirilen MCP sunucu yapılandırmanızın üst kısmındaki Başlat'ı seçin.

    McP sunucusunu yerel mcp.json dosyasından el ile başlatmayı gösteren ekran görüntüsü.

  5. Yeni bir GitHub Copilot Sohbet penceresi başlatın. Copilot aracısında görevleri görüntüleyebilmeniz, oluşturabilmeniz, güncelleştirebilmeniz ve silebilmeniz gerekir.

En iyi güvenlik uygulamaları

MCP sunucunuz, büyük dil modelleri (LLM) tarafından desteklenen bir aracı tarafından çağrıldığında, prompt ekleme saldırılarına dikkat edin. Aşağıdaki en iyi güvenlik uygulamalarını göz önünde bulundurun:

  • Kimlik Doğrulaması ve Yetkilendirme: Araçlarınıza yalnızca yetkili kullanıcıların veya aracıların erişebildiğinden emin olmak için MCP sunucunuzun güvenliğini Microsoft Entra kimlik doğrulaması ile sağlayın. Adım adım kılavuz için bkz. Microsoft Entra kimlik doğrulaması ile Visual Studio Code'dan Azure App Service'e Güvenli Model Bağlam Protokolü çağrıları .
  • Giriş Doğrulama ve Temizleme: Bu öğreticideki örnek kod, gelen verilerin beklenen şemayla eşleştiğinden emin olarak giriş doğrulaması için zod kullanır. Ek güvenlik için şunları göz önünde bulundurun:
    • Özellikle veritabanı sorgularında veya çıkışında kullanılan alanlar için, işlemeden önce tüm kullanıcı girişlerini doğrulama ve temizleme.
    • API'niz tarayıcılar tarafından kullanılıyorsa, siteler arası betik saldırılarını (XSS) önlemek için yanıt çıktısında çıkış karakterlerini engellemek.
    • Beklenmeyen verileri önlemek için modellerinizde katı şemalar ve varsayılan değerler uygulama.
  • HTTPS: Örnek, varsayılan olarak HTTPS'yi zorlayan ve aktarımdaki verileri şifrelemek için ücretsiz TLS/SSL sertifikaları sağlayan Azure App Service'e dayanır.
  • En Az Ayrıcalık İlkesi: Yalnızca kullanım örneğininiz için gerekli araçları ve verileri kullanıma sunma. Gerekli olmadıkça hassas işlemleri ortaya çıkarmaktan kaçının.
  • Hız Sınırlama ve Azaltma: Kötüye kullanım ve hizmet reddi saldırılarını önlemek için API Management veya özel ara yazılım kullanın.
  • Günlük ve İzleme: Denetim ve anomali algılama için MCP uç noktalarının günlük erişimi ve kullanımı. Şüpheli etkinliği izleyin.
  • CORS Yapılandırması: MCP sunucunuza tarayıcılardan erişiliyorsa, çapraz kökenli istekleri güvenilen etki alanlarıyla kısıtlayın. Daha fazla bilgi için bkz. CORS'yi etkinleştirme.
  • Düzenli Güncelleştirmeler: Bilinen güvenlik açıklarını azaltmak için bağımlılıklarınızı güncel tutun.

Daha fazla kaynak

Yapay zekayı Azure App Service uygulamalarınızla tümleştirme