Как сделать MCP-сервер для AI-ассистента на Node.js

Если вы активно пользуетесь Claude Desktop или другими продвинутыми AI-ассистентами, вы наверняка заметили, что их главная проблема — «изоляция». Модель может отлично писать код, но она не знает, что происходит в вашей локальной базе данных, не видит структуру ваших файлов в реальном времени и не может дернуть API вашего внутреннего сервиса.
Протокол MCP (Model Context Protocol) от Anthropic призван решить эту проблему. По сути, это открытый стандарт, который позволяет создать «мост» между LLM и любыми данными или инструментами. Вместо того чтобы писать сложные интеграции под каждую модель, вы создаете один MCP-сервер, который предоставляет инструменты (tools) и ресурсы, а AI-клиент сам решает, когда и как их вызвать.
В этой статье мы разберем, как с нуля поднять MCP-сервер на Node.js, который будет выполнять реальные задачи.
Что такое MCP на пальцах?
Представьте, что LLM — это очень умный мозг, но у него нет рук. MCP-сервер — это и есть те самые «руки».
Архитектура выглядит так:
LLM (Клиент) $\leftrightarrow$ MCP Host (например, Claude Desktop) $\leftrightarrow$ Ваш MCP Server $\leftrightarrow$ Ваша БД/API/Файлы.
Сервер сообщает клиенту: «Я умею делать вот эти пять вещей (функции)». Когда пользователь просит что-то, что требует этих действий, клиент отправляет запрос серверу, тот выполняет код на Node.js и возвращает результат обратно в контекст модели.
Подготовка среды
Для разработки нам понадобится Node.js (рекомендую версию 18+ или 20 LTS) и менеджер пакетов npm или pnpm.
Шаг 1: Инициализация проекта
Создаем директорию и настраиваем проект. Мы будем использовать TypeScript, так как в MCP строгая типизация критически важна для того, чтобы модель правильно понимала аргументы функций.
bash
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node ts-node
npx tsc —init
В tsconfig.json убедитесь, что у вас установлены следующие параметры для корректной компиляции:
json
{
«compilerOptions»: {
«target»: «ES2022»,
«module»: «NodeNext»,
«moduleResolution»: «NodeNext»,
«outDir»: «./build»,
«esModuleInterop»: true,
«strict»: true
}
}
Разработка сервера: Пошаговый разбор
Суть любого MCP-сервера заключается в трех вещах: Resources (данные), Tools (функции) и Prompts (шаблоны). В этом примере мы сосредоточимся на Tools, так как это самый мощный функционал.
1. Создание базового экземпляра сервера
Создайте файл index.ts. Начнем с импорта SDK и инициализации сервера.
typescript
import { Server } from «@modelcontextprotocol/sdk/server/index.js»;
import { StdioServerTransport } from «@modelcontextprotocol/sdk/server/stdio.js»;
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from «@modelcontextprotocol/sdk/types.js»;
const server = new Server(
{
name: «my-custom-assistant»,
version: «1.0.0»,
},
{
capabilities: {
tools: {}, // Объявляем, что сервер поддерживает инструменты
},
}
);
2. Описание доступных инструментов
Чтобы AI понял, зачем нужен ваш инструмент, нужно максимально подробно описать его назначение и схему аргументов. Это фактически «документация для нейронки».
typescript
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: «get_system_metrics»,
description: «Возвращает текущую нагрузку на процессор и использование памяти сервера»,
inputSchema: {
type: «object»,
properties: {
detail: {
type: «string»,
description: «Уровень детализации: ‘brief’ или ‘full'»
},
},
},
},
{
name: «read_local_log»,
description: «Читает последние N строк из указанного лог-файла»,
inputSchema: {
type: «object»,
properties: {
filePath: { type: «string» },
lines: { type: «number», default: 10 },
},
required: [«filePath»],
},
},
],
};
});
3. Реализация бизнес-логики (Обработка вызовов)
Теперь нужно прописать, что именно происходит, когда модель вызывает инструмент. Мы используем CallToolRequestSchema.
typescript
import * as fs from «fs/promises»;
import os from «os»;
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case «get_system_metrics»: {
const freeMem = os.freemem();
const totalMem = os.totalmem();
const load = os.loadavg()[0];
return {
content: [{
type: "text",
text: `CPU Load: ${load}, RAM Free: ${Math.round(freeMem / 1024 / 1024)} MB / ${Math.round(totalMem / 1024 / 1024)} MB`
}],
};
}
case "read_local_log": {
const { filePath, lines } = args as { filePath: string, lines: number };
try {
const data = await fs.readFile(filePath, "utf8");
const lastLines = data.split("\n").slice(-lines).join("\n");
return {
content: [{ type: "text", text: lastLines }],
};
} catch (error: any) {
return {
content: [{ type: "text", text: `Ошибка чтения файла: ${error.message}` }],
isError: true,
};
}
}
default:
throw new Error("Tool not found");
}
});
4. Запуск транспорта
MCP может работать через HTTP или stdio (стандартный ввод/вывод). Для локальных ассистентов (вроде Claude Desktop) используется stdio, так как это проще и безопаснее.
typescript
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(«MCP Server running on stdio»);
}
main().catch((err) => {
console.error(«Fatal error in main():», err);
process.exit(1);
});
Важный нюанс: В MCP-серверах через stdio console.log используется для общения с клиентом. Поэтому все ваши отладочные сообщения должны идти в console.error, иначе протокол «сломается», так как клиент примет ваш лог за ответ сервера.
Сборка и интеграция с клиентом
Теперь скомпилируем проект в JS:
bash
npx tsc
Чтобы подключить этот сервер к Claude Desktop, нужно отредактировать конфигурационный файл claude_desktop_config.json.
-
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
- macOS:
-
- Windows:
%APPDATA%\Claude\claude_desktop_config.json
- Windows:
Добавьте туда ваш сервер:
json
{
«mcpServers»: {
«my-node-server»: {
«command»: «node»,
«args»: [«/абсолютный/путь/к/вашему/проекту/build/index.js»]
}
}
}
Перезапустите Claude Desktop, и вы увидите иконку «молотка», означающую, что инструменты доступны.
Оптимизация и безопасность
Когда вы выводите сервер в продакшн или используете его для работы с реальными данными, помните о следующих моментах:
- Валидация путей: В примере с
read_local_logмы просто читаем файл. В реальности это огромная дыра в безопасности (Path Traversal). Всегда проверяйте, что путь к файлу находится внутри разрешенной директории. - Таймауты: LLM может ждать ответа довольно долго, но если ваш инструмент «зависнет» (например, тяжелый SQL запрос), клиент может оборвать соединение. Используйте
Promise.raceдля установки жестких лимитов на выполнение. - Размер контекста: Не пытайтесь вернуть в ответе 10 МБ текста. Модели имеют лимит контекстного окна. Если данных много, возвращайте краткую выжимку или ссылку на файл, предложив модели запросить конкретные части.
Итоги
Создание MCP-сервера на Node.js позволяет превратить AI из простого чат-бота в полноценного оператора вашей инфраструктуры. Основной секрет успеха здесь — в качественном описании инструментов. Чем точнее вы опишете description и inputSchema, тем реже модель будет ошибаться в параметрах.
Теперь вы можете расширить этот сервер, добавив подключение к PostgreSQL, интеграцию с Jira API или даже управление умным домом через MQTT. Главное — помнить, что вы создаете интерфейс, который будет использовать не человек, а алгоритм, поэтому однозначность и предсказуемость ответов важнее, чем их красота.

