Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Dalam tutorial ini, Anda membangun server Protokol Konteks Model (MCP) yang mengekspos alat manajemen tugas dengan menggunakan Express dan MCP TypeScript SDK. Anda menyebarkan server ke Azure Container Apps dan menghubungkannya dari GitHub Copilot Chat di VS Code.
Di tutorial ini, Anda akan:
- Membuat aplikasi Ekspres yang mengekspos alat MCP
- Menguji server MCP secara lokal dengan GitHub Copilot
- Kontainerisasi dan sebarkan aplikasi ke Azure Container Apps
- Menyambungkan GitHub Copilot ke server MCP yang disebarkan
Prasyarat
- Sebuah akun Azure dengan langganan aktif. Buat akun gratis.
- Azure CLI versi 2.62.0 atau yang lebih baru.
- Node.js 20 LTS atau yang lebih baru.
- Visual Studio Code dengan ekstensi GitHub Copilot .
- Docker Desktop (opsional - hanya diperlukan untuk menguji kontainer secara lokal).
Membuat perancah aplikasi
Di bagian ini, Anda membuat proyek Node.js baru dengan Express dan MCP TypeScript SDK.
Buat direktori proyek dan inisialisasi:
mkdir tasks-mcp-server && cd tasks-mcp-server npm init -yInstal dependensi:
npm install @modelcontextprotocol/sdk express zod npm install -D typescript @types/node @types/express tsxBuat
tsconfig.json:{ "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "declaration": true }, "include": ["src/**/*"] }Konfigurasi ini menargetkan ES2022 dengan resolusi modul Node.js, menghasilkan file yang dikompilasi ke
dist/, dan memungkinkan pemeriksaan jenis yang ketat.Perbarui
package.jsonuntuk mengaktifkan modul ES dan menambahkan build dan memulai skrip. Tambahkan atau ganti kolomtypedanscripts{ "type": "module", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsx watch src/index.ts" } }Penting
Atur
"type": "module". Kode server MCP menggunakan tingkatawaitatas , yang hanya didukung dalam modul ES.Buat
src/taskStore.tsuntuk penyimpanan data dalam memori:export interface TaskItem { id: number; title: string; description: string; isComplete: boolean; createdAt: string; } class TaskStore { private tasks: TaskItem[] = [ { id: 1, title: "Buy groceries", description: "Milk, eggs, bread", isComplete: false, createdAt: new Date().toISOString(), }, { id: 2, title: "Write docs", description: "Draft the MCP tutorial", isComplete: true, createdAt: new Date(Date.now() - 86400000).toISOString(), }, ]; private nextId = 3; getAll(): TaskItem[] { return [...this.tasks]; } getById(id: number): TaskItem | undefined { return this.tasks.find((t) => t.id === id); } create(title: string, description: string): TaskItem { const task: TaskItem = { id: this.nextId++, title, description, isComplete: false, createdAt: new Date().toISOString(), }; this.tasks.push(task); return task; } toggleComplete(id: number): TaskItem | undefined { const task = this.tasks.find((t) => t.id === id); if (!task) return undefined; task.isComplete = !task.isComplete; return task; } delete(id: number): boolean { const index = this.tasks.findIndex((t) => t.id === id); if (index < 0) return false; this.tasks.splice(index, 1); return true; } } export const store = new TaskStore();Antarmuka
TaskItemmenentukan bentuk data tugas. KelasTaskStoremengelola array dalam memori yang telah diisi sebelumnya dengan data sampel dan menyediakan metode untuk mencantumkan, menemukan, membuat, mengalihkan, dan menghapus tugas. Singleton pada tingkat modul diekspor untuk digunakan oleh alat MCP.
Tentukan alat MCP
Selanjutnya, Anda mendefinisikan server MCP dengan registrasi alat yang memperlihatkan penyimpanan tugas yang dapat diakses oleh klien AI.
Buat
src/index.ts:import express, { Request, Response } from "express"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { z } from "zod"; import { store } from "./taskStore.js"; const app = express(); app.use(express.json()); // Health endpoint for Container Apps probes app.get("/health", (_req: Request, res: Response) => { res.json({ status: "healthy" }); }); // Create the MCP server const mcpServer = new McpServer({ name: "TasksMCP", version: "1.0.0", }); // Register tools mcpServer.tool("list_tasks", "List all tasks with their ID, title, description, and completion status.", {}, async () => { return { content: [{ type: "text", text: JSON.stringify(store.getAll(), null, 2) }], }; }); mcpServer.tool( "get_task", "Get a single task by its numeric ID.", { task_id: z.number().describe("The numeric ID of the task to retrieve") }, async ({ task_id }) => { const task = store.getById(task_id); return { content: [ { type: "text", text: task ? JSON.stringify(task, null, 2) : `Task with ID ${task_id} not found.`, }, ], }; } ); mcpServer.tool( "create_task", "Create a new task with the given title and description. Returns the created task.", { title: z.string().describe("A short title for the task"), description: z.string().describe("A detailed description of what the task involves"), }, async ({ title, description }) => { const task = store.create(title, description); return { content: [{ type: "text", text: JSON.stringify(task, null, 2) }], }; } ); mcpServer.tool( "toggle_task_complete", "Toggle a task's completion status between complete and incomplete.", { task_id: z.number().describe("The numeric ID of the task to toggle") }, async ({ task_id }) => { const task = store.toggleComplete(task_id); const msg = task ? `Task ${task.id} is now ${task.isComplete ? "complete" : "incomplete"}.` : `Task with ID ${task_id} not found.`; return { content: [{ type: "text", text: msg }] }; } ); mcpServer.tool( "delete_task", "Delete a task by its numeric ID.", { task_id: z.number().describe("The numeric ID of the task to delete") }, async ({ task_id }) => { const deleted = store.delete(task_id); const msg = deleted ? `Task ${task_id} deleted.` : `Task with ID ${task_id} not found.`; return { content: [{ type: "text", text: msg }] }; } ); // Mount the MCP streamable HTTP transport const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined }); app.post("/mcp", async (req: Request, res: Response) => { await transport.handleRequest(req, res, req.body); }); app.get("/mcp", async (req: Request, res: Response) => { await transport.handleRequest(req, res); }); app.delete("/mcp", async (req: Request, res: Response) => { await transport.handleRequest(req, res); }); // Connect the transport to the MCP server await mcpServer.connect(transport); // Start the Express server const PORT = parseInt(process.env.PORT || "3000", 10); app.listen(PORT, () => { console.log(`MCP server running on http://localhost:${PORT}/mcp`); });Poin utama:
-
McpServerdari TypeScript SDK mendefinisikan server MCP dengan registrasi alat. -
StreamableHTTPServerTransportmenangani protokol HTTP yang dapat dialirkan MCP. PengaturansessionIdGenerator: undefinedmenjalankan server dalam mode stateless. - Alat menggunakan skema Zod untuk menentukan parameter input dengan deskripsi.
- Titik akhir terpisah
/healthdiperlukan untuk melakukan pemeriksaan kesehatan Container Apps.
-
Menguji server MCP secara lokal
Sebelum menyebarkan ke Azure, verifikasi server MCP berfungsi dengan menjalankannya secara lokal dan menyambungkan dari GitHub Copilot.
Mulai server pengembangan:
npx tsx src/index.tsBuka Visual Studio Code, buka Obrolan Copilot, dan pilih Mode agen .
Pilih tombol Alat , lalu pilih Tambahkan Alat Lainnya...>Tambahkan Server MCP.
Pilih HTTP (HTTP atau Event Server-Sent).
Masukkan URL server:
http://localhost:3000/mcpNota
Server pengembangan lokal secara default menggunakan port 3000. Ketika dikontainerisasi, Dockerfile mengatur
PORTvariabel lingkungan ke 8080 untuk mencocokkan dengan port target Container Apps.Masukkan ID server:
tasks-mcpPilih Pengaturan Ruang Kerja.
Uji dengan perintah: "Tampilkan saya semua tugas"
Pilih Lanjutkan saat Copilot meminta konfirmasi pemanggilan alat.
Anda seharusnya melihat Copilot menampilkan kembali daftar tugas yang berasal dari penyimpanan dalam memori Anda.
Petunjuk / Saran
Coba perintah lain seperti "Buat tugas untuk meninjau PR", "Tandai tugas 1 sebagai selesai", atau "Hapus tugas 2".
Menerapkan kontainer pada aplikasi
Kemas aplikasi sebagai kontainer Docker sehingga Anda dapat mengujinya secara lokal sebelum menyebarkan ke Azure.
Buat
Dockerfile:FROM node:20-slim AS build WORKDIR /app COPY package*.json . RUN npm ci COPY tsconfig.json . COPY src/ src/ RUN npm run build FROM node:20-slim WORKDIR /app COPY package*.json . RUN npm ci --omit=dev COPY --from=build /app/dist ./dist ENV PORT=8080 EXPOSE 8080 CMD ["node", "dist/index.js"]Build multi-tahap mengompilasi TypeScript pada tahap pertama, kemudian membuat citra produksi hanya dengan dependensi runtime dan output JavaScript yang terkompilasi. Variabel
PORTlingkungan diatur ke 8080 agar sesuai dengan port target Container Apps.Verifikasi secara lokal:
docker build -t tasks-mcp-server . docker run -p 8080:8080 tasks-mcp-serverKonfirmasi:
curl http://localhost:8080/health
Menyebarkan ke Azure Container Apps
Setelah Anda membuat kontainer aplikasi, sebarkan ke Azure Container Apps dengan menggunakan Azure CLI. Perintah ini az containerapp up membangun gambar kontainer di cloud, sehingga Anda tidak memerlukan Docker di komputer Anda untuk langkah ini.
Atur variabel lingkungan:
RESOURCE_GROUP="mcp-tutorial-rg" LOCATION="eastus" ENVIRONMENT_NAME="mcp-env" APP_NAME="tasks-mcp-server-node"Buat grup sumber daya:
az group create --name $RESOURCE_GROUP --location $LOCATIONMembuat lingkungan Aplikasi Kontainer:
az containerapp env create \ --name $ENVIRONMENT_NAME \ --resource-group $RESOURCE_GROUP \ --location $LOCATIONSebarkan aplikasi kontainer:
az containerapp up \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $ENVIRONMENT_NAME \ --source . \ --ingress external \ --target-port 8080Konfigurasikan CORS untuk mengizinkan permintaan GitHub Copilot:
az containerapp ingress cors enable \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --allowed-origins "*" \ --allowed-methods "GET,POST,DELETE,OPTIONS" \ --allowed-headers "*"Nota
Untuk produksi, gantilah asal wildcard
*dengan asal tepercaya yang spesifik. Lihat Mengamankan server MCP di Container Apps untuk panduan.Verifikasi penyebaran:
APP_URL=$(az containerapp show \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --query "properties.configuration.ingress.fqdn" -o tsv) curl https://$APP_URL/health
Menyambungkan GitHub Copilot ke server yang disebarkan
Sekarang setelah server MCP berjalan di Azure, konfigurasikan VS Code untuk menyambungkan GitHub Copilot ke endpoint yang telah diterapkan.
Dalam proyek Anda, buat atau perbarui
.vscode/mcp.json:{ "servers": { "tasks-mcp-server": { "type": "http", "url": "https://<your-app-fqdn>/mcp" } } }Ganti
<your-app-fqdn>dengan FQDN dari output penyebaran.Di Visual Studio Code, buka Copilot Chat dalam mode Agent.
Jika server tidak muncul secara otomatis, pilih tombol Alat dan pastikan
tasks-mcp-servertercantum dalam daftar. Pilih Mulai jika diperlukan.Uji dengan perintah seperti "Daftar semua tugas saya" untuk mengonfirmasi server MCP yang disebarkan merespons.
Mengonfigurasi penskalaan untuk penggunaan interaktif
Secara default, Azure Container Apps dapat menskalakan ke nol replika. Untuk server MCP yang melayani klien interaktif seperti Copilot, cold start menyebabkan penundaan yang nyata. Atur jumlah replika minimum untuk menjaga setidaknya satu instans tetap berjalan:
az containerapp update \
--name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--min-replicas 1
Pertimbangan keamanan
Tutorial ini menggunakan server MCP yang tidak diaauthenticated untuk kesederhanaan. Sebelum menjalankan server MCP dalam produksi, tinjau rekomendasi berikut. Ketika agen yang didukung oleh model bahasa besar (LLM) memanggil server MCP Anda, waspadai serangan injeksi yang cepat .
- Autentikasi dan otorisasi: Amankan server MCP Anda dengan menggunakan ID Microsoft Entra. Lihat Mengamankan server MCP di Container Apps.
- Validasi input: Skema Zod memberikan keamanan tipe, tetapi menambahkan validasi berdasarkan aturan bisnis untuk parameter alat. Pertimbangkan perpustakaan seperti zod-express-middleware untuk validasi pada tingkat permintaan.
- HTTPS: Azure Container Apps memberlakukan HTTPS secara default dengan sertifikat TLS otomatis.
- Hak istimewa terkecil: Hanya mengekspos alat yang diperlukan kasus penggunaan Anda. Hindari alat yang melakukan operasi destruktif tanpa konfirmasi.
- CORS: Membatasi asal yang diizinkan ke domain tepercaya di lingkungan produksi.
- Pencatatan dan Pemantauan: Pemanggilan alat MCP dicatat untuk audit. Gunakan Azure Monitor dan Analitik Log.
Membersihkan sumber daya
Jika Anda tidak berencana untuk terus menggunakan aplikasi ini, hapus grup sumber daya untuk menghapus semua sumber daya yang Anda buat dalam tutorial ini:
az group delete --resource-group $RESOURCE_GROUP --yes --no-wait