Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом руководстве вы создадите сервер протокола MCP, предоставляющий инструменты управления задачами с помощью Express и TypeScript-SDK для MCP. Вы развертываете сервер в приложениях контейнеров Azure и подключаетесь к нему из чата GitHub Copilot в VS Code.
Изучив это руководство, вы:
- Создание приложения Express, которое предоставляет средства MCP
- Тестирование сервера MCP локально с помощью GitHub Copilot
- Контейнеризация и развертывание приложения в приложениях контейнеров Azure
- Подключите GitHub Copilot к развернутому серверу MCP
Предпосылки
- Учетная запись Azure с активной подпиской. Создайте его бесплатно.
- Azure CLI версии 2.62.0 или более поздней.
- Node.js 20 LTS или более поздней версии.
- Visual Studio Code с расширением GitHub Copilot .
- Docker Desktop (необязательно) требуется только для локального тестирования контейнера.
Создайте каркас приложения
В этом разделе описано, как создать проект Node.js с помощью Express и пакета SDK ДЛЯ MCP TypeScript.
Создайте каталог проекта и инициализируйте его:
mkdir tasks-mcp-server && cd tasks-mcp-server npm init -yУстановите зависимости:
npm install @modelcontextprotocol/sdk express zod npm install -D typescript @types/node @types/express tsxСоздание
tsconfig.json:{ "compilerOptions": { "target": "ES2022", "module": "Node16", "moduleResolution": "Node16", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "declaration": true }, "include": ["src/**/*"] }Эта конфигурация предназначена для ES2022 с модулем разрешения Node.js, выводом скомпилированных файлов в
dist/, и включением строгой проверки типов.Обновите
package.json, чтобы включить модули ES и добавить скрипты сборки и запуска. Добавьте или замените поляtypeиscripts.{ "type": "module", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsx watch src/index.ts" } }Это важно
Задайте
"type": "module". Код сервера MCP использует верхний уровеньawait, который поддерживается только в модулях ES.Создайте
src/taskStore.tsдля хранилища данных в памяти: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();Интерфейс
TaskItemопределяет структуру данных задачи. КлассTaskStoreуправляет массивом в памяти, предварительно заполненным примерами данных и предоставляет методы для перечисления, поиска, создания, переключения и удаления задач. Экспортируется объект-одиночка на уровне модуля для использования инструментами MCP.
Определение средств MCP
Затем вы определите сервер MCP с регистрацией инструментов, которые предоставляют хранилище задач клиентам ИИ.
Создание
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`); });Основные моменты:
-
McpServerв пакете SDK TypeScript определяет сервер MCP с регистрацией инструментов. -
StreamableHTTPServerTransportобрабатывает протокол HTTP, доступный для потоковой передачи MCP. ПараметрsessionIdGenerator: undefinedзапускает сервер в режиме без отслеживания состояния. - Средства используют схемы Zod для определения входных параметров с описанием.
- Для проверок работоспособности контейнерных приложений требуется отдельная
/healthконечная точка.
-
Тестирование сервера MCP локально
Перед развертыванием в Azure убедитесь, что сервер MCP работает, запустив его локально и подключившись к нему с помощью GitHub Copilot.
Запустите сервер разработки:
npx tsx src/index.tsОткройте VS Code, откройте чат Copilot и выберите режим агента .
Нажмите кнопку "Сервис" , а затем нажмите кнопку "Добавить дополнительные инструменты" ...>Добавьте СЕРВЕР MCP.
Выберите HTTP (HTTP или Server-Sent события).
Введите URL-адрес сервера:
http://localhost:3000/mcpЗамечание
Локальный сервер разработки по умолчанию использует порт 3000. При контейнеризации Dockerfile задает переменной среды
PORTзначение 8080, чтобы сопоставить с целевым портом приложений контейнеров.Введите идентификатор сервера:
tasks-mcpВыберите параметры рабочей области.
Проверка с помощью запроса : "Показать мне все задачи"
Выберите Продолжить, когда Copilot запрашивает подтверждение вызова средства.
Вы увидите, что Copilot возвращает список задач из хранилища в памяти.
Подсказка
Попробуйте использовать другие запросы, такие как "Создать задачу для проверки pr", "Пометить задачу 1 как завершенную" или "Удалить задачу 2".
Помещение приложения в контейнер
Упаковайте приложение как контейнер Docker, чтобы протестировать его локально перед развертыванием в Azure.
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"]Многоэтапная сборка компилирует TypeScript на первом этапе, а затем создает рабочий образ только с зависимостями среды выполнения и скомпилированных выходных данных JavaScript. Переменная
PORTсреды имеет значение 8080, чтобы соответствовать целевому порту контейнерных приложений.Проверьте локально:
docker build -t tasks-mcp-server . docker run -p 8080:8080 tasks-mcp-serverПодтвердить:
curl http://localhost:8080/health
Развертывание в приложениях контейнеров Azure
После контейнеризации приложения разверните его в azure Container Apps с помощью Azure CLI. Команда az containerapp up создает образ контейнера в облаке, поэтому для этого шага не требуется Docker.
Задайте переменные среды:
RESOURCE_GROUP="mcp-tutorial-rg" LOCATION="eastus" ENVIRONMENT_NAME="mcp-env" APP_NAME="tasks-mcp-server-node"Создайте группу ресурсов:
az group create --name $RESOURCE_GROUP --location $LOCATIONСоздайте среду приложений контейнеров:
az containerapp env create \ --name $ENVIRONMENT_NAME \ --resource-group $RESOURCE_GROUP \ --location $LOCATIONРазверните приложение контейнера:
az containerapp up \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --environment $ENVIRONMENT_NAME \ --source . \ --ingress external \ --target-port 8080Настройте CORS, чтобы разрешить запросы GitHub Copilot.
az containerapp ingress cors enable \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --allowed-origins "*" \ --allowed-methods "GET,POST,DELETE,OPTIONS" \ --allowed-headers "*"Замечание
Для производственной среды замените подстановочные знаки
*определенными доверенными источниками. См. руководство по серверам Secure MCP в контейнерных приложениях.Проверьте развертывание:
APP_URL=$(az containerapp show \ --name $APP_NAME \ --resource-group $RESOURCE_GROUP \ --query "properties.configuration.ingress.fqdn" -o tsv) curl https://$APP_URL/health
Подключите GitHub Copilot к развернутому серверу
Теперь, когда сервер MCP запущен в Azure, настройте VS Code для подключения GitHub Copilot к развернутой конечной точке.
В проекте создайте или обновите
.vscode/mcp.json:{ "servers": { "tasks-mcp-server": { "type": "http", "url": "https://<your-app-fqdn>/mcp" } } }Замените
<your-app-fqdn>полным доменным именем из выходных данных развертывания.В VS Code откройте чат Copilot в режиме агента.
Если сервер не появляется автоматически, выберите кнопку «Сервис» и проверьте, отображается ли
tasks-mcp-server. При необходимости нажмите кнопку "Пуск ".Проверьте, как сервер MCP отвечает на запрос "Перечислить все мои задачи", чтобы убедиться в его правильной работе после развертывания.
Настройка масштабирования для интерактивного использования
По умолчанию приложения контейнеров Azure могут масштабироваться до нуля реплик. Для серверов MCP, которые обслуживают интерактивных клиентов, таких как Copilot, холодные запуски вызывают заметные задержки. Задайте минимальное число реплик для поддержания по крайней мере одного экземпляра:
az containerapp update \
--name $APP_NAME \
--resource-group $RESOURCE_GROUP \
--min-replicas 1
Вопросы безопасности
В этом руководстве для простоты используется неавтоентизованный сервер MCP. Перед запуском сервера MCP в рабочей среде ознакомьтесь со следующими рекомендациями. Когда агент, работающий на больших языковых моделях (LLM), вызывает сервер MCP, учитывайте атаки инъекции команд.
- Проверка подлинности и авторизация. Защита сервера MCP с помощью идентификатора Microsoft Entra. См. раздел "Безопасные серверы MCP" в приложениях контейнеров.
- Проверка входных данных: схемы Zod обеспечивают безопасность типов, но добавляют проверку бизнес-правила для параметров средства. Рассмотрите такие библиотеки, как zod-express-middleware для проверки на уровне запроса.
- HTTPS: приложения контейнеров Azure по умолчанию применяют ПРОТОКОЛ HTTPS с автоматическими сертификатами TLS.
- Минимальные привилегии: предоставляйте только те инструменты, которые требуются для вашего случая. Избегайте средств, выполняющих разрушительные операции без подтверждения.
- CORS: ограничение разрешенных источников доверенным доменам в рабочей среде.
- Ведение журнала и мониторинг: Запись вызовов инструментов MCP для аудита. Используйте Azure Monitor и Log Analytics.
Очистите ресурсы
Если вы не планируете продолжать использовать это приложение, удалите группу ресурсов, чтобы удалить все ресурсы, созданные в этом руководстве:
az group delete --resource-group $RESOURCE_GROUP --yes --no-wait
Следующий шаг
Связанный контент
- Общие сведения о серверах MCP в приложениях контейнеров Azure
- Развертывание сервера MCP в приложениях-контейнерах (.NET)
- Развертывание сервера MCP в приложениях контейнеров (Python)
- Развертывание сервера MCP в приложениях контейнеров (Java)
- Устранение неполадок серверов MCP в приложениях контейнеров
- MCP TypeScript SDK